summaryrefslogtreecommitdiff
path: root/ELF
diff options
context:
space:
mode:
Diffstat (limited to 'ELF')
-rw-r--r--ELF/AArch64ErrataFix.cpp495
-rw-r--r--ELF/AArch64ErrataFix.h19
-rw-r--r--ELF/Arch/AArch64.cpp466
-rw-r--r--ELF/Arch/AMDGPU.cpp70
-rw-r--r--ELF/Arch/ARM.cpp462
-rw-r--r--ELF/Arch/AVR.cpp37
-rw-r--r--ELF/Arch/Hexagon.cpp195
-rw-r--r--ELF/Arch/MSP430.cpp49
-rw-r--r--ELF/Arch/Mips.cpp607
-rw-r--r--ELF/Arch/MipsArchTree.cpp239
-rw-r--r--ELF/Arch/PPC.cpp407
-rw-r--r--ELF/Arch/PPC64.cpp730
-rw-r--r--ELF/Arch/RISCV.cpp401
-rw-r--r--ELF/Arch/SPARCV9.cpp102
-rw-r--r--ELF/Arch/X86.cpp442
-rw-r--r--ELF/Arch/X86_64.cpp651
-rw-r--r--ELF/Bits.h35
-rw-r--r--ELF/CMakeLists.txt1
-rw-r--r--ELF/CallGraphSort.cpp185
-rw-r--r--ELF/CallGraphSort.h7
-rw-r--r--ELF/Config.h356
-rw-r--r--ELF/DWARF.cpp132
-rw-r--r--ELF/DWARF.h65
-rw-r--r--ELF/Driver.cpp1899
-rw-r--r--ELF/Driver.h42
-rw-r--r--ELF/DriverUtils.cpp195
-rw-r--r--ELF/EhFrame.cpp137
-rw-r--r--ELF/EhFrame.h11
-rw-r--r--ELF/Filesystem.cpp86
-rw-r--r--ELF/Filesystem.h23
-rw-r--r--ELF/ICF.cpp337
-rw-r--r--ELF/ICF.h7
-rw-r--r--ELF/InputFiles.cpp1706
-rw-r--r--ELF/InputFiles.h358
-rw-r--r--ELF/InputSection.cpp1113
-rw-r--r--ELF/InputSection.h278
-rw-r--r--ELF/LTO.cpp327
-rw-r--r--ELF/LTO.h21
-rw-r--r--ELF/LinkerScript.cpp1018
-rw-r--r--ELF/LinkerScript.h222
-rw-r--r--ELF/MapFile.cpp234
-rw-r--r--ELF/MapFile.h7
-rw-r--r--ELF/MarkLive.cpp385
-rw-r--r--ELF/MarkLive.h7
-rw-r--r--ELF/Options.td60
-rw-r--r--ELF/OutputSections.cpp442
-rw-r--r--ELF/OutputSections.h122
-rw-r--r--ELF/Relocations.cpp1663
-rw-r--r--ELF/Relocations.h178
-rw-r--r--ELF/ScriptLexer.cpp224
-rw-r--r--ELF/ScriptLexer.h29
-rw-r--r--ELF/ScriptParser.cpp1313
-rw-r--r--ELF/ScriptParser.h17
-rw-r--r--ELF/SymbolTable.cpp835
-rw-r--r--ELF/SymbolTable.h98
-rw-r--r--ELF/Symbols.cpp627
-rw-r--r--ELF/Symbols.h461
-rw-r--r--ELF/SyntheticSections.cpp3698
-rw-r--r--ELF/SyntheticSections.h893
-rw-r--r--ELF/Target.cpp101
-rw-r--r--ELF/Target.h233
-rw-r--r--ELF/Thunks.cpp809
-rw-r--r--ELF/Thunks.h42
-rw-r--r--ELF/Writer.cpp2668
-rw-r--r--ELF/Writer.h29
65 files changed, 15824 insertions, 13284 deletions
diff --git a/ELF/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp
index ac753cb582653..b2eda4dcbc4e9 100644
--- a/ELF/AArch64ErrataFix.cpp
+++ b/ELF/AArch64ErrataFix.cpp
@@ -1,9 +1,8 @@
//===- AArch64ErrataFix.cpp -----------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file implements Section Patching for the purpose of working around
@@ -57,8 +56,8 @@ using namespace lld::elf;
// ADRP
// | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) |
-static bool isADRP(uint32_t Instr) {
- return (Instr & 0x9f000000) == 0x90000000;
+static bool isADRP(uint32_t instr) {
+ return (instr & 0x9f000000) == 0x90000000;
}
// Load and store bit patterns from ARMv8-A ARM ARM.
@@ -67,8 +66,8 @@ static bool isADRP(uint32_t Instr) {
// All loads and stores have 1 (at bit postion 27), (0 at bit position 25).
// | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) |
-static bool isLoadStoreClass(uint32_t Instr) {
- return (Instr & 0x0a000000) == 0x08000000;
+static bool isLoadStoreClass(uint32_t instr) {
+ return (instr & 0x0a000000) == 0x08000000;
}
// LDN/STN multiple no offset
@@ -83,20 +82,20 @@ static bool isLoadStoreClass(uint32_t Instr) {
// opcode == 0110 ST1 3 registers.
// opcode == 0111 ST1 1 register.
// opcode == 1010 ST1 2 registers.
-static bool isST1MultipleOpcode(uint32_t Instr) {
- return (Instr & 0x0000f000) == 0x00002000 ||
- (Instr & 0x0000f000) == 0x00006000 ||
- (Instr & 0x0000f000) == 0x00007000 ||
- (Instr & 0x0000f000) == 0x0000a000;
+static bool isST1MultipleOpcode(uint32_t instr) {
+ return (instr & 0x0000f000) == 0x00002000 ||
+ (instr & 0x0000f000) == 0x00006000 ||
+ (instr & 0x0000f000) == 0x00007000 ||
+ (instr & 0x0000f000) == 0x0000a000;
}
-static bool isST1Multiple(uint32_t Instr) {
- return (Instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(Instr);
+static bool isST1Multiple(uint32_t instr) {
+ return (instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(instr);
}
// Writes to Rn (writeback).
-static bool isST1MultiplePost(uint32_t Instr) {
- return (Instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(Instr);
+static bool isST1MultiplePost(uint32_t instr) {
+ return (instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(instr);
}
// LDN/STN single no offset
@@ -111,41 +110,41 @@ static bool isST1MultiplePost(uint32_t Instr) {
// opcode == 000 ST1 8-bit.
// opcode == 010 ST1 16-bit.
// opcode == 100 ST1 32 or 64-bit (Size determines which).
-static bool isST1SingleOpcode(uint32_t Instr) {
- return (Instr & 0x0040e000) == 0x00000000 ||
- (Instr & 0x0040e000) == 0x00004000 ||
- (Instr & 0x0040e000) == 0x00008000;
+static bool isST1SingleOpcode(uint32_t instr) {
+ return (instr & 0x0040e000) == 0x00000000 ||
+ (instr & 0x0040e000) == 0x00004000 ||
+ (instr & 0x0040e000) == 0x00008000;
}
-static bool isST1Single(uint32_t Instr) {
- return (Instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(Instr);
+static bool isST1Single(uint32_t instr) {
+ return (instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(instr);
}
// Writes to Rn (writeback).
-static bool isST1SinglePost(uint32_t Instr) {
- return (Instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(Instr);
+static bool isST1SinglePost(uint32_t instr) {
+ return (instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(instr);
}
-static bool isST1(uint32_t Instr) {
- return isST1Multiple(Instr) || isST1MultiplePost(Instr) ||
- isST1Single(Instr) || isST1SinglePost(Instr);
+static bool isST1(uint32_t instr) {
+ return isST1Multiple(instr) || isST1MultiplePost(instr) ||
+ isST1Single(instr) || isST1SinglePost(instr);
}
// Load/store exclusive
// | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) |
// L == 0 for Stores.
-static bool isLoadStoreExclusive(uint32_t Instr) {
- return (Instr & 0x3f000000) == 0x08000000;
+static bool isLoadStoreExclusive(uint32_t instr) {
+ return (instr & 0x3f000000) == 0x08000000;
}
-static bool isLoadExclusive(uint32_t Instr) {
- return (Instr & 0x3f400000) == 0x08400000;
+static bool isLoadExclusive(uint32_t instr) {
+ return (instr & 0x3f400000) == 0x08400000;
}
// Load register literal
// | opc (2) 01 | 1 V 00 | imm19 | Rt (5) |
-static bool isLoadLiteral(uint32_t Instr) {
- return (Instr & 0x3b000000) == 0x18000000;
+static bool isLoadLiteral(uint32_t instr) {
+ return (instr & 0x3b000000) == 0x18000000;
}
// Load/store no-allocate pair
@@ -153,8 +152,8 @@ static bool isLoadLiteral(uint32_t Instr) {
// | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
// L == 0 for stores.
// Never writes to register
-static bool isSTNP(uint32_t Instr) {
- return (Instr & 0x3bc00000) == 0x28000000;
+static bool isSTNP(uint32_t instr) {
+ return (instr & 0x3bc00000) == 0x28000000;
}
// Load/store register pair
@@ -162,69 +161,69 @@ static bool isSTNP(uint32_t Instr) {
// | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
// L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP
// Writes to Rn.
-static bool isSTPPost(uint32_t Instr) {
- return (Instr & 0x3bc00000) == 0x28800000;
+static bool isSTPPost(uint32_t instr) {
+ return (instr & 0x3bc00000) == 0x28800000;
}
// (offset)
// | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
-static bool isSTPOffset(uint32_t Instr) {
- return (Instr & 0x3bc00000) == 0x29000000;
+static bool isSTPOffset(uint32_t instr) {
+ return (instr & 0x3bc00000) == 0x29000000;
}
// (pre-index)
// | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
// Writes to Rn.
-static bool isSTPPre(uint32_t Instr) {
- return (Instr & 0x3bc00000) == 0x29800000;
+static bool isSTPPre(uint32_t instr) {
+ return (instr & 0x3bc00000) == 0x29800000;
}
-static bool isSTP(uint32_t Instr) {
- return isSTPPost(Instr) || isSTPOffset(Instr) || isSTPPre(Instr);
+static bool isSTP(uint32_t instr) {
+ return isSTPPost(instr) || isSTPOffset(instr) || isSTPPre(instr);
}
// Load/store register (unscaled immediate)
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) |
// V == 0 for Scalar, V == 1 for Simd/FP.
-static bool isLoadStoreUnscaled(uint32_t Instr) {
- return (Instr & 0x3b000c00) == 0x38000000;
+static bool isLoadStoreUnscaled(uint32_t instr) {
+ return (instr & 0x3b000c00) == 0x38000000;
}
// Load/store register (immediate post-indexed)
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) |
-static bool isLoadStoreImmediatePost(uint32_t Instr) {
- return (Instr & 0x3b200c00) == 0x38000400;
+static bool isLoadStoreImmediatePost(uint32_t instr) {
+ return (instr & 0x3b200c00) == 0x38000400;
}
// Load/store register (unprivileged)
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) |
-static bool isLoadStoreUnpriv(uint32_t Instr) {
- return (Instr & 0x3b200c00) == 0x38000800;
+static bool isLoadStoreUnpriv(uint32_t instr) {
+ return (instr & 0x3b200c00) == 0x38000800;
}
// Load/store register (immediate pre-indexed)
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) |
-static bool isLoadStoreImmediatePre(uint32_t Instr) {
- return (Instr & 0x3b200c00) == 0x38000c00;
+static bool isLoadStoreImmediatePre(uint32_t instr) {
+ return (instr & 0x3b200c00) == 0x38000c00;
}
// Load/store register (register offset)
// | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt |
-static bool isLoadStoreRegisterOff(uint32_t Instr) {
- return (Instr & 0x3b200c00) == 0x38200800;
+static bool isLoadStoreRegisterOff(uint32_t instr) {
+ return (instr & 0x3b200c00) == 0x38200800;
}
// Load/store register (unsigned immediate)
// | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) |
-static bool isLoadStoreRegisterUnsigned(uint32_t Instr) {
- return (Instr & 0x3b000000) == 0x39000000;
+static bool isLoadStoreRegisterUnsigned(uint32_t instr) {
+ return (instr & 0x3b000000) == 0x39000000;
}
// Rt is always in bit position 0 - 4.
-static uint32_t getRt(uint32_t Instr) { return (Instr & 0x1f); }
+static uint32_t getRt(uint32_t instr) { return (instr & 0x1f); }
// Rn is always in bit position 5 - 9.
-static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; }
+static uint32_t getRn(uint32_t instr) { return (instr >> 5) & 0x1f; }
// C4.1.2 Branches, Exception Generating and System instructions
// | op0 (3) 1 | 01 op1 (4) | x (22) |
@@ -233,41 +232,41 @@ static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; }
// op0 == x00 101 op1 == xxxx Unconditional Branch immediate.
// op0 == x01 101 op1 == 0xxx Compare and branch immediate.
// op0 == x01 101 op1 == 1xxx Test and branch immediate.
-static bool isBranch(uint32_t Instr) {
- return ((Instr & 0xfe000000) == 0xd6000000) || // Cond branch.
- ((Instr & 0xfe000000) == 0x54000000) || // Uncond branch reg.
- ((Instr & 0x7c000000) == 0x14000000) || // Uncond branch imm.
- ((Instr & 0x7c000000) == 0x34000000); // Compare and test branch.
+static bool isBranch(uint32_t instr) {
+ return ((instr & 0xfe000000) == 0xd6000000) || // Cond branch.
+ ((instr & 0xfe000000) == 0x54000000) || // Uncond branch reg.
+ ((instr & 0x7c000000) == 0x14000000) || // Uncond branch imm.
+ ((instr & 0x7c000000) == 0x34000000); // Compare and test branch.
}
-static bool isV8SingleRegisterNonStructureLoadStore(uint32_t Instr) {
- return isLoadStoreUnscaled(Instr) || isLoadStoreImmediatePost(Instr) ||
- isLoadStoreUnpriv(Instr) || isLoadStoreImmediatePre(Instr) ||
- isLoadStoreRegisterOff(Instr) || isLoadStoreRegisterUnsigned(Instr);
+static bool isV8SingleRegisterNonStructureLoadStore(uint32_t instr) {
+ return isLoadStoreUnscaled(instr) || isLoadStoreImmediatePost(instr) ||
+ isLoadStoreUnpriv(instr) || isLoadStoreImmediatePre(instr) ||
+ isLoadStoreRegisterOff(instr) || isLoadStoreRegisterUnsigned(instr);
}
// Note that this function refers to v8.0 only and does not include the
// additional load and store instructions added for in later revisions of
// the architecture such as the Atomic memory operations introduced
// in v8.1.
-static bool isV8NonStructureLoad(uint32_t Instr) {
- if (isLoadExclusive(Instr))
+static bool isV8NonStructureLoad(uint32_t instr) {
+ if (isLoadExclusive(instr))
return true;
- if (isLoadLiteral(Instr))
+ if (isLoadLiteral(instr))
return true;
- else if (isV8SingleRegisterNonStructureLoadStore(Instr)) {
+ else if (isV8SingleRegisterNonStructureLoadStore(instr)) {
// For Load and Store single register, Loads are derived from a
// combination of the Size, V and Opc fields.
- uint32_t Size = (Instr >> 30) & 0xff;
- uint32_t V = (Instr >> 26) & 0x1;
- uint32_t Opc = (Instr >> 22) & 0x3;
+ uint32_t size = (instr >> 30) & 0xff;
+ uint32_t v = (instr >> 26) & 0x1;
+ uint32_t opc = (instr >> 22) & 0x3;
// For the load and store instructions that we are decoding.
// Opc == 0 are all stores.
// Opc == 1 with a couple of exceptions are loads. The exceptions are:
// Size == 00 (0), V == 1, Opc == 10 (2) which is a store and
// Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch.
- return Opc != 0 && !(Size == 0 && V == 1 && Opc == 2) &&
- !(Size == 3 && V == 0 && Opc == 2);
+ return opc != 0 && !(size == 0 && v == 1 && opc == 2) &&
+ !(size == 3 && v == 0 && opc == 2);
}
return false;
}
@@ -276,18 +275,18 @@ static bool isV8NonStructureLoad(uint32_t Instr) {
// needed for errata 843419.
// Instruction with writeback updates the index register after the load/store.
-static bool hasWriteback(uint32_t Instr) {
- return isLoadStoreImmediatePre(Instr) || isLoadStoreImmediatePost(Instr) ||
- isSTPPre(Instr) || isSTPPost(Instr) || isST1SinglePost(Instr) ||
- isST1MultiplePost(Instr);
+static bool hasWriteback(uint32_t instr) {
+ return isLoadStoreImmediatePre(instr) || isLoadStoreImmediatePost(instr) ||
+ isSTPPre(instr) || isSTPPost(instr) || isST1SinglePost(instr) ||
+ isST1MultiplePost(instr);
}
// For the load and store class of instructions, a load can write to the
// destination register, a load and a store can write to the base register when
// the instruction has writeback.
-static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) {
- return (isV8NonStructureLoad(Instr) && getRt(Instr) == Reg) ||
- (hasWriteback(Instr) && getRn(Instr) == Reg);
+static bool doesLoadStoreWriteToReg(uint32_t instr, uint32_t reg) {
+ return (isV8NonStructureLoad(instr) && getRt(instr) == reg) ||
+ (hasWriteback(instr) && getRn(instr) == reg);
}
// Scanner for Cortex-A53 errata 843419
@@ -319,18 +318,18 @@ static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) {
// Return true if the Instruction sequence Adrp, Instr2, and Instr4 match
// the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.),
// and 4.) in the Scanner for Cortex-A53 errata comment above.
-static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2,
- uint32_t Instr4) {
- if (!isADRP(Instr1))
+static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2,
+ uint32_t instr4) {
+ if (!isADRP(instr1))
return false;
- uint32_t Rn = getRt(Instr1);
- return isLoadStoreClass(Instr2) &&
- (isLoadStoreExclusive(Instr2) || isLoadLiteral(Instr2) ||
- isV8SingleRegisterNonStructureLoadStore(Instr2) || isSTP(Instr2) ||
- isSTNP(Instr2) || isST1(Instr2)) &&
- !doesLoadStoreWriteToReg(Instr2, Rn) &&
- isLoadStoreRegisterUnsigned(Instr4) && getRn(Instr4) == Rn;
+ uint32_t rn = getRt(instr1);
+ return isLoadStoreClass(instr2) &&
+ (isLoadStoreExclusive(instr2) || isLoadLiteral(instr2) ||
+ isV8SingleRegisterNonStructureLoadStore(instr2) || isSTP(instr2) ||
+ isSTNP(instr2) || isST1(instr2)) &&
+ !doesLoadStoreWriteToReg(instr2, rn) &&
+ isLoadStoreRegisterUnsigned(instr4) && getRn(instr4) == rn;
}
// Scan the instruction sequence starting at Offset Off from the base of
@@ -339,143 +338,143 @@ static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2,
// instructions we've scanned.
// Return the offset of the load or store instruction in IS that we want to
// patch or 0 if no patch required.
-static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
- uint64_t Limit) {
- uint64_t ISAddr = IS->getVA(0);
+static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
+ uint64_t limit) {
+ uint64_t isecAddr = isec->getVA(0);
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
- uint64_t InitialPageOff = (ISAddr + Off) & 0xfff;
- if (InitialPageOff < 0xff8)
- Off += 0xff8 - InitialPageOff;
+ uint64_t initialPageOff = (isecAddr + off) & 0xfff;
+ if (initialPageOff < 0xff8)
+ off += 0xff8 - initialPageOff;
- bool OptionalAllowed = Limit - Off > 12;
- if (Off >= Limit || Limit - Off < 12) {
+ bool optionalAllowed = limit - off > 12;
+ if (off >= limit || limit - off < 12) {
// Need at least 3 4-byte sized instructions to trigger erratum.
- Off = Limit;
+ off = limit;
return 0;
}
- uint64_t PatchOff = 0;
- const uint8_t *Buf = IS->data().begin();
- const ulittle32_t *InstBuf = reinterpret_cast<const ulittle32_t *>(Buf + Off);
- uint32_t Instr1 = *InstBuf++;
- uint32_t Instr2 = *InstBuf++;
- uint32_t Instr3 = *InstBuf++;
- if (is843419ErratumSequence(Instr1, Instr2, Instr3)) {
- PatchOff = Off + 8;
- } else if (OptionalAllowed && !isBranch(Instr3)) {
- uint32_t Instr4 = *InstBuf++;
- if (is843419ErratumSequence(Instr1, Instr2, Instr4))
- PatchOff = Off + 12;
+ uint64_t patchOff = 0;
+ const uint8_t *buf = isec->data().begin();
+ const ulittle32_t *instBuf = reinterpret_cast<const ulittle32_t *>(buf + off);
+ uint32_t instr1 = *instBuf++;
+ uint32_t instr2 = *instBuf++;
+ uint32_t instr3 = *instBuf++;
+ if (is843419ErratumSequence(instr1, instr2, instr3)) {
+ patchOff = off + 8;
+ } else if (optionalAllowed && !isBranch(instr3)) {
+ uint32_t instr4 = *instBuf++;
+ if (is843419ErratumSequence(instr1, instr2, instr4))
+ patchOff = off + 12;
}
- if (((ISAddr + Off) & 0xfff) == 0xff8)
- Off += 4;
+ if (((isecAddr + off) & 0xfff) == 0xff8)
+ off += 4;
else
- Off += 0xffc;
- return PatchOff;
+ off += 0xffc;
+ return patchOff;
}
class lld::elf::Patch843419Section : public SyntheticSection {
public:
- Patch843419Section(InputSection *P, uint64_t Off);
+ Patch843419Section(InputSection *p, uint64_t off);
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override { return 8; }
uint64_t getLDSTAddr() const;
// The Section we are patching.
- const InputSection *Patchee;
+ const InputSection *patchee;
// The offset of the instruction in the Patchee section we are patching.
- uint64_t PatcheeOffset;
+ uint64_t patcheeOffset;
// A label for the start of the Patch that we can use as a relocation target.
- Symbol *PatchSym;
+ Symbol *patchSym;
};
-lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off)
+lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
".text.patch"),
- Patchee(P), PatcheeOffset(Off) {
- this->Parent = P->getParent();
- PatchSym = addSyntheticLocal(
- Saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0,
+ patchee(p), patcheeOffset(off) {
+ this->parent = p->getParent();
+ patchSym = addSyntheticLocal(
+ saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0,
getSize(), *this);
- addSyntheticLocal(Saver.save("$x"), STT_NOTYPE, 0, 0, *this);
+ addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this);
}
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
- return Patchee->getVA(PatcheeOffset);
+ return patchee->getVA(patcheeOffset);
}
-void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) {
+void lld::elf::Patch843419Section::writeTo(uint8_t *buf) {
// Copy the instruction that we will be replacing with a branch in the
// Patchee Section.
- write32le(Buf, read32le(Patchee->data().begin() + PatcheeOffset));
+ write32le(buf, read32le(patchee->data().begin() + patcheeOffset));
// Apply any relocation transferred from the original PatcheeSection.
- // For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc
- // also adds OutSecOff so we need to subtract to avoid double counting.
- this->relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + getSize());
+ // For a SyntheticSection Buf already has outSecOff added, but relocateAlloc
+ // also adds outSecOff so we need to subtract to avoid double counting.
+ this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
// Return address is the next instruction after the one we have just copied.
- uint64_t S = getLDSTAddr() + 4;
- uint64_t P = PatchSym->getVA() + 4;
- Target->relocateOne(Buf + 4, R_AARCH64_JUMP26, S - P);
+ uint64_t s = getLDSTAddr() + 4;
+ uint64_t p = patchSym->getVA() + 4;
+ target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p);
}
void AArch64Err843419Patcher::init() {
// The AArch64 ABI permits data in executable sections. We must avoid scanning
// this data as if it were instructions to avoid false matches. We use the
// mapping symbols in the InputObjects to identify this data, caching the
- // results in SectionMap so we don't have to recalculate it each pass.
+ // results in sectionMap so we don't have to recalculate it each pass.
// The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe
// half open intervals [Symbol Value, Next Symbol Value) of code and data
// within sections. If there is no next symbol then the half open interval is
// [Symbol Value, End of section). The type, code or data, is determined by
// the mapping symbol name, $x for code, $d for data.
- auto IsCodeMapSymbol = [](const Symbol *B) {
- return B->getName() == "$x" || B->getName().startswith("$x.");
+ auto isCodeMapSymbol = [](const Symbol *b) {
+ return b->getName() == "$x" || b->getName().startswith("$x.");
};
- auto IsDataMapSymbol = [](const Symbol *B) {
- return B->getName() == "$d" || B->getName().startswith("$d.");
+ auto isDataMapSymbol = [](const Symbol *b) {
+ return b->getName() == "$d" || b->getName().startswith("$d.");
};
// Collect mapping symbols for every executable InputSection.
- for (InputFile *File : ObjectFiles) {
- auto *F = cast<ObjFile<ELF64LE>>(File);
- for (Symbol *B : F->getLocalSymbols()) {
- auto *Def = dyn_cast<Defined>(B);
- if (!Def)
+ for (InputFile *file : objectFiles) {
+ auto *f = cast<ObjFile<ELF64LE>>(file);
+ for (Symbol *b : f->getLocalSymbols()) {
+ auto *def = dyn_cast<Defined>(b);
+ if (!def)
continue;
- if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def))
+ if (!isCodeMapSymbol(def) && !isDataMapSymbol(def))
continue;
- if (auto *Sec = dyn_cast_or_null<InputSection>(Def->Section))
- if (Sec->Flags & SHF_EXECINSTR)
- SectionMap[Sec].push_back(Def);
+ if (auto *sec = dyn_cast_or_null<InputSection>(def->section))
+ if (sec->flags & SHF_EXECINSTR)
+ sectionMap[sec].push_back(def);
}
}
// For each InputSection make sure the mapping symbols are in sorted in
// ascending order and free from consecutive runs of mapping symbols with
// the same type. For example we must remove the redundant $d.1 from $x.0
// $d.0 $d.1 $x.1.
- for (auto &KV : SectionMap) {
- std::vector<const Defined *> &MapSyms = KV.second;
- if (MapSyms.size() <= 1)
+ for (auto &kv : sectionMap) {
+ std::vector<const Defined *> &mapSyms = kv.second;
+ if (mapSyms.size() <= 1)
continue;
- std::stable_sort(
- MapSyms.begin(), MapSyms.end(),
- [](const Defined *A, const Defined *B) { return A->Value < B->Value; });
- MapSyms.erase(
- std::unique(MapSyms.begin(), MapSyms.end(),
- [=](const Defined *A, const Defined *B) {
- return (IsCodeMapSymbol(A) && IsCodeMapSymbol(B)) ||
- (IsDataMapSymbol(A) && IsDataMapSymbol(B));
+ llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) {
+ return a->value < b->value;
+ });
+ mapSyms.erase(
+ std::unique(mapSyms.begin(), mapSyms.end(),
+ [=](const Defined *a, const Defined *b) {
+ return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) ||
+ (isDataMapSymbol(a) && isDataMapSymbol(b));
}),
- MapSyms.end());
+ mapSyms.end());
}
- Initialized = true;
+ initialized = true;
}
// Insert the PatchSections we have created back into the
@@ -484,60 +483,60 @@ void AArch64Err843419Patcher::init() {
// executable sections, although we may need to insert them earlier if the
// InputSectionDescription is larger than the maximum branch range.
void AArch64Err843419Patcher::insertPatches(
- InputSectionDescription &ISD, std::vector<Patch843419Section *> &Patches) {
- uint64_t ISLimit;
- uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff;
- uint64_t PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing();
- uint64_t OutSecAddr = ISD.Sections.front()->getParent()->Addr;
+ InputSectionDescription &isd, std::vector<Patch843419Section *> &patches) {
+ uint64_t isecLimit;
+ uint64_t prevIsecLimit = isd.sections.front()->outSecOff;
+ uint64_t patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing();
+ uint64_t outSecAddr = isd.sections.front()->getParent()->addr;
- // Set the OutSecOff of patches to the place where we want to insert them.
+ // Set the outSecOff of patches to the place where we want to insert them.
// We use a similar strategy to Thunk placement. Place patches roughly
// every multiple of maximum branch range.
- auto PatchIt = Patches.begin();
- auto PatchEnd = Patches.end();
- for (const InputSection *IS : ISD.Sections) {
- ISLimit = IS->OutSecOff + IS->getSize();
- if (ISLimit > PatchUpperBound) {
- while (PatchIt != PatchEnd) {
- if ((*PatchIt)->getLDSTAddr() - OutSecAddr >= PrevISLimit)
+ auto patchIt = patches.begin();
+ auto patchEnd = patches.end();
+ for (const InputSection *isec : isd.sections) {
+ isecLimit = isec->outSecOff + isec->getSize();
+ if (isecLimit > patchUpperBound) {
+ while (patchIt != patchEnd) {
+ if ((*patchIt)->getLDSTAddr() - outSecAddr >= prevIsecLimit)
break;
- (*PatchIt)->OutSecOff = PrevISLimit;
- ++PatchIt;
+ (*patchIt)->outSecOff = prevIsecLimit;
+ ++patchIt;
}
- PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing();
+ patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing();
}
- PrevISLimit = ISLimit;
+ prevIsecLimit = isecLimit;
}
- for (; PatchIt != PatchEnd; ++PatchIt) {
- (*PatchIt)->OutSecOff = ISLimit;
+ for (; patchIt != patchEnd; ++patchIt) {
+ (*patchIt)->outSecOff = isecLimit;
}
- // merge all patch sections. We use the OutSecOff assigned above to
+ // merge all patch sections. We use the outSecOff assigned above to
// determine the insertion point. This is ok as we only merge into an
// InputSectionDescription once per pass, and at the end of the pass
- // assignAddresses() will recalculate all the OutSecOff values.
- std::vector<InputSection *> Tmp;
- Tmp.reserve(ISD.Sections.size() + Patches.size());
- auto MergeCmp = [](const InputSection *A, const InputSection *B) {
- if (A->OutSecOff < B->OutSecOff)
+ // assignAddresses() will recalculate all the outSecOff values.
+ std::vector<InputSection *> tmp;
+ tmp.reserve(isd.sections.size() + patches.size());
+ auto mergeCmp = [](const InputSection *a, const InputSection *b) {
+ if (a->outSecOff < b->outSecOff)
return true;
- if (A->OutSecOff == B->OutSecOff && isa<Patch843419Section>(A) &&
- !isa<Patch843419Section>(B))
+ if (a->outSecOff == b->outSecOff && isa<Patch843419Section>(a) &&
+ !isa<Patch843419Section>(b))
return true;
return false;
};
- std::merge(ISD.Sections.begin(), ISD.Sections.end(), Patches.begin(),
- Patches.end(), std::back_inserter(Tmp), MergeCmp);
- ISD.Sections = std::move(Tmp);
+ std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
+ patches.end(), std::back_inserter(tmp), mergeCmp);
+ isd.sections = std::move(tmp);
}
-// Given an erratum sequence that starts at address AdrpAddr, with an
-// instruction that we need to patch at PatcheeOffset from the start of
+// Given an erratum sequence that starts at address adrpAddr, with an
+// instruction that we need to patch at patcheeOffset from the start of
// InputSection IS, create a Patch843419 Section and add it to the
// Patches that we need to insert.
-static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
- InputSection *IS,
- std::vector<Patch843419Section *> &Patches) {
+static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
+ InputSection *isec,
+ std::vector<Patch843419Section *> &patches) {
// There may be a relocation at the same offset that we are patching. There
// are four cases that we need to consider.
// Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this
@@ -552,29 +551,29 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
// and replace the relocation with a R_AARCH_JUMP26 branch relocation.
// Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch
// relocation at the offset.
- auto RelIt = std::find_if(
- IS->Relocations.begin(), IS->Relocations.end(),
- [=](const Relocation &R) { return R.Offset == PatcheeOffset; });
- if (RelIt != IS->Relocations.end() &&
- (RelIt->Type == R_AARCH64_JUMP26 || RelIt->Expr == R_RELAX_TLS_IE_TO_LE))
+ auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) {
+ return r.offset == patcheeOffset;
+ });
+ if (relIt != isec->relocations.end() &&
+ (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE))
return;
log("detected cortex-a53-843419 erratum sequence starting at " +
- utohexstr(AdrpAddr) + " in unpatched output.");
+ utohexstr(adrpAddr) + " in unpatched output.");
- auto *PS = make<Patch843419Section>(IS, PatcheeOffset);
- Patches.push_back(PS);
+ auto *ps = make<Patch843419Section>(isec, patcheeOffset);
+ patches.push_back(ps);
- auto MakeRelToPatch = [](uint64_t Offset, Symbol *PatchSym) {
- return Relocation{R_PC, R_AARCH64_JUMP26, Offset, 0, PatchSym};
+ auto makeRelToPatch = [](uint64_t offset, Symbol *patchSym) {
+ return Relocation{R_PC, R_AARCH64_JUMP26, offset, 0, patchSym};
};
- if (RelIt != IS->Relocations.end()) {
- PS->Relocations.push_back(
- {RelIt->Expr, RelIt->Type, 0, RelIt->Addend, RelIt->Sym});
- *RelIt = MakeRelToPatch(PatcheeOffset, PS->PatchSym);
+ if (relIt != isec->relocations.end()) {
+ ps->relocations.push_back(
+ {relIt->expr, relIt->type, 0, relIt->addend, relIt->sym});
+ *relIt = makeRelToPatch(patcheeOffset, ps->patchSym);
} else
- IS->Relocations.push_back(MakeRelToPatch(PatcheeOffset, PS->PatchSym));
+ isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym));
}
// Scan all the instructions in InputSectionDescription, for each instance of
@@ -582,40 +581,40 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
// Patch843419Sections that need to be applied to ISD.
std::vector<Patch843419Section *>
AArch64Err843419Patcher::patchInputSectionDescription(
- InputSectionDescription &ISD) {
- std::vector<Patch843419Section *> Patches;
- for (InputSection *IS : ISD.Sections) {
+ InputSectionDescription &isd) {
+ std::vector<Patch843419Section *> patches;
+ for (InputSection *isec : isd.sections) {
// LLD doesn't use the erratum sequence in SyntheticSections.
- if (isa<SyntheticSection>(IS))
+ if (isa<SyntheticSection>(isec))
continue;
- // Use SectionMap to make sure we only scan code and not inline data.
+ // Use sectionMap to make sure we only scan code and not inline data.
// We have already sorted MapSyms in ascending order and removed consecutive
// mapping symbols of the same type. Our range of executable instructions to
- // scan is therefore [CodeSym->Value, DataSym->Value) or [CodeSym->Value,
+ // scan is therefore [codeSym->value, dataSym->value) or [codeSym->value,
// section size).
- std::vector<const Defined *> &MapSyms = SectionMap[IS];
+ std::vector<const Defined *> &mapSyms = sectionMap[isec];
- auto CodeSym = llvm::find_if(MapSyms, [&](const Defined *MS) {
- return MS->getName().startswith("$x");
+ auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) {
+ return ms->getName().startswith("$x");
});
- while (CodeSym != MapSyms.end()) {
- auto DataSym = std::next(CodeSym);
- uint64_t Off = (*CodeSym)->Value;
- uint64_t Limit =
- (DataSym == MapSyms.end()) ? IS->data().size() : (*DataSym)->Value;
+ while (codeSym != mapSyms.end()) {
+ auto dataSym = std::next(codeSym);
+ uint64_t off = (*codeSym)->value;
+ uint64_t limit =
+ (dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value;
- while (Off < Limit) {
- uint64_t StartAddr = IS->getVA(Off);
- if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit))
- implementPatch(StartAddr, PatcheeOffset, IS, Patches);
+ while (off < limit) {
+ uint64_t startAddr = isec->getVA(off);
+ if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit))
+ implementPatch(startAddr, patcheeOffset, isec, patches);
}
- if (DataSym == MapSyms.end())
+ if (dataSym == mapSyms.end())
break;
- CodeSym = std::next(DataSym);
+ codeSym = std::next(dataSym);
}
}
- return Patches;
+ return patches;
}
// For each InputSectionDescription make one pass over the executable sections
@@ -631,22 +630,22 @@ AArch64Err843419Patcher::patchInputSectionDescription(
// Ouptut and Input Sections may have been changed.
// Returns false if no patches were required and no changes were made.
bool AArch64Err843419Patcher::createFixes() {
- if (Initialized == false)
+ if (initialized == false)
init();
- bool AddressesChanged = false;
- for (OutputSection *OS : OutputSections) {
- if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
+ bool addressesChanged = false;
+ for (OutputSection *os : outputSections) {
+ if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR))
continue;
- for (BaseCommand *BC : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
- std::vector<Patch843419Section *> Patches =
- patchInputSectionDescription(*ISD);
- if (!Patches.empty()) {
- insertPatches(*ISD, Patches);
- AddressesChanged = true;
+ for (BaseCommand *bc : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(bc)) {
+ std::vector<Patch843419Section *> patches =
+ patchInputSectionDescription(*isd);
+ if (!patches.empty()) {
+ insertPatches(*isd, patches);
+ addressesChanged = true;
}
}
}
- return AddressesChanged;
+ return addressesChanged;
}
diff --git a/ELF/AArch64ErrataFix.h b/ELF/AArch64ErrataFix.h
index edd154d4cab33..0548b58751ff9 100644
--- a/ELF/AArch64ErrataFix.h
+++ b/ELF/AArch64ErrataFix.h
@@ -1,9 +1,8 @@
//===- AArch64ErrataFix.h ---------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -30,19 +29,19 @@ public:
private:
std::vector<Patch843419Section *>
- patchInputSectionDescription(InputSectionDescription &ISD);
+ patchInputSectionDescription(InputSectionDescription &isd);
- void insertPatches(InputSectionDescription &ISD,
- std::vector<Patch843419Section *> &Patches);
+ void insertPatches(InputSectionDescription &isd,
+ std::vector<Patch843419Section *> &patches);
void init();
- // A cache of the mapping symbols defined by the InputSecion sorted in order
+ // A cache of the mapping symbols defined by the InputSection sorted in order
// of ascending value with redundant symbols removed. These describe
// the ranges of code and data in an executable InputSection.
- std::map<InputSection *, std::vector<const Defined *>> SectionMap;
+ std::map<InputSection *, std::vector<const Defined *>> sectionMap;
- bool Initialized = false;
+ bool initialized = false;
};
} // namespace elf
diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp
index 08ffe2a08c0fd..4d4789702f03d 100644
--- a/ELF/Arch/AArch64.cpp
+++ b/ELF/Arch/AArch64.cpp
@@ -1,9 +1,8 @@
//===- AArch64.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -24,60 +23,59 @@ using namespace lld::elf;
// Page(Expr) is the page address of the expression Expr, defined
// as (Expr & ~0xFFF). (This applies even if the machine page size
// supported by the platform has a different value.)
-uint64_t elf::getAArch64Page(uint64_t Expr) {
- return Expr & ~static_cast<uint64_t>(0xFFF);
+uint64_t elf::getAArch64Page(uint64_t expr) {
+ return expr & ~static_cast<uint64_t>(0xFFF);
}
namespace {
-class AArch64 final : public TargetInfo {
+class AArch64 : public TargetInfo {
public:
AArch64();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- RelType getDynRel(RelType Type) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const override;
uint32_t getThunkSectionSpacing() const override;
- bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
- bool usesOnlyLowPageBits(RelType Type) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const override;
- void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
+ bool usesOnlyLowPageBits(RelType type) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const override;
+ void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
AArch64::AArch64() {
- CopyRel = R_AARCH64_COPY;
- RelativeRel = R_AARCH64_RELATIVE;
- IRelativeRel = R_AARCH64_IRELATIVE;
- GotRel = R_AARCH64_GLOB_DAT;
- NoneRel = R_AARCH64_NONE;
- PltRel = R_AARCH64_JUMP_SLOT;
- TlsDescRel = R_AARCH64_TLSDESC;
- TlsGotRel = R_AARCH64_TLS_TPREL64;
- GotEntrySize = 8;
- GotPltEntrySize = 8;
- PltEntrySize = 16;
- PltHeaderSize = 32;
- DefaultMaxPageSize = 65536;
+ copyRel = R_AARCH64_COPY;
+ relativeRel = R_AARCH64_RELATIVE;
+ iRelativeRel = R_AARCH64_IRELATIVE;
+ gotRel = R_AARCH64_GLOB_DAT;
+ noneRel = R_AARCH64_NONE;
+ pltRel = R_AARCH64_JUMP_SLOT;
+ symbolicRel = R_AARCH64_ABS64;
+ tlsDescRel = R_AARCH64_TLSDESC;
+ tlsGotRel = R_AARCH64_TLS_TPREL64;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
+ defaultMaxPageSize = 65536;
// Align to the 2 MiB page size (known as a superpage or huge page).
// FreeBSD automatically promotes 2 MiB-aligned allocations.
- DefaultImageBase = 0x200000;
+ defaultImageBase = 0x200000;
- NeedsThunks = true;
+ needsThunks = true;
}
-RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_AARCH64_TLSDESC_ADR_PAGE21:
return R_AARCH64_TLSDESC_PAGE;
case R_AARCH64_TLSDESC_LD64_LO12:
@@ -105,6 +103,7 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
case R_AARCH64_LD_PREL_LO19:
return R_PC;
case R_AARCH64_ADR_PREL_PG_HI21:
+ case R_AARCH64_ADR_PREL_PG_HI21_NC:
return R_AARCH64_PAGE_PC;
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
@@ -119,18 +118,18 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
}
}
-RelExpr AArch64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const {
- if (Expr == R_RELAX_TLS_GD_TO_IE) {
- if (Type == R_AARCH64_TLSDESC_ADR_PAGE21)
+RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ if (expr == R_RELAX_TLS_GD_TO_IE) {
+ if (type == R_AARCH64_TLSDESC_ADR_PAGE21)
return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC;
return R_RELAX_TLS_GD_TO_IE_ABS;
}
- return Expr;
+ return expr;
}
-bool AArch64::usesOnlyLowPageBits(RelType Type) const {
- switch (Type) {
+bool AArch64::usesOnlyLowPageBits(RelType type) const {
+ switch (type) {
default:
return false;
case R_AARCH64_ADD_ABS_LO12_NC:
@@ -147,18 +146,18 @@ bool AArch64::usesOnlyLowPageBits(RelType Type) const {
}
}
-RelType AArch64::getDynRel(RelType Type) const {
- if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64)
- return Type;
+RelType AArch64::getDynRel(RelType type) const {
+ if (type == R_AARCH64_ABS64)
+ return type;
return R_AARCH64_NONE;
}
-void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- write64le(Buf, In.Plt->getVA());
+void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const {
+ write64le(buf, in.plt->getVA());
}
-void AArch64::writePltHeader(uint8_t *Buf) const {
- const uint8_t PltData[] = {
+void AArch64::writePltHeader(uint8_t *buf) const {
+ const uint8_t pltData[] = {
0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2]))
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))]
@@ -168,42 +167,42 @@ void AArch64::writePltHeader(uint8_t *Buf) const {
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5 // nop
};
- memcpy(Buf, PltData, sizeof(PltData));
-
- uint64_t Got = In.GotPlt->getVA();
- uint64_t Plt = In.Plt->getVA();
- relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(Got + 16) - getAArch64Page(Plt + 4));
- relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16);
- relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16);
+ memcpy(buf, pltData, sizeof(pltData));
+
+ uint64_t got = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
+ relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(got + 16) - getAArch64Page(plt + 4));
+ relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
+ relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
}
-void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Inst[] = {
+void AArch64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t inst[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))]
0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n]))
0x20, 0x02, 0x1f, 0xd6 // br x17
};
- memcpy(Buf, Inst, sizeof(Inst));
+ memcpy(buf, inst, sizeof(inst));
- relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr));
- relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr);
- relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr);
+ relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
+ relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
+ relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
}
-bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
+bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
// ELF for the ARM 64-bit architecture, section Call and Jump relocations
// only permits range extension thunks for R_AARCH64_CALL26 and
// R_AARCH64_JUMP26 relocation types.
- if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
return false;
- uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
- return !inBranchRange(Type, BranchAddr, Dst);
+ uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
+ return !inBranchRange(type, branchAddr, dst);
}
uint32_t AArch64::getThunkSectionSpacing() const {
@@ -213,71 +212,72 @@ uint32_t AArch64::getThunkSectionSpacing() const {
return (128 * 1024 * 1024) - 0x30000;
}
-bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
- if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
+bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
return true;
// The AArch64 call and unconditional branch instructions have a range of
// +/- 128 MiB.
- uint64_t Range = 128 * 1024 * 1024;
- if (Dst > Src) {
+ uint64_t range = 128 * 1024 * 1024;
+ if (dst > src) {
// Immediate of branch is signed.
- Range -= 4;
- return Dst - Src <= Range;
+ range -= 4;
+ return dst - src <= range;
}
- return Src - Dst <= Range;
+ return src - dst <= range;
}
-static void write32AArch64Addr(uint8_t *L, uint64_t Imm) {
- uint32_t ImmLo = (Imm & 0x3) << 29;
- uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
- uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
- write32le(L, (read32le(L) & ~Mask) | ImmLo | ImmHi);
+static void write32AArch64Addr(uint8_t *l, uint64_t imm) {
+ uint32_t immLo = (imm & 0x3) << 29;
+ uint32_t immHi = (imm & 0x1FFFFC) << 3;
+ uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3);
+ write32le(l, (read32le(l) & ~mask) | immLo | immHi);
}
// Return the bits [Start, End] from Val shifted Start bits.
// For instance, getBits(0xF0, 4, 8) returns 0xF.
-static uint64_t getBits(uint64_t Val, int Start, int End) {
- uint64_t Mask = ((uint64_t)1 << (End + 1 - Start)) - 1;
- return (Val >> Start) & Mask;
+static uint64_t getBits(uint64_t val, int start, int end) {
+ uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1;
+ return (val >> start) & mask;
}
-static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); }
+static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
-static void or32AArch64Imm(uint8_t *L, uint64_t Imm) {
- or32le(L, (Imm & 0xFFF) << 10);
+static void or32AArch64Imm(uint8_t *l, uint64_t imm) {
+ or32le(l, (imm & 0xFFF) << 10);
}
-void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
- checkIntUInt(Loc, Val, 16, Type);
- write16le(Loc, Val);
+ checkIntUInt(loc, val, 16, type);
+ write16le(loc, val);
break;
case R_AARCH64_ABS32:
case R_AARCH64_PREL32:
- checkIntUInt(Loc, Val, 32, Type);
- write32le(Loc, Val);
+ checkIntUInt(loc, val, 32, type);
+ write32le(loc, val);
break;
case R_AARCH64_ABS64:
- case R_AARCH64_GLOB_DAT:
case R_AARCH64_PREL64:
- write64le(Loc, Val);
+ write64le(loc, val);
break;
case R_AARCH64_ADD_ABS_LO12_NC:
- or32AArch64Imm(Loc, Val);
+ or32AArch64Imm(loc, val);
break;
case R_AARCH64_ADR_GOT_PAGE:
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSDESC_ADR_PAGE21:
- checkInt(Loc, Val, 33, Type);
- write32AArch64Addr(Loc, Val >> 12);
+ checkInt(loc, val, 33, type);
+ LLVM_FALLTHROUGH;
+ case R_AARCH64_ADR_PREL_PG_HI21_NC:
+ write32AArch64Addr(loc, val >> 12);
break;
case R_AARCH64_ADR_PREL_LO21:
- checkInt(Loc, Val, 21, Type);
- write32AArch64Addr(Loc, Val);
+ checkInt(loc, val, 21, type);
+ write32AArch64Addr(loc, val);
break;
case R_AARCH64_JUMP26:
// Normally we would just write the bits of the immediate field, however
@@ -287,75 +287,75 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// opcode and the immediate (0 001 | 01 imm26) we can do this
// transformation by placing a R_AARCH64_JUMP26 relocation at the offset of
// the instruction we want to patch.
- write32le(Loc, 0x14000000);
+ write32le(loc, 0x14000000);
LLVM_FALLTHROUGH;
case R_AARCH64_CALL26:
- checkInt(Loc, Val, 28, Type);
- or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
+ checkInt(loc, val, 28, type);
+ or32le(loc, (val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 21, Type);
- or32le(Loc, (Val & 0x1FFFFC) << 3);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 21, type);
+ or32le(loc, (val & 0x1FFFFC) << 3);
break;
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
- or32AArch64Imm(Loc, getBits(Val, 0, 11));
+ or32AArch64Imm(loc, getBits(val, 0, 11));
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
- checkAlignment(Loc, Val, 2, Type);
- or32AArch64Imm(Loc, getBits(Val, 1, 11));
+ checkAlignment(loc, val, 2, type);
+ or32AArch64Imm(loc, getBits(val, 1, 11));
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
- checkAlignment(Loc, Val, 4, Type);
- or32AArch64Imm(Loc, getBits(Val, 2, 11));
+ checkAlignment(loc, val, 4, type);
+ or32AArch64Imm(loc, getBits(val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
case R_AARCH64_TLSDESC_LD64_LO12:
- checkAlignment(Loc, Val, 8, Type);
- or32AArch64Imm(Loc, getBits(Val, 3, 11));
+ checkAlignment(loc, val, 8, type);
+ or32AArch64Imm(loc, getBits(val, 3, 11));
break;
case R_AARCH64_LDST128_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
- checkAlignment(Loc, Val, 16, Type);
- or32AArch64Imm(Loc, getBits(Val, 4, 11));
+ checkAlignment(loc, val, 16, type);
+ or32AArch64Imm(loc, getBits(val, 4, 11));
break;
case R_AARCH64_MOVW_UABS_G0_NC:
- or32le(Loc, (Val & 0xFFFF) << 5);
+ or32le(loc, (val & 0xFFFF) << 5);
break;
case R_AARCH64_MOVW_UABS_G1_NC:
- or32le(Loc, (Val & 0xFFFF0000) >> 11);
+ or32le(loc, (val & 0xFFFF0000) >> 11);
break;
case R_AARCH64_MOVW_UABS_G2_NC:
- or32le(Loc, (Val & 0xFFFF00000000) >> 27);
+ or32le(loc, (val & 0xFFFF00000000) >> 27);
break;
case R_AARCH64_MOVW_UABS_G3:
- or32le(Loc, (Val & 0xFFFF000000000000) >> 43);
+ or32le(loc, (val & 0xFFFF000000000000) >> 43);
break;
case R_AARCH64_TSTBR14:
- checkInt(Loc, Val, 16, Type);
- or32le(Loc, (Val & 0xFFFC) << 3);
+ checkInt(loc, val, 16, type);
+ or32le(loc, (val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- checkUInt(Loc, Val, 24, Type);
- or32AArch64Imm(Loc, Val >> 12);
+ checkUInt(loc, val, 24, type);
+ or32AArch64Imm(loc, val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case R_AARCH64_TLSDESC_ADD_LO12:
- or32AArch64Imm(Loc, Val);
+ or32AArch64Imm(loc, val);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
-void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// TLSDESC Global-Dynamic relocation are in the form:
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
@@ -367,25 +367,25 @@ void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// movk x0, #0x10
// nop
// nop
- checkUInt(Loc, Val, 32, Type);
+ checkUInt(loc, val, 32, type);
- switch (Type) {
+ switch (type) {
case R_AARCH64_TLSDESC_ADD_LO12:
case R_AARCH64_TLSDESC_CALL:
- write32le(Loc, 0xd503201f); // nop
+ write32le(loc, 0xd503201f); // nop
return;
case R_AARCH64_TLSDESC_ADR_PAGE21:
- write32le(Loc, 0xd2a00000 | (((Val >> 16) & 0xffff) << 5)); // movz
+ write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz
return;
case R_AARCH64_TLSDESC_LD64_LO12:
- write32le(Loc, 0xf2800000 | ((Val & 0xffff) << 5)); // movk
+ write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk
return;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
// TLSDESC Global-Dynamic relocation are in the form:
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
@@ -398,43 +398,193 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// nop
// nop
- switch (Type) {
+ switch (type) {
case R_AARCH64_TLSDESC_ADD_LO12:
case R_AARCH64_TLSDESC_CALL:
- write32le(Loc, 0xd503201f); // nop
+ write32le(loc, 0xd503201f); // nop
break;
case R_AARCH64_TLSDESC_ADR_PAGE21:
- write32le(Loc, 0x90000000); // adrp
- relocateOne(Loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, Val);
+ write32le(loc, 0x90000000); // adrp
+ relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val);
break;
case R_AARCH64_TLSDESC_LD64_LO12:
- write32le(Loc, 0xf9400000); // ldr
- relocateOne(Loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, Val);
+ write32le(loc, 0xf9400000); // ldr
+ relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
- checkUInt(Loc, Val, 32, Type);
+void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ checkUInt(loc, val, 32, type);
- if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
+ if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
// Generate MOVZ.
- uint32_t RegNo = read32le(Loc) & 0x1f;
- write32le(Loc, (0xd2a00000 | RegNo) | (((Val >> 16) & 0xffff) << 5));
+ uint32_t regNo = read32le(loc) & 0x1f;
+ write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5));
return;
}
- if (Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
+ if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
// Generate MOVK.
- uint32_t RegNo = read32le(Loc) & 0x1f;
- write32le(Loc, (0xf2800000 | RegNo) | ((Val & 0xffff) << 5));
+ uint32_t regNo = read32le(loc) & 0x1f;
+ write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5));
return;
}
llvm_unreachable("invalid relocation for TLS IE to LE relaxation");
}
-TargetInfo *elf::getAArch64TargetInfo() {
- static AArch64 Target;
- return &Target;
+// AArch64 may use security features in variant PLT sequences. These are:
+// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target
+// Indicator (BTI) introduced in armv8.5-a. The additional instructions used
+// in the variant Plt sequences are encoded in the Hint space so they can be
+// deployed on older architectures, which treat the instructions as a nop.
+// PAC and BTI can be combined leading to the following combinations:
+// writePltHeader
+// writePltHeaderBti (no PAC Header needed)
+// writePlt
+// writePltBti (BTI only)
+// writePltPac (PAC only)
+// writePltBtiPac (BTI and PAC)
+//
+// When PAC is enabled the dynamic loader encrypts the address that it places
+// in the .got.plt using the pacia1716 instruction which encrypts the value in
+// x17 using the modifier in x16. The static linker places autia1716 before the
+// indirect branch to x17 to authenticate the address in x17 with the modifier
+// in x16. This makes it more difficult for an attacker to modify the value in
+// the .got.plt.
+//
+// When BTI is enabled all indirect branches must land on a bti instruction.
+// The static linker must place a bti instruction at the start of any PLT entry
+// that may be the target of an indirect branch. As the PLT entries call the
+// lazy resolver indirectly this must have a bti instruction at start. In
+// general a bti instruction is not needed for a PLT entry as indirect calls
+// are resolved to the function address and not the PLT entry for the function.
+// There are a small number of cases where the PLT address can escape, such as
+// taking the address of a function or ifunc via a non got-generating
+// relocation, and a shared library refers to that symbol.
+//
+// We use the bti c variant of the instruction which permits indirect branches
+// (br) via x16/x17 and indirect function calls (blr) via any register. The ABI
+// guarantees that all indirect branches from code requiring BTI protection
+// will go via x16/x17
+
+namespace {
+class AArch64BtiPac final : public AArch64 {
+public:
+ AArch64BtiPac();
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+
+private:
+ bool btiHeader; // bti instruction needed in PLT Header
+ bool btiEntry; // bti instruction needed in PLT Entry
+ bool pacEntry; // autia1716 instruction needed in PLT Entry
+};
+} // namespace
+
+AArch64BtiPac::AArch64BtiPac() {
+ btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI);
+ // A BTI (Branch Target Indicator) Plt Entry is only required if the
+ // address of the PLT entry can be taken by the program, which permits an
+ // indirect jump to the PLT entry. This can happen when the address
+ // of the PLT entry for a function is canonicalised due to the address of
+ // the function in an executable being taken by a shared library.
+ // FIXME: There is a potential optimization to omit the BTI if we detect
+ // that the address of the PLT entry isn't taken.
+ btiEntry = btiHeader && !config->shared;
+ pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC);
+
+ if (btiEntry || pacEntry)
+ pltEntrySize = 24;
}
+
+void AArch64BtiPac::writePltHeader(uint8_t *buf) const {
+ const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
+ const uint8_t pltData[] = {
+ 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2]))
+ 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))]
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2]))
+ 0x20, 0x02, 0x1f, 0xd6, // br x17
+ 0x1f, 0x20, 0x03, 0xd5, // nop
+ 0x1f, 0x20, 0x03, 0xd5 // nop
+ };
+ const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
+
+ uint64_t got = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
+
+ if (btiHeader) {
+ // PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C
+ // instruction.
+ memcpy(buf, btiData, sizeof(btiData));
+ buf += sizeof(btiData);
+ plt += sizeof(btiData);
+ }
+ memcpy(buf, pltData, sizeof(pltData));
+
+ relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(got + 16) - getAArch64Page(plt + 8));
+ relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
+ relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
+ if (!btiHeader)
+ // We didn't add the BTI c instruction so round out size with NOP.
+ memcpy(buf + sizeof(pltData), nopData, sizeof(nopData));
+}
+
+void AArch64BtiPac::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ // The PLT entry is of the form:
+ // [btiData] addrInst (pacBr | stdBr) [nopData]
+ const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
+ const uint8_t addrInst[] = {
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
+ 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))]
+ 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n]))
+ };
+ const uint8_t pacBr[] = {
+ 0x9f, 0x21, 0x03, 0xd5, // autia1716
+ 0x20, 0x02, 0x1f, 0xd6 // br x17
+ };
+ const uint8_t stdBr[] = {
+ 0x20, 0x02, 0x1f, 0xd6, // br x17
+ 0x1f, 0x20, 0x03, 0xd5 // nop
+ };
+ const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
+
+ if (btiEntry) {
+ memcpy(buf, btiData, sizeof(btiData));
+ buf += sizeof(btiData);
+ pltEntryAddr += sizeof(btiData);
+ }
+
+ memcpy(buf, addrInst, sizeof(addrInst));
+ relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(gotPltEntryAddr) -
+ getAArch64Page(pltEntryAddr));
+ relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
+ relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
+
+ if (pacEntry)
+ memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr));
+ else
+ memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr));
+ if (!btiEntry)
+ // We didn't add the BTI c instruction so round out size with NOP.
+ memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData));
+}
+
+static TargetInfo *getTargetInfo() {
+ if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI |
+ GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) {
+ static AArch64BtiPac t;
+ return &t;
+ }
+ static AArch64 t;
+ return &t;
+}
+
+TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
diff --git a/ELF/Arch/AMDGPU.cpp b/ELF/Arch/AMDGPU.cpp
index a7c6c84ceecd4..f2e32ca0996d5 100644
--- a/ELF/Arch/AMDGPU.cpp
+++ b/ELF/Arch/AMDGPU.cpp
@@ -1,9 +1,8 @@
//===- AMDGPU.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -26,62 +25,63 @@ class AMDGPU final : public TargetInfo {
public:
AMDGPU();
uint32_t calcEFlags() const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
};
} // namespace
AMDGPU::AMDGPU() {
- RelativeRel = R_AMDGPU_RELATIVE64;
- GotRel = R_AMDGPU_ABS64;
- NoneRel = R_AMDGPU_NONE;
- GotEntrySize = 8;
+ relativeRel = R_AMDGPU_RELATIVE64;
+ gotRel = R_AMDGPU_ABS64;
+ noneRel = R_AMDGPU_NONE;
+ symbolicRel = R_AMDGPU_ABS64;
}
-static uint32_t getEFlags(InputFile *File) {
- return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags;
+static uint32_t getEFlags(InputFile *file) {
+ return cast<ObjFile<ELF64LE>>(file)->getObj().getHeader()->e_flags;
}
uint32_t AMDGPU::calcEFlags() const {
- assert(!ObjectFiles.empty());
- uint32_t Ret = getEFlags(ObjectFiles[0]);
+ assert(!objectFiles.empty());
+ uint32_t ret = getEFlags(objectFiles[0]);
// Verify that all input files have the same e_flags.
- for (InputFile *F : makeArrayRef(ObjectFiles).slice(1)) {
- if (Ret == getEFlags(F))
+ for (InputFile *f : makeArrayRef(objectFiles).slice(1)) {
+ if (ret == getEFlags(f))
continue;
- error("incompatible e_flags: " + toString(F));
+ error("incompatible e_flags: " + toString(f));
return 0;
}
- return Ret;
+ return ret;
}
-void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_AMDGPU_ABS32:
case R_AMDGPU_GOTPCREL:
case R_AMDGPU_GOTPCREL32_LO:
case R_AMDGPU_REL32:
case R_AMDGPU_REL32_LO:
- write32le(Loc, Val);
+ write32le(loc, val);
break;
case R_AMDGPU_ABS64:
case R_AMDGPU_REL64:
- write64le(Loc, Val);
+ write64le(loc, val);
break;
case R_AMDGPU_GOTPCREL32_HI:
case R_AMDGPU_REL32_HI:
- write32le(Loc, Val >> 32);
+ write32le(loc, val >> 32);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_AMDGPU_ABS32:
case R_AMDGPU_ABS64:
return R_ABS;
@@ -95,11 +95,19 @@ RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S,
case R_AMDGPU_GOTPCREL32_HI:
return R_GOT_PC;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
+RelType AMDGPU::getDynRel(RelType type) const {
+ if (type == R_AMDGPU_ABS64)
+ return type;
+ return R_AMDGPU_NONE;
+}
+
TargetInfo *elf::getAMDGPUTargetInfo() {
- static AMDGPU Target;
- return &Target;
+ static AMDGPU target;
+ return &target;
}
diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp
index 120caca671afe..64adc33c07ae2 100644
--- a/ELF/Arch/ARM.cpp
+++ b/ELF/Arch/ARM.cpp
@@ -1,9 +1,8 @@
//===- ARM.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -27,63 +26,62 @@ class ARM final : public TargetInfo {
public:
ARM();
uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- RelType getDynRel(RelType Type) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void addPltSymbols(InputSection &IS, uint64_t Off) const override;
- void addPltHeaderSymbols(InputSection &ISD) const override;
- bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void addPltSymbols(InputSection &isec, uint64_t off) const override;
+ void addPltHeaderSymbols(InputSection &isd) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const override;
uint32_t getThunkSectionSpacing() const override;
- bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
ARM::ARM() {
- CopyRel = R_ARM_COPY;
- RelativeRel = R_ARM_RELATIVE;
- IRelativeRel = R_ARM_IRELATIVE;
- GotRel = R_ARM_GLOB_DAT;
- NoneRel = R_ARM_NONE;
- PltRel = R_ARM_JUMP_SLOT;
- TlsGotRel = R_ARM_TLS_TPOFF32;
- TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
- TlsOffsetRel = R_ARM_TLS_DTPOFF32;
- GotBaseSymInGotPlt = false;
- GotEntrySize = 4;
- GotPltEntrySize = 4;
- PltEntrySize = 16;
- PltHeaderSize = 32;
- TrapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
- NeedsThunks = true;
+ copyRel = R_ARM_COPY;
+ relativeRel = R_ARM_RELATIVE;
+ iRelativeRel = R_ARM_IRELATIVE;
+ gotRel = R_ARM_GLOB_DAT;
+ noneRel = R_ARM_NONE;
+ pltRel = R_ARM_JUMP_SLOT;
+ symbolicRel = R_ARM_ABS32;
+ tlsGotRel = R_ARM_TLS_TPOFF32;
+ tlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
+ tlsOffsetRel = R_ARM_TLS_DTPOFF32;
+ gotBaseSymInGotPlt = false;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
+ trapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
+ needsThunks = true;
}
uint32_t ARM::calcEFlags() const {
// The ABIFloatType is used by loaders to detect the floating point calling
// convention.
- uint32_t ABIFloatType = 0;
- if (Config->ARMVFPArgs == ARMVFPArgKind::Base ||
- Config->ARMVFPArgs == ARMVFPArgKind::Default)
- ABIFloatType = EF_ARM_ABI_FLOAT_SOFT;
- else if (Config->ARMVFPArgs == ARMVFPArgKind::VFP)
- ABIFloatType = EF_ARM_ABI_FLOAT_HARD;
+ uint32_t abiFloatType = 0;
+ if (config->armVFPArgs == ARMVFPArgKind::Base ||
+ config->armVFPArgs == ARMVFPArgKind::Default)
+ abiFloatType = EF_ARM_ABI_FLOAT_SOFT;
+ else if (config->armVFPArgs == ARMVFPArgKind::VFP)
+ abiFloatType = EF_ARM_ABI_FLOAT_HARD;
// We don't currently use any features incompatible with EF_ARM_EABI_VER5,
// but we don't have any firm guarantees of conformance. Linux AArch64
// kernels (as of 2016) require an EABI version to be set.
- return EF_ARM_EABI_VER5 | ABIFloatType;
+ return EF_ARM_EABI_VER5 | abiFloatType;
}
-RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_ARM_THM_JUMP11:
return R_PC;
case R_ARM_CALL:
@@ -108,11 +106,11 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
case R_ARM_SBREL32:
return R_ARM_SBREL;
case R_ARM_TARGET1:
- return Config->Target1Rel ? R_PC : R_ABS;
+ return config->target1Rel ? R_PC : R_ABS;
case R_ARM_TARGET2:
- if (Config->Target2 == Target2Policy::Rel)
+ if (config->target2 == Target2Policy::Rel)
return R_PC;
- if (Config->Target2 == Target2Policy::Abs)
+ if (config->target2 == Target2Policy::Abs)
return R_ABS;
return R_GOT_PC;
case R_ARM_TLS_GD32:
@@ -145,25 +143,25 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
}
}
-RelType ARM::getDynRel(RelType Type) const {
- if ((Type == R_ARM_ABS32) || (Type == R_ARM_TARGET1 && !Config->Target1Rel))
+RelType ARM::getDynRel(RelType type) const {
+ if ((type == R_ARM_ABS32) || (type == R_ARM_TARGET1 && !config->target1Rel))
return R_ARM_ABS32;
return R_ARM_NONE;
}
-void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- write32le(Buf, In.Plt->getVA());
+void ARM::writeGotPlt(uint8_t *buf, const Symbol &) const {
+ write32le(buf, in.plt->getVA());
}
-void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
+void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
// An ARM entry is the address of the ifunc resolver function.
- write32le(Buf, S.getVA());
+ write32le(buf, s.getVA());
}
// Long form PLT Header that does not have any restrictions on the displacement
// of the .plt from the .plt.got.
-static void writePltHeaderLong(uint8_t *Buf) {
- const uint8_t PltData[] = {
+static void writePltHeaderLong(uint8_t *buf) {
+ const uint8_t pltData[] = {
0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]!
0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2
0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr
@@ -172,128 +170,128 @@ static void writePltHeaderLong(uint8_t *Buf) {
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
0xd4, 0xd4, 0xd4, 0xd4};
- memcpy(Buf, PltData, sizeof(PltData));
- uint64_t GotPlt = In.GotPlt->getVA();
- uint64_t L1 = In.Plt->getVA() + 8;
- write32le(Buf + 16, GotPlt - L1 - 8);
+ memcpy(buf, pltData, sizeof(pltData));
+ uint64_t gotPlt = in.gotPlt->getVA();
+ uint64_t l1 = in.plt->getVA() + 8;
+ write32le(buf + 16, gotPlt - l1 - 8);
}
// The default PLT header requires the .plt.got to be within 128 Mb of the
// .plt in the positive direction.
-void ARM::writePltHeader(uint8_t *Buf) const {
+void ARM::writePltHeader(uint8_t *buf) const {
// Use a similar sequence to that in writePlt(), the difference is the calling
// conventions mean we use lr instead of ip. The PLT entry is responsible for
// saving lr on the stack, the dynamic loader is responsible for reloading
// it.
- const uint32_t PltData[] = {
+ const uint32_t pltData[] = {
0xe52de004, // L1: str lr, [sp,#-4]!
0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4)
0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4)
0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4)
};
- uint64_t Offset = In.GotPlt->getVA() - In.Plt->getVA() - 4;
- if (!llvm::isUInt<27>(Offset)) {
+ uint64_t offset = in.gotPlt->getVA() - in.plt->getVA() - 4;
+ if (!llvm::isUInt<27>(offset)) {
// We cannot encode the Offset, use the long form.
- writePltHeaderLong(Buf);
+ writePltHeaderLong(buf);
return;
}
- write32le(Buf + 0, PltData[0]);
- write32le(Buf + 4, PltData[1] | ((Offset >> 20) & 0xff));
- write32le(Buf + 8, PltData[2] | ((Offset >> 12) & 0xff));
- write32le(Buf + 12, PltData[3] | (Offset & 0xfff));
- memcpy(Buf + 16, TrapInstr.data(), 4); // Pad to 32-byte boundary
- memcpy(Buf + 20, TrapInstr.data(), 4);
- memcpy(Buf + 24, TrapInstr.data(), 4);
- memcpy(Buf + 28, TrapInstr.data(), 4);
+ write32le(buf + 0, pltData[0]);
+ write32le(buf + 4, pltData[1] | ((offset >> 20) & 0xff));
+ write32le(buf + 8, pltData[2] | ((offset >> 12) & 0xff));
+ write32le(buf + 12, pltData[3] | (offset & 0xfff));
+ memcpy(buf + 16, trapInstr.data(), 4); // Pad to 32-byte boundary
+ memcpy(buf + 20, trapInstr.data(), 4);
+ memcpy(buf + 24, trapInstr.data(), 4);
+ memcpy(buf + 28, trapInstr.data(), 4);
}
-void ARM::addPltHeaderSymbols(InputSection &IS) const {
- addSyntheticLocal("$a", STT_NOTYPE, 0, 0, IS);
- addSyntheticLocal("$d", STT_NOTYPE, 16, 0, IS);
+void ARM::addPltHeaderSymbols(InputSection &isec) const {
+ addSyntheticLocal("$a", STT_NOTYPE, 0, 0, isec);
+ addSyntheticLocal("$d", STT_NOTYPE, 16, 0, isec);
}
// Long form PLT entries that do not have any restrictions on the displacement
// of the .plt from the .plt.got.
-static void writePltLong(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) {
- const uint8_t PltData[] = {
+static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) {
+ const uint8_t pltData[] = {
0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip]
0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - L1 - 8
};
- memcpy(Buf, PltData, sizeof(PltData));
- uint64_t L1 = PltEntryAddr + 4;
- write32le(Buf + 12, GotPltEntryAddr - L1 - 8);
+ memcpy(buf, pltData, sizeof(pltData));
+ uint64_t l1 = pltEntryAddr + 4;
+ write32le(buf + 12, gotPltEntryAddr - l1 - 8);
}
// The default PLT entries require the .plt.got to be within 128 Mb of the
// .plt in the positive direction.
-void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
+void ARM::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
// The PLT entry is similar to the example given in Appendix A of ELF for
// the Arm Architecture. Instead of using the Group Relocations to find the
// optimal rotation for the 8-bit immediate used in the add instructions we
// hard code the most compact rotations for simplicity. This saves a load
// instruction over the long plt sequences.
- const uint32_t PltData[] = {
+ const uint32_t pltData[] = {
0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8
0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8
0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8
};
- uint64_t Offset = GotPltEntryAddr - PltEntryAddr - 8;
- if (!llvm::isUInt<27>(Offset)) {
+ uint64_t offset = gotPltEntryAddr - pltEntryAddr - 8;
+ if (!llvm::isUInt<27>(offset)) {
// We cannot encode the Offset, use the long form.
- writePltLong(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff);
+ writePltLong(buf, gotPltEntryAddr, pltEntryAddr, index, relOff);
return;
}
- write32le(Buf + 0, PltData[0] | ((Offset >> 20) & 0xff));
- write32le(Buf + 4, PltData[1] | ((Offset >> 12) & 0xff));
- write32le(Buf + 8, PltData[2] | (Offset & 0xfff));
- memcpy(Buf + 12, TrapInstr.data(), 4); // Pad to 16-byte boundary
+ write32le(buf + 0, pltData[0] | ((offset >> 20) & 0xff));
+ write32le(buf + 4, pltData[1] | ((offset >> 12) & 0xff));
+ write32le(buf + 8, pltData[2] | (offset & 0xfff));
+ memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary
}
-void ARM::addPltSymbols(InputSection &IS, uint64_t Off) const {
- addSyntheticLocal("$a", STT_NOTYPE, Off, 0, IS);
- addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS);
+void ARM::addPltSymbols(InputSection &isec, uint64_t off) const {
+ addSyntheticLocal("$a", STT_NOTYPE, off, 0, isec);
+ addSyntheticLocal("$d", STT_NOTYPE, off + 12, 0, isec);
}
-bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
+bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
// If S is an undefined weak symbol and does not have a PLT entry then it
// will be resolved as a branch to the next instruction.
- if (S.isUndefWeak() && !S.isInPlt())
+ if (s.isUndefWeak() && !s.isInPlt())
return false;
// A state change from ARM to Thumb and vice versa must go through an
// interworking thunk if the relocation type is not R_ARM_CALL or
// R_ARM_THM_CALL.
- switch (Type) {
+ switch (type) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
// Source is ARM, all PLT entries are ARM so no interworking required.
// Otherwise we need to interwork if Symbol has bit 0 set (Thumb).
- if (Expr == R_PC && ((S.getVA() & 1) == 1))
+ if (expr == R_PC && ((s.getVA() & 1) == 1))
return true;
LLVM_FALLTHROUGH;
case R_ARM_CALL: {
- uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
- return !inBranchRange(Type, BranchAddr, Dst);
+ uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
+ return !inBranchRange(type, branchAddr, dst);
}
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
// Source is Thumb, all PLT entries are ARM so interworking is required.
// Otherwise we need to interwork if Symbol has bit 0 clear (ARM).
- if (Expr == R_PLT_PC || ((S.getVA() & 1) == 0))
+ if (expr == R_PLT_PC || ((s.getVA() & 1) == 0))
return true;
LLVM_FALLTHROUGH;
case R_ARM_THM_CALL: {
- uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
- return !inBranchRange(Type, BranchAddr, Dst);
+ uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
+ return !inBranchRange(type, branchAddr, dst);
}
}
return false;
@@ -301,13 +299,13 @@ bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint32_t ARM::getThunkSectionSpacing() const {
// The placing of pre-created ThunkSections is controlled by the value
- // ThunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to
+ // thunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to
// place the ThunkSection such that all branches from the InputSections
// prior to the ThunkSection can reach a Thunk placed at the end of the
// ThunkSection. Graphically:
- // | up to ThunkSectionSpacing .text input sections |
+ // | up to thunkSectionSpacing .text input sections |
// | ThunkSection |
- // | up to ThunkSectionSpacing .text input sections |
+ // | up to thunkSectionSpacing .text input sections |
// | ThunkSection |
// Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This
@@ -318,69 +316,68 @@ uint32_t ARM::getThunkSectionSpacing() const {
// Thumb B<cc>.W range +/- 1MiB
// If a branch cannot reach a pre-created ThunkSection a new one will be
// created so we can handle the rare cases of a Thumb 2 conditional branch.
- // We intentionally use a lower size for ThunkSectionSpacing than the maximum
+ // We intentionally use a lower size for thunkSectionSpacing than the maximum
// branch range so the end of the ThunkSection is more likely to be within
// range of the branch instruction that is furthest away. The value we shorten
- // ThunkSectionSpacing by is set conservatively to allow us to create 16,384
+ // thunkSectionSpacing by is set conservatively to allow us to create 16,384
// 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
// one of the Thunks going out of range.
- // On Arm the ThunkSectionSpacing depends on the range of the Thumb Branch
+ // On Arm the thunkSectionSpacing depends on the range of the Thumb Branch
// range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except
// ARMv6T2) the range is +/- 4MiB.
- return (Config->ARMJ1J2BranchEncoding) ? 0x1000000 - 0x30000
+ return (config->armJ1J2BranchEncoding) ? 0x1000000 - 0x30000
: 0x400000 - 0x7500;
}
-bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
- uint64_t Range;
- uint64_t InstrSize;
+bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ uint64_t range;
+ uint64_t instrSize;
- switch (Type) {
+ switch (type) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
case R_ARM_CALL:
- Range = 0x2000000;
- InstrSize = 4;
+ range = 0x2000000;
+ instrSize = 4;
break;
case R_ARM_THM_JUMP19:
- Range = 0x100000;
- InstrSize = 2;
+ range = 0x100000;
+ instrSize = 2;
break;
case R_ARM_THM_JUMP24:
case R_ARM_THM_CALL:
- Range = Config->ARMJ1J2BranchEncoding ? 0x1000000 : 0x400000;
- InstrSize = 2;
+ range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000;
+ instrSize = 2;
break;
default:
return true;
}
// PC at Src is 2 instructions ahead, immediate of branch is signed
- if (Src > Dst)
- Range -= 2 * InstrSize;
+ if (src > dst)
+ range -= 2 * instrSize;
else
- Range += InstrSize;
+ range += instrSize;
- if ((Dst & 0x1) == 0)
+ if ((dst & 0x1) == 0)
// Destination is ARM, if ARM caller then Src is already 4-byte aligned.
// If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure
// destination will be 4 byte aligned.
- Src &= ~0x3;
+ src &= ~0x3;
else
// Bit 0 == 1 denotes Thumb state, it is not part of the range
- Dst &= ~0x1;
+ dst &= ~0x1;
- uint64_t Distance = (Src > Dst) ? Src - Dst : Dst - Src;
- return Distance <= Range;
+ uint64_t distance = (src > dst) ? src - dst : dst - src;
+ return distance <= range;
}
-void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
- case R_ARM_GLOB_DAT:
case R_ARM_GOTOFF32:
case R_ARM_GOT_BREL:
case R_ARM_GOT_PREL:
@@ -396,135 +393,132 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_ARM_TLS_LE32:
case R_ARM_TLS_TPOFF32:
case R_ARM_TLS_DTPOFF32:
- write32le(Loc, Val);
- break;
- case R_ARM_TLS_DTPMOD32:
- write32le(Loc, 1);
+ write32le(loc, val);
break;
case R_ARM_PREL31:
- checkInt(Loc, Val, 31, Type);
- write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000));
+ checkInt(loc, val, 31, type);
+ write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000));
break;
case R_ARM_CALL:
// R_ARM_CALL is used for BL and BLX instructions, depending on the
// value of bit 0 of Val, we must select a BL or BLX instruction
- if (Val & 1) {
+ if (val & 1) {
// If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
- checkInt(Loc, Val, 26, Type);
- write32le(Loc, 0xfa000000 | // opcode
- ((Val & 2) << 23) | // H
- ((Val >> 2) & 0x00ffffff)); // imm24
+ checkInt(loc, val, 26, type);
+ write32le(loc, 0xfa000000 | // opcode
+ ((val & 2) << 23) | // H
+ ((val >> 2) & 0x00ffffff)); // imm24
break;
}
- if ((read32le(Loc) & 0xfe000000) == 0xfa000000)
+ if ((read32le(loc) & 0xfe000000) == 0xfa000000)
// BLX (always unconditional) instruction to an ARM Target, select an
// unconditional BL.
- write32le(Loc, 0xeb000000 | (read32le(Loc) & 0x00ffffff));
+ write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
// fall through as BL encoding is shared with B
LLVM_FALLTHROUGH;
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
- checkInt(Loc, Val, 26, Type);
- write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff));
+ checkInt(loc, val, 26, type);
+ write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff));
break;
case R_ARM_THM_JUMP11:
- checkInt(Loc, Val, 12, Type);
- write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff));
+ checkInt(loc, val, 12, type);
+ write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff));
break;
case R_ARM_THM_JUMP19:
// Encoding T3: Val = S:J2:J1:imm6:imm11:0
- checkInt(Loc, Val, 21, Type);
- write16le(Loc,
- (read16le(Loc) & 0xfbc0) | // opcode cond
- ((Val >> 10) & 0x0400) | // S
- ((Val >> 12) & 0x003f)); // imm6
- write16le(Loc + 2,
+ checkInt(loc, val, 21, type);
+ write16le(loc,
+ (read16le(loc) & 0xfbc0) | // opcode cond
+ ((val >> 10) & 0x0400) | // S
+ ((val >> 12) & 0x003f)); // imm6
+ write16le(loc + 2,
0x8000 | // opcode
- ((Val >> 8) & 0x0800) | // J2
- ((Val >> 5) & 0x2000) | // J1
- ((Val >> 1) & 0x07ff)); // imm11
+ ((val >> 8) & 0x0800) | // J2
+ ((val >> 5) & 0x2000) | // J1
+ ((val >> 1) & 0x07ff)); // imm11
break;
case R_ARM_THM_CALL:
// R_ARM_THM_CALL is used for BL and BLX instructions, depending on the
// value of bit 0 of Val, we must select a BL or BLX instruction
- if ((Val & 1) == 0) {
+ if ((val & 1) == 0) {
// Ensure BLX destination is 4-byte aligned. As BLX instruction may
// only be two byte aligned. This must be done before overflow check
- Val = alignTo(Val, 4);
+ val = alignTo(val, 4);
}
// Bit 12 is 0 for BLX, 1 for BL
- write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12);
- if (!Config->ARMJ1J2BranchEncoding) {
+ write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12);
+ if (!config->armJ1J2BranchEncoding) {
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
// different encoding rules and range due to J1 and J2 always being 1.
- checkInt(Loc, Val, 23, Type);
- write16le(Loc,
+ checkInt(loc, val, 23, type);
+ write16le(loc,
0xf000 | // opcode
- ((Val >> 12) & 0x07ff)); // imm11
- write16le(Loc + 2,
- (read16le(Loc + 2) & 0xd000) | // opcode
+ ((val >> 12) & 0x07ff)); // imm11
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0xd000) | // opcode
0x2800 | // J1 == J2 == 1
- ((Val >> 1) & 0x07ff)); // imm11
+ ((val >> 1) & 0x07ff)); // imm11
break;
}
// Fall through as rest of encoding is the same as B.W
LLVM_FALLTHROUGH;
case R_ARM_THM_JUMP24:
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
- checkInt(Loc, Val, 25, Type);
- write16le(Loc,
+ checkInt(loc, val, 25, type);
+ write16le(loc,
0xf000 | // opcode
- ((Val >> 14) & 0x0400) | // S
- ((Val >> 12) & 0x03ff)); // imm10
- write16le(Loc + 2,
- (read16le(Loc + 2) & 0xd000) | // opcode
- (((~(Val >> 10)) ^ (Val >> 11)) & 0x2000) | // J1
- (((~(Val >> 11)) ^ (Val >> 13)) & 0x0800) | // J2
- ((Val >> 1) & 0x07ff)); // imm11
+ ((val >> 14) & 0x0400) | // S
+ ((val >> 12) & 0x03ff)); // imm10
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0xd000) | // opcode
+ (((~(val >> 10)) ^ (val >> 11)) & 0x2000) | // J1
+ (((~(val >> 11)) ^ (val >> 13)) & 0x0800) | // J2
+ ((val >> 1) & 0x07ff)); // imm11
break;
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVW_PREL_NC:
- write32le(Loc, (read32le(Loc) & ~0x000f0fff) | ((Val & 0xf000) << 4) |
- (Val & 0x0fff));
+ write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) |
+ (val & 0x0fff));
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
- write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
- (((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
+ write32le(loc, (read32le(loc) & ~0x000f0fff) |
+ (((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff));
break;
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVT_PREL:
// Encoding T1: A = imm4:i:imm3:imm8
- write16le(Loc,
+ write16le(loc,
0xf2c0 | // opcode
- ((Val >> 17) & 0x0400) | // i
- ((Val >> 28) & 0x000f)); // imm4
- write16le(Loc + 2,
- (read16le(Loc + 2) & 0x8f00) | // opcode
- ((Val >> 12) & 0x7000) | // imm3
- ((Val >> 16) & 0x00ff)); // imm8
+ ((val >> 17) & 0x0400) | // i
+ ((val >> 28) & 0x000f)); // imm4
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0x8f00) | // opcode
+ ((val >> 12) & 0x7000) | // imm3
+ ((val >> 16) & 0x00ff)); // imm8
break;
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVW_PREL_NC:
// Encoding T3: A = imm4:i:imm3:imm8
- write16le(Loc,
+ write16le(loc,
0xf240 | // opcode
- ((Val >> 1) & 0x0400) | // i
- ((Val >> 12) & 0x000f)); // imm4
- write16le(Loc + 2,
- (read16le(Loc + 2) & 0x8f00) | // opcode
- ((Val << 4) & 0x7000) | // imm3
- (Val & 0x00ff)); // imm8
+ ((val >> 1) & 0x0400) | // i
+ ((val >> 12) & 0x000f)); // imm4
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0x8f00) | // opcode
+ ((val << 4) & 0x7000) | // imm3
+ (val & 0x00ff)); // imm8
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
-int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
- switch (Type) {
+int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
+ switch (type) {
default:
return 0;
case R_ARM_ABS32:
@@ -540,47 +534,47 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
case R_ARM_TLS_LDO32:
case R_ARM_TLS_IE32:
case R_ARM_TLS_LE32:
- return SignExtend64<32>(read32le(Buf));
+ return SignExtend64<32>(read32le(buf));
case R_ARM_PREL31:
- return SignExtend64<31>(read32le(Buf));
+ return SignExtend64<31>(read32le(buf));
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
- return SignExtend64<26>(read32le(Buf) << 2);
+ return SignExtend64<26>(read32le(buf) << 2);
case R_ARM_THM_JUMP11:
- return SignExtend64<12>(read16le(Buf) << 1);
+ return SignExtend64<12>(read16le(buf) << 1);
case R_ARM_THM_JUMP19: {
// Encoding T3: A = S:J2:J1:imm10:imm6:0
- uint16_t Hi = read16le(Buf);
- uint16_t Lo = read16le(Buf + 2);
- return SignExtend64<20>(((Hi & 0x0400) << 10) | // S
- ((Lo & 0x0800) << 8) | // J2
- ((Lo & 0x2000) << 5) | // J1
- ((Hi & 0x003f) << 12) | // imm6
- ((Lo & 0x07ff) << 1)); // imm11:0
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ return SignExtend64<20>(((hi & 0x0400) << 10) | // S
+ ((lo & 0x0800) << 8) | // J2
+ ((lo & 0x2000) << 5) | // J1
+ ((hi & 0x003f) << 12) | // imm6
+ ((lo & 0x07ff) << 1)); // imm11:0
}
case R_ARM_THM_CALL:
- if (!Config->ARMJ1J2BranchEncoding) {
+ if (!config->armJ1J2BranchEncoding) {
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
// different encoding rules and range due to J1 and J2 always being 1.
- uint16_t Hi = read16le(Buf);
- uint16_t Lo = read16le(Buf + 2);
- return SignExtend64<22>(((Hi & 0x7ff) << 12) | // imm11
- ((Lo & 0x7ff) << 1)); // imm11:0
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ return SignExtend64<22>(((hi & 0x7ff) << 12) | // imm11
+ ((lo & 0x7ff) << 1)); // imm11:0
break;
}
LLVM_FALLTHROUGH;
case R_ARM_THM_JUMP24: {
// Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0
// I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S)
- uint16_t Hi = read16le(Buf);
- uint16_t Lo = read16le(Buf + 2);
- return SignExtend64<24>(((Hi & 0x0400) << 14) | // S
- (~((Lo ^ (Hi << 3)) << 10) & 0x00800000) | // I1
- (~((Lo ^ (Hi << 1)) << 11) & 0x00400000) | // I2
- ((Hi & 0x003ff) << 12) | // imm0
- ((Lo & 0x007ff) << 1)); // imm11:0
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ return SignExtend64<24>(((hi & 0x0400) << 14) | // S
+ (~((lo ^ (hi << 3)) << 10) & 0x00800000) | // I1
+ (~((lo ^ (hi << 1)) << 11) & 0x00400000) | // I2
+ ((hi & 0x003ff) << 12) | // imm0
+ ((lo & 0x007ff) << 1)); // imm11:0
}
// ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and
// MOVT is in the range -32768 <= A < 32768
@@ -588,25 +582,25 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVT_PREL: {
- uint64_t Val = read32le(Buf) & 0x000f0fff;
- return SignExtend64<16>(((Val & 0x000f0000) >> 4) | (Val & 0x00fff));
+ uint64_t val = read32le(buf) & 0x000f0fff;
+ return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff));
}
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL: {
// Encoding T3: A = imm4:i:imm3:imm8
- uint16_t Hi = read16le(Buf);
- uint16_t Lo = read16le(Buf + 2);
- return SignExtend64<16>(((Hi & 0x000f) << 12) | // imm4
- ((Hi & 0x0400) << 1) | // i
- ((Lo & 0x7000) >> 4) | // imm3
- (Lo & 0x00ff)); // imm8
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ return SignExtend64<16>(((hi & 0x000f) << 12) | // imm4
+ ((hi & 0x0400) << 1) | // i
+ ((lo & 0x7000) >> 4) | // imm3
+ (lo & 0x00ff)); // imm8
}
}
}
TargetInfo *elf::getARMTargetInfo() {
- static ARM Target;
- return &Target;
+ static ARM target;
+ return &target;
}
diff --git a/ELF/Arch/AVR.cpp b/ELF/Arch/AVR.cpp
index 637da3778bd20..869f0fe0c5257 100644
--- a/ELF/Arch/AVR.cpp
+++ b/ELF/Arch/AVR.cpp
@@ -1,9 +1,8 @@
//===- AVR.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -44,34 +43,34 @@ namespace {
class AVR final : public TargetInfo {
public:
AVR();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
-AVR::AVR() { NoneRel = R_AVR_NONE; }
+AVR::AVR() { noneRel = R_AVR_NONE; }
-RelExpr AVR::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
+RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
return R_ABS;
}
-void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_AVR_CALL: {
- uint16_t Hi = Val >> 17;
- uint16_t Lo = Val >> 1;
- write16le(Loc, read16le(Loc) | ((Hi >> 1) << 4) | (Hi & 1));
- write16le(Loc + 2, Lo);
+ uint16_t hi = val >> 17;
+ uint16_t lo = val >> 1;
+ write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1));
+ write16le(loc + 2, lo);
break;
}
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
TargetInfo *elf::getAVRTargetInfo() {
- static AVR Target;
- return &Target;
+ static AVR target;
+ return &target;
}
diff --git a/ELF/Arch/Hexagon.cpp b/ELF/Arch/Hexagon.cpp
index b4d33be2ad39b..c497a6df79873 100644
--- a/ELF/Arch/Hexagon.cpp
+++ b/ELF/Arch/Hexagon.cpp
@@ -1,9 +1,8 @@
//===-- Hexagon.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -28,65 +27,65 @@ class Hexagon final : public TargetInfo {
public:
Hexagon();
uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
} // namespace
Hexagon::Hexagon() {
- PltRel = R_HEX_JMP_SLOT;
- RelativeRel = R_HEX_RELATIVE;
- GotRel = R_HEX_GLOB_DAT;
- GotEntrySize = 4;
+ pltRel = R_HEX_JMP_SLOT;
+ relativeRel = R_HEX_RELATIVE;
+ gotRel = R_HEX_GLOB_DAT;
+ symbolicRel = R_HEX_32;
+
// The zero'th GOT entry is reserved for the address of _DYNAMIC. The
// next 3 are reserved for the dynamic loader.
- GotPltHeaderEntriesNum = 4;
- GotPltEntrySize = 4;
+ gotPltHeaderEntriesNum = 4;
- PltEntrySize = 16;
- PltHeaderSize = 32;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
// Hexagon Linux uses 64K pages by default.
- DefaultMaxPageSize = 0x10000;
- NoneRel = R_HEX_NONE;
+ defaultMaxPageSize = 0x10000;
+ noneRel = R_HEX_NONE;
}
uint32_t Hexagon::calcEFlags() const {
- assert(!ObjectFiles.empty());
+ assert(!objectFiles.empty());
// The architecture revision must always be equal to or greater than
// greatest revision in the list of inputs.
- uint32_t Ret = 0;
- for (InputFile *F : ObjectFiles) {
- uint32_t EFlags = cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags;
- if (EFlags > Ret)
- Ret = EFlags;
+ uint32_t ret = 0;
+ for (InputFile *f : objectFiles) {
+ uint32_t eflags = cast<ObjFile<ELF32LE>>(f)->getObj().getHeader()->e_flags;
+ if (eflags > ret)
+ ret = eflags;
}
- return Ret;
+ return ret;
}
-static uint32_t applyMask(uint32_t Mask, uint32_t Data) {
- uint32_t Result = 0;
- size_t Off = 0;
+static uint32_t applyMask(uint32_t mask, uint32_t data) {
+ uint32_t result = 0;
+ size_t off = 0;
- for (size_t Bit = 0; Bit != 32; ++Bit) {
- uint32_t ValBit = (Data >> Off) & 1;
- uint32_t MaskBit = (Mask >> Bit) & 1;
- if (MaskBit) {
- Result |= (ValBit << Bit);
- ++Off;
+ for (size_t bit = 0; bit != 32; ++bit) {
+ uint32_t valBit = (data >> off) & 1;
+ uint32_t maskBit = (mask >> bit) & 1;
+ if (maskBit) {
+ result |= (valBit << bit);
+ ++off;
}
}
- return Result;
+ return result;
}
-RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_HEX_B9_PCREL:
case R_HEX_B9_PCREL_X:
case R_HEX_B13_PCREL:
@@ -109,16 +108,16 @@ RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S,
}
}
-static uint32_t findMaskR6(uint32_t Insn) {
+static uint32_t findMaskR6(uint32_t insn) {
// There are (arguably too) many relocation masks for the DSP's
// R_HEX_6_X type. The table below is used to select the correct mask
// for the given instruction.
struct InstructionMask {
- uint32_t CmpMask;
- uint32_t RelocMask;
+ uint32_t cmpMask;
+ uint32_t relocMask;
};
- static const InstructionMask R6[] = {
+ static const InstructionMask r6[] = {
{0x38000000, 0x0000201f}, {0x39000000, 0x0000201f},
{0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80},
{0x40000000, 0x000020f8}, {0x41000000, 0x000007e0},
@@ -136,124 +135,124 @@ static uint32_t findMaskR6(uint32_t Insn) {
// Duplex forms have a fixed mask and parse bits 15:14 are always
// zero. Non-duplex insns will always have at least one bit set in the
// parse field.
- if ((0xC000 & Insn) == 0x0)
+ if ((0xC000 & insn) == 0x0)
return 0x03f00000;
- for (InstructionMask I : R6)
- if ((0xff000000 & Insn) == I.CmpMask)
- return I.RelocMask;
+ for (InstructionMask i : r6)
+ if ((0xff000000 & insn) == i.cmpMask)
+ return i.relocMask;
error("unrecognized instruction for R_HEX_6 relocation: 0x" +
- utohexstr(Insn));
+ utohexstr(insn));
return 0;
}
-static uint32_t findMaskR8(uint32_t Insn) {
- if ((0xff000000 & Insn) == 0xde000000)
+static uint32_t findMaskR8(uint32_t insn) {
+ if ((0xff000000 & insn) == 0xde000000)
return 0x00e020e8;
- if ((0xff000000 & Insn) == 0x3c000000)
+ if ((0xff000000 & insn) == 0x3c000000)
return 0x0000207f;
return 0x00001fe0;
}
-static uint32_t findMaskR11(uint32_t Insn) {
- if ((0xff000000 & Insn) == 0xa1000000)
+static uint32_t findMaskR11(uint32_t insn) {
+ if ((0xff000000 & insn) == 0xa1000000)
return 0x060020ff;
return 0x06003fe0;
}
-static uint32_t findMaskR16(uint32_t Insn) {
- if ((0xff000000 & Insn) == 0x48000000)
+static uint32_t findMaskR16(uint32_t insn) {
+ if ((0xff000000 & insn) == 0x48000000)
return 0x061f20ff;
- if ((0xff000000 & Insn) == 0x49000000)
+ if ((0xff000000 & insn) == 0x49000000)
return 0x061f3fe0;
- if ((0xff000000 & Insn) == 0x78000000)
+ if ((0xff000000 & insn) == 0x78000000)
return 0x00df3fe0;
- if ((0xff000000 & Insn) == 0xb0000000)
+ if ((0xff000000 & insn) == 0xb0000000)
return 0x0fe03fe0;
error("unrecognized instruction for R_HEX_16_X relocation: 0x" +
- utohexstr(Insn));
+ utohexstr(insn));
return 0;
}
-static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); }
+static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
-void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_HEX_NONE:
break;
case R_HEX_6_PCREL_X:
case R_HEX_6_X:
- or32le(Loc, applyMask(findMaskR6(read32le(Loc)), Val));
+ or32le(loc, applyMask(findMaskR6(read32le(loc)), val));
break;
case R_HEX_8_X:
- or32le(Loc, applyMask(findMaskR8(read32le(Loc)), Val));
+ or32le(loc, applyMask(findMaskR8(read32le(loc)), val));
break;
case R_HEX_9_X:
- or32le(Loc, applyMask(0x00003fe0, Val & 0x3f));
+ or32le(loc, applyMask(0x00003fe0, val & 0x3f));
break;
case R_HEX_10_X:
- or32le(Loc, applyMask(0x00203fe0, Val & 0x3f));
+ or32le(loc, applyMask(0x00203fe0, val & 0x3f));
break;
case R_HEX_11_X:
case R_HEX_GOT_11_X:
- or32le(Loc, applyMask(findMaskR11(read32le(Loc)), Val & 0x3f));
+ or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f));
break;
case R_HEX_12_X:
- or32le(Loc, applyMask(0x000007e0, Val));
+ or32le(loc, applyMask(0x000007e0, val));
break;
case R_HEX_16_X: // These relocs only have 6 effective bits.
case R_HEX_GOT_16_X:
- or32le(Loc, applyMask(findMaskR16(read32le(Loc)), Val & 0x3f));
+ or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f));
break;
case R_HEX_32:
case R_HEX_32_PCREL:
- or32le(Loc, Val);
+ or32le(loc, val);
break;
case R_HEX_32_6_X:
case R_HEX_GOT_32_6_X:
- or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
+ or32le(loc, applyMask(0x0fff3fff, val >> 6));
break;
case R_HEX_B9_PCREL:
- or32le(Loc, applyMask(0x003000fe, Val >> 2));
+ or32le(loc, applyMask(0x003000fe, val >> 2));
break;
case R_HEX_B9_PCREL_X:
- or32le(Loc, applyMask(0x003000fe, Val & 0x3f));
+ or32le(loc, applyMask(0x003000fe, val & 0x3f));
break;
case R_HEX_B13_PCREL:
- or32le(Loc, applyMask(0x00202ffe, Val >> 2));
+ or32le(loc, applyMask(0x00202ffe, val >> 2));
break;
case R_HEX_B15_PCREL:
- or32le(Loc, applyMask(0x00df20fe, Val >> 2));
+ or32le(loc, applyMask(0x00df20fe, val >> 2));
break;
case R_HEX_B15_PCREL_X:
- or32le(Loc, applyMask(0x00df20fe, Val & 0x3f));
+ or32le(loc, applyMask(0x00df20fe, val & 0x3f));
break;
case R_HEX_B22_PCREL:
case R_HEX_PLT_B22_PCREL:
- or32le(Loc, applyMask(0x1ff3ffe, Val >> 2));
+ or32le(loc, applyMask(0x1ff3ffe, val >> 2));
break;
case R_HEX_B22_PCREL_X:
- or32le(Loc, applyMask(0x1ff3ffe, Val & 0x3f));
+ or32le(loc, applyMask(0x1ff3ffe, val & 0x3f));
break;
case R_HEX_B32_PCREL_X:
- or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
+ or32le(loc, applyMask(0x0fff3fff, val >> 6));
break;
case R_HEX_HI16:
- or32le(Loc, applyMask(0x00c03fff, Val >> 16));
+ or32le(loc, applyMask(0x00c03fff, val >> 16));
break;
case R_HEX_LO16:
- or32le(Loc, applyMask(0x00c03fff, Val));
+ or32le(loc, applyMask(0x00c03fff, val));
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
break;
}
}
-void Hexagon::writePltHeader(uint8_t *Buf) const {
- const uint8_t PltData[] = {
+void Hexagon::writePltHeader(uint8_t *buf) const {
+ const uint8_t pltData[] = {
0x00, 0x40, 0x00, 0x00, // { immext (#0)
0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0
0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn
@@ -263,30 +262,30 @@ void Hexagon::writePltHeader(uint8_t *Buf) const {
0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker
0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment
};
- memcpy(Buf, PltData, sizeof(PltData));
+ memcpy(buf, pltData, sizeof(pltData));
// Offset from PLT0 to the GOT.
- uint64_t Off = In.GotPlt->getVA() - In.Plt->getVA();
- relocateOne(Buf, R_HEX_B32_PCREL_X, Off);
- relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off);
+ uint64_t off = in.gotPlt->getVA() - in.plt->getVA();
+ relocateOne(buf, R_HEX_B32_PCREL_X, off);
+ relocateOne(buf + 4, R_HEX_6_PCREL_X, off);
}
-void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Inst[] = {
+void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t inst[] = {
0x00, 0x40, 0x00, 0x00, // { immext (#0)
0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) }
0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14)
0x00, 0xc0, 0x9c, 0x52, // jumpr r28
};
- memcpy(Buf, Inst, sizeof(Inst));
+ memcpy(buf, inst, sizeof(inst));
- relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr);
- relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr);
+ relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr);
+ relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
}
TargetInfo *elf::getHexagonTargetInfo() {
- static Hexagon Target;
- return &Target;
+ static Hexagon target;
+ return &target;
}
diff --git a/ELF/Arch/MSP430.cpp b/ELF/Arch/MSP430.cpp
index fe0c0fe64daf0..90664396c85ea 100644
--- a/ELF/Arch/MSP430.cpp
+++ b/ELF/Arch/MSP430.cpp
@@ -1,9 +1,8 @@
//===- MSP430.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -34,20 +33,20 @@ namespace {
class MSP430 final : public TargetInfo {
public:
MSP430();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
MSP430::MSP430() {
// mov.b #0, r3
- TrapInstr = {0x43, 0x43, 0x43, 0x43};
+ trapInstr = {0x43, 0x43, 0x43, 0x43};
}
-RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr MSP430::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_MSP430_10_PCREL:
case R_MSP430_16_PCREL:
case R_MSP430_16_PCREL_BYTE:
@@ -60,35 +59,35 @@ RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S,
}
}
-void MSP430::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_MSP430_8:
- checkIntUInt(Loc, Val, 8, Type);
- *Loc = Val;
+ checkIntUInt(loc, val, 8, type);
+ *loc = val;
break;
case R_MSP430_16:
case R_MSP430_16_PCREL:
case R_MSP430_16_BYTE:
case R_MSP430_16_PCREL_BYTE:
- checkIntUInt(Loc, Val, 16, Type);
- write16le(Loc, Val);
+ checkIntUInt(loc, val, 16, type);
+ write16le(loc, val);
break;
case R_MSP430_32:
- checkIntUInt(Loc, Val, 32, Type);
- write32le(Loc, Val);
+ checkIntUInt(loc, val, 32, type);
+ write32le(loc, val);
break;
case R_MSP430_10_PCREL: {
- int16_t Offset = ((int16_t)Val >> 1) - 1;
- checkInt(Loc, Offset, 10, Type);
- write16le(Loc, (read16le(Loc) & 0xFC00) | (Offset & 0x3FF));
+ int16_t offset = ((int16_t)val >> 1) - 1;
+ checkInt(loc, offset, 10, type);
+ write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF));
break;
}
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
TargetInfo *elf::getMSP430TargetInfo() {
- static MSP430 Target;
- return &Target;
+ static MSP430 target;
+ return &target;
}
diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp
index 23b0c1dd8a2d1..24b3957acd990 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -1,9 +1,8 @@
//===- MIPS.cpp -----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -29,47 +28,47 @@ template <class ELFT> class MIPS final : public TargetInfo {
public:
MIPS();
uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
- RelType getDynRel(RelType Type) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- bool usesOnlyLowPageBits(RelType Type) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ bool usesOnlyLowPageBits(RelType type) const override;
};
} // namespace
template <class ELFT> MIPS<ELFT>::MIPS() {
- GotPltHeaderEntriesNum = 2;
- DefaultMaxPageSize = 65536;
- GotEntrySize = sizeof(typename ELFT::uint);
- GotPltEntrySize = sizeof(typename ELFT::uint);
- GotBaseSymInGotPlt = false;
- PltEntrySize = 16;
- PltHeaderSize = 32;
- CopyRel = R_MIPS_COPY;
- NoneRel = R_MIPS_NONE;
- PltRel = R_MIPS_JUMP_SLOT;
- NeedsThunks = true;
+ gotPltHeaderEntriesNum = 2;
+ defaultMaxPageSize = 65536;
+ gotBaseSymInGotPlt = false;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
+ copyRel = R_MIPS_COPY;
+ noneRel = R_MIPS_NONE;
+ pltRel = R_MIPS_JUMP_SLOT;
+ needsThunks = true;
// Set `sigrie 1` as a trap instruction.
- write32(TrapInstr.data(), 0x04170001);
+ write32(trapInstr.data(), 0x04170001);
if (ELFT::Is64Bits) {
- RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
- TlsGotRel = R_MIPS_TLS_TPREL64;
- TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64;
- TlsOffsetRel = R_MIPS_TLS_DTPREL64;
+ relativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
+ symbolicRel = R_MIPS_64;
+ tlsGotRel = R_MIPS_TLS_TPREL64;
+ tlsModuleIndexRel = R_MIPS_TLS_DTPMOD64;
+ tlsOffsetRel = R_MIPS_TLS_DTPREL64;
} else {
- RelativeRel = R_MIPS_REL32;
- TlsGotRel = R_MIPS_TLS_TPREL32;
- TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32;
- TlsOffsetRel = R_MIPS_TLS_DTPREL32;
+ relativeRel = R_MIPS_REL32;
+ symbolicRel = R_MIPS_32;
+ tlsGotRel = R_MIPS_TLS_TPREL32;
+ tlsModuleIndexRel = R_MIPS_TLS_DTPMOD32;
+ tlsOffsetRel = R_MIPS_TLS_DTPREL32;
}
}
@@ -78,13 +77,13 @@ template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const {
}
template <class ELFT>
-RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
+RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
// See comment in the calculateMipsRelChain.
- if (ELFT::Is64Bits || Config->MipsN32Abi)
- Type &= 0xff;
+ if (ELFT::Is64Bits || config->mipsN32Abi)
+ type &= 0xff;
- switch (Type) {
+ switch (type) {
case R_MIPS_JALR:
case R_MICROMIPS_JALR:
return R_HINT;
@@ -108,9 +107,9 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
// offset between start of function and 'gp' value which by default
// equal to the start of .got section. In that case we consider these
// relocations as relative.
- if (&S == ElfSym::MipsGpDisp)
+ if (&s == ElfSym::mipsGpDisp)
return R_MIPS_GOT_GP_PC;
- if (&S == ElfSym::MipsLocalGp)
+ if (&s == ElfSym::mipsLocalGp)
return R_MIPS_GOT_GP;
LLVM_FALLTHROUGH;
case R_MIPS_32:
@@ -147,7 +146,7 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
return R_PC;
case R_MIPS_GOT16:
case R_MICROMIPS_GOT16:
- if (S.isLocal())
+ if (s.isLocal())
return R_MIPS_GOT_LOCAL_PAGE;
LLVM_FALLTHROUGH;
case R_MIPS_CALL16:
@@ -176,209 +175,213 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MIPS_NONE:
return R_NONE;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
-template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const {
- if (Type == R_MIPS_32 || Type == R_MIPS_64)
- return RelativeRel;
+template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType type) const {
+ if (type == symbolicRel)
+ return type;
return R_MIPS_NONE;
}
template <class ELFT>
-void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- uint64_t VA = In.Plt->getVA();
+void MIPS<ELFT>::writeGotPlt(uint8_t *buf, const Symbol &) const {
+ uint64_t va = in.plt->getVA();
if (isMicroMips())
- VA |= 1;
- write32<ELFT::TargetEndianness>(Buf, VA);
+ va |= 1;
+ write32<ELFT::TargetEndianness>(buf, va);
}
-template <endianness E> static uint32_t readShuffle(const uint8_t *Loc) {
+template <endianness E> static uint32_t readShuffle(const uint8_t *loc) {
// The major opcode of a microMIPS instruction needs to appear
// in the first 16-bit word (lowest address) for efficient hardware
// decode so that it knows if the instruction is 16-bit or 32-bit
// as early as possible. To do so, little-endian binaries keep 16-bit
// words in a big-endian order. That is why we have to swap these
// words to get a correct value.
- uint32_t V = read32<E>(Loc);
+ uint32_t v = read32<E>(loc);
if (E == support::little)
- return (V << 16) | (V >> 16);
- return V;
+ return (v << 16) | (v >> 16);
+ return v;
}
template <endianness E>
-static void writeValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
- uint8_t Shift) {
- uint32_t Instr = read32<E>(Loc);
- uint32_t Mask = 0xffffffff >> (32 - BitsSize);
- uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
- write32<E>(Loc, Data);
+static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
+ uint8_t shift) {
+ uint32_t instr = read32<E>(loc);
+ uint32_t mask = 0xffffffff >> (32 - bitsSize);
+ uint32_t data = (instr & ~mask) | ((v >> shift) & mask);
+ write32<E>(loc, data);
}
template <endianness E>
-static void writeShuffleValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
- uint8_t Shift) {
+static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
+ uint8_t shift) {
// See comments in readShuffle for purpose of this code.
- uint16_t *Words = (uint16_t *)Loc;
+ uint16_t *words = (uint16_t *)loc;
if (E == support::little)
- std::swap(Words[0], Words[1]);
+ std::swap(words[0], words[1]);
- writeValue<E>(Loc, V, BitsSize, Shift);
+ writeValue<E>(loc, v, bitsSize, shift);
if (E == support::little)
- std::swap(Words[0], Words[1]);
+ std::swap(words[0], words[1]);
}
template <endianness E>
-static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
- uint8_t Shift) {
- uint16_t Instr = read16<E>(Loc);
- uint16_t Mask = 0xffff >> (16 - BitsSize);
- uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
- write16<E>(Loc, Data);
+static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize,
+ uint8_t shift) {
+ uint16_t instr = read16<E>(loc);
+ uint16_t mask = 0xffff >> (16 - bitsSize);
+ uint16_t data = (instr & ~mask) | ((v >> shift) & mask);
+ write16<E>(loc, data);
}
-template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
- const endianness E = ELFT::TargetEndianness;
+template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const {
+ const endianness e = ELFT::TargetEndianness;
if (isMicroMips()) {
- uint64_t GotPlt = In.GotPlt->getVA();
- uint64_t Plt = In.Plt->getVA();
+ uint64_t gotPlt = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
// Overwrite trap instructions written by Writer::writeTrapInstr.
- memset(Buf, 0, PltHeaderSize);
-
- write16<E>(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
- write16<E>(Buf + 4, 0xff23); // lw $25, 0($3)
- write16<E>(Buf + 8, 0x0535); // subu16 $2, $2, $3
- write16<E>(Buf + 10, 0x2525); // srl16 $2, $2, 2
- write16<E>(Buf + 12, 0x3302); // addiu $24, $2, -2
- write16<E>(Buf + 14, 0xfffe);
- write16<E>(Buf + 16, 0x0dff); // move $15, $31
+ memset(buf, 0, pltHeaderSize);
+
+ write16<e>(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
+ write16<e>(buf + 4, 0xff23); // lw $25, 0($3)
+ write16<e>(buf + 8, 0x0535); // subu16 $2, $2, $3
+ write16<e>(buf + 10, 0x2525); // srl16 $2, $2, 2
+ write16<e>(buf + 12, 0x3302); // addiu $24, $2, -2
+ write16<e>(buf + 14, 0xfffe);
+ write16<e>(buf + 16, 0x0dff); // move $15, $31
if (isMipsR6()) {
- write16<E>(Buf + 18, 0x0f83); // move $28, $3
- write16<E>(Buf + 20, 0x472b); // jalrc $25
- write16<E>(Buf + 22, 0x0c00); // nop
- relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt);
+ write16<e>(buf + 18, 0x0f83); // move $28, $3
+ write16<e>(buf + 20, 0x472b); // jalrc $25
+ write16<e>(buf + 22, 0x0c00); // nop
+ relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
} else {
- write16<E>(Buf + 18, 0x45f9); // jalrc $25
- write16<E>(Buf + 20, 0x0f83); // move $28, $3
- write16<E>(Buf + 22, 0x0c00); // nop
- relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt);
+ write16<e>(buf + 18, 0x45f9); // jalrc $25
+ write16<e>(buf + 20, 0x0f83); // move $28, $3
+ write16<e>(buf + 22, 0x0c00); // nop
+ relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
}
return;
}
- if (Config->MipsN32Abi) {
- write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
- write32<E>(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
- write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
- write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
- write32<E>(Buf + 16, 0x03e07825); // move $15, $31
- write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
+ if (config->mipsN32Abi) {
+ write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
+ write32<e>(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
+ write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
+ write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
+ write32<e>(buf + 16, 0x03e07825); // move $15, $31
+ write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
} else if (ELFT::Is64Bits) {
- write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
- write32<E>(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
- write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
- write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
- write32<E>(Buf + 16, 0x03e07825); // move $15, $31
- write32<E>(Buf + 20, 0x0018c0c2); // srl $24, $24, 3
+ write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
+ write32<e>(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
+ write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
+ write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
+ write32<e>(buf + 16, 0x03e07825); // move $15, $31
+ write32<e>(buf + 20, 0x0018c0c2); // srl $24, $24, 3
} else {
- write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
- write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
- write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
- write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
- write32<E>(Buf + 16, 0x03e07825); // move $15, $31
- write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
+ write32<e>(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
+ write32<e>(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
+ write32<e>(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
+ write32<e>(buf + 12, 0x031cc023); // subu $24, $24, $28
+ write32<e>(buf + 16, 0x03e07825); // move $15, $31
+ write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
}
- uint32_t JalrInst = Config->ZHazardplt ? 0x0320fc09 : 0x0320f809;
- write32<E>(Buf + 24, JalrInst); // jalr.hb $25 or jalr $25
- write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
+ uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809;
+ write32<e>(buf + 24, jalrInst); // jalr.hb $25 or jalr $25
+ write32<e>(buf + 28, 0x2718fffe); // subu $24, $24, 2
- uint64_t GotPlt = In.GotPlt->getVA();
- writeValue<E>(Buf, GotPlt + 0x8000, 16, 16);
- writeValue<E>(Buf + 4, GotPlt, 16, 0);
- writeValue<E>(Buf + 8, GotPlt, 16, 0);
+ uint64_t gotPlt = in.gotPlt->getVA();
+ writeValue<e>(buf, gotPlt + 0x8000, 16, 16);
+ writeValue<e>(buf + 4, gotPlt, 16, 0);
+ writeValue<e>(buf + 8, gotPlt, 16, 0);
}
template <class ELFT>
-void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const endianness E = ELFT::TargetEndianness;
+void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const endianness e = ELFT::TargetEndianness;
if (isMicroMips()) {
// Overwrite trap instructions written by Writer::writeTrapInstr.
- memset(Buf, 0, PltEntrySize);
+ memset(buf, 0, pltEntrySize);
if (isMipsR6()) {
- write16<E>(Buf, 0x7840); // addiupc $2, (GOTPLT) - .
- write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
- write16<E>(Buf + 8, 0x0f02); // move $24, $2
- write16<E>(Buf + 10, 0x4723); // jrc $25 / jr16 $25
- relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr);
+ write16<e>(buf, 0x7840); // addiupc $2, (GOTPLT) - .
+ write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
+ write16<e>(buf + 8, 0x0f02); // move $24, $2
+ write16<e>(buf + 10, 0x4723); // jrc $25 / jr16 $25
+ relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
} else {
- write16<E>(Buf, 0x7900); // addiupc $2, (GOTPLT) - .
- write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
- write16<E>(Buf + 8, 0x4599); // jrc $25 / jr16 $25
- write16<E>(Buf + 10, 0x0f02); // move $24, $2
- relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr);
+ write16<e>(buf, 0x7900); // addiupc $2, (GOTPLT) - .
+ write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
+ write16<e>(buf + 8, 0x4599); // jrc $25 / jr16 $25
+ write16<e>(buf + 10, 0x0f02); // move $24, $2
+ relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
}
return;
}
- uint32_t JrInst = isMipsR6() ? (Config->ZHazardplt ? 0x03200409 : 0x03200009)
- : (Config->ZHazardplt ? 0x03200408 : 0x03200008);
-
- write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
- write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
- write32<E>(Buf + 8, JrInst); // jr $25 / jr.hb $25
- write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
- writeValue<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
- writeValue<E>(Buf + 4, GotPltEntryAddr, 16, 0);
- writeValue<E>(Buf + 12, GotPltEntryAddr, 16, 0);
+ uint32_t loadInst = ELFT::Is64Bits ? 0xddf90000 : 0x8df90000;
+ uint32_t jrInst = isMipsR6() ? (config->zHazardplt ? 0x03200409 : 0x03200009)
+ : (config->zHazardplt ? 0x03200408 : 0x03200008);
+ uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000;
+
+ write32<e>(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
+ write32<e>(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15)
+ write32<e>(buf + 8, jrInst); // jr $25 / jr.hb $25
+ write32<e>(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry)
+ writeValue<e>(buf, gotPltEntryAddr + 0x8000, 16, 16);
+ writeValue<e>(buf + 4, gotPltEntryAddr, 16, 0);
+ writeValue<e>(buf + 12, gotPltEntryAddr, 16, 0);
}
template <class ELFT>
-bool MIPS<ELFT>::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
+bool MIPS<ELFT>::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
// Any MIPS PIC code function is invoked with its address in register $t9.
// So if we have a branch instruction from non-PIC code to the PIC one
// we cannot make the jump directly and need to create a small stubs
// to save the target function address.
// See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- if (Type != R_MIPS_26 && Type != R_MICROMIPS_26_S1 &&
- Type != R_MICROMIPS_PC26_S1)
+ if (type != R_MIPS_26 && type != R_MIPS_PC26_S2 &&
+ type != R_MICROMIPS_26_S1 && type != R_MICROMIPS_PC26_S1)
return false;
- auto *F = dyn_cast_or_null<ELFFileBase<ELFT>>(File);
- if (!F)
+ auto *f = dyn_cast_or_null<ObjFile<ELFT>>(file);
+ if (!f)
return false;
// If current file has PIC code, LA25 stub is not required.
- if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
+ if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC)
return false;
- auto *D = dyn_cast<Defined>(&S);
+ auto *d = dyn_cast<Defined>(&s);
// LA25 is required if target file has PIC code
// or target symbol is a PIC symbol.
- return D && isMipsPIC<ELFT>(D);
+ return d && isMipsPIC<ELFT>(d);
}
template <class ELFT>
-int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
- const endianness E = ELFT::TargetEndianness;
- switch (Type) {
+int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
+ const endianness e = ELFT::TargetEndianness;
+ switch (type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_TPREL32:
- return SignExtend64<32>(read32<E>(Buf));
+ return SignExtend64<32>(read32<e>(buf));
case R_MIPS_26:
// FIXME (simon): If the relocation target symbol is not a PLT entry
// we should use another expression for calculation:
// ((A << 2) | (P & 0xf0000000)) >> 2
- return SignExtend64<28>(read32<E>(Buf) << 2);
+ return SignExtend64<28>(read32<e>(buf) << 2);
case R_MIPS_GOT16:
case R_MIPS_HI16:
case R_MIPS_PCHI16:
- return SignExtend64<16>(read32<E>(Buf)) << 16;
+ return SignExtend64<16>(read32<e>(buf)) << 16;
case R_MIPS_GPREL16:
case R_MIPS_LO16:
case R_MIPS_PCLO16:
@@ -386,54 +389,54 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16:
- return SignExtend64<16>(read32<E>(Buf));
+ return SignExtend64<16>(read32<e>(buf));
case R_MICROMIPS_GOT16:
case R_MICROMIPS_HI16:
- return SignExtend64<16>(readShuffle<E>(Buf)) << 16;
+ return SignExtend64<16>(readShuffle<e>(buf)) << 16;
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_LO16:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_TPREL_HI16:
case R_MICROMIPS_TLS_TPREL_LO16:
- return SignExtend64<16>(readShuffle<E>(Buf));
+ return SignExtend64<16>(readShuffle<e>(buf));
case R_MICROMIPS_GPREL7_S2:
- return SignExtend64<9>(readShuffle<E>(Buf) << 2);
+ return SignExtend64<9>(readShuffle<e>(buf) << 2);
case R_MIPS_PC16:
- return SignExtend64<18>(read32<E>(Buf) << 2);
+ return SignExtend64<18>(read32<e>(buf) << 2);
case R_MIPS_PC19_S2:
- return SignExtend64<21>(read32<E>(Buf) << 2);
+ return SignExtend64<21>(read32<e>(buf) << 2);
case R_MIPS_PC21_S2:
- return SignExtend64<23>(read32<E>(Buf) << 2);
+ return SignExtend64<23>(read32<e>(buf) << 2);
case R_MIPS_PC26_S2:
- return SignExtend64<28>(read32<E>(Buf) << 2);
+ return SignExtend64<28>(read32<e>(buf) << 2);
case R_MIPS_PC32:
- return SignExtend64<32>(read32<E>(Buf));
+ return SignExtend64<32>(read32<e>(buf));
case R_MICROMIPS_26_S1:
- return SignExtend64<27>(readShuffle<E>(Buf) << 1);
+ return SignExtend64<27>(readShuffle<e>(buf) << 1);
case R_MICROMIPS_PC7_S1:
- return SignExtend64<8>(read16<E>(Buf) << 1);
+ return SignExtend64<8>(read16<e>(buf) << 1);
case R_MICROMIPS_PC10_S1:
- return SignExtend64<11>(read16<E>(Buf) << 1);
+ return SignExtend64<11>(read16<e>(buf) << 1);
case R_MICROMIPS_PC16_S1:
- return SignExtend64<17>(readShuffle<E>(Buf) << 1);
+ return SignExtend64<17>(readShuffle<e>(buf) << 1);
case R_MICROMIPS_PC18_S3:
- return SignExtend64<21>(readShuffle<E>(Buf) << 3);
+ return SignExtend64<21>(readShuffle<e>(buf) << 3);
case R_MICROMIPS_PC19_S2:
- return SignExtend64<21>(readShuffle<E>(Buf) << 2);
+ return SignExtend64<21>(readShuffle<e>(buf) << 2);
case R_MICROMIPS_PC21_S1:
- return SignExtend64<22>(readShuffle<E>(Buf) << 1);
+ return SignExtend64<22>(readShuffle<e>(buf) << 1);
case R_MICROMIPS_PC23_S2:
- return SignExtend64<25>(readShuffle<E>(Buf) << 2);
+ return SignExtend64<25>(readShuffle<e>(buf) << 2);
case R_MICROMIPS_PC26_S1:
- return SignExtend64<27>(readShuffle<E>(Buf) << 1);
+ return SignExtend64<27>(readShuffle<e>(buf) << 1);
default:
return 0;
}
}
static std::pair<uint32_t, uint64_t>
-calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) {
+calculateMipsRelChain(uint8_t *loc, RelType type, uint64_t val) {
// MIPS N64 ABI packs multiple relocations into the single relocation
// record. In general, all up to three relocations can have arbitrary
// types. In fact, Clang and GCC uses only a few combinations. For now,
@@ -446,72 +449,134 @@ calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) {
// relocations used to modify result of the first one: extend it to
// 64-bit, extract high or low part etc. For details, see part 2.9 Relocation
// at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf
- RelType Type2 = (Type >> 8) & 0xff;
- RelType Type3 = (Type >> 16) & 0xff;
- if (Type2 == R_MIPS_NONE && Type3 == R_MIPS_NONE)
- return std::make_pair(Type, Val);
- if (Type2 == R_MIPS_64 && Type3 == R_MIPS_NONE)
- return std::make_pair(Type2, Val);
- if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16))
- return std::make_pair(Type3, -Val);
- error(getErrorLocation(Loc) + "unsupported relocations combination " +
- Twine(Type));
- return std::make_pair(Type & 0xff, Val);
+ RelType type2 = (type >> 8) & 0xff;
+ RelType type3 = (type >> 16) & 0xff;
+ if (type2 == R_MIPS_NONE && type3 == R_MIPS_NONE)
+ return std::make_pair(type, val);
+ if (type2 == R_MIPS_64 && type3 == R_MIPS_NONE)
+ return std::make_pair(type2, val);
+ if (type2 == R_MIPS_SUB && (type3 == R_MIPS_HI16 || type3 == R_MIPS_LO16))
+ return std::make_pair(type3, -val);
+ error(getErrorLocation(loc) + "unsupported relocations combination " +
+ Twine(type));
+ return std::make_pair(type & 0xff, val);
+}
+
+static bool isBranchReloc(RelType type) {
+ return type == R_MIPS_26 || type == R_MIPS_PC26_S2 ||
+ type == R_MIPS_PC21_S2 || type == R_MIPS_PC16;
+}
+
+static bool isMicroBranchReloc(RelType type) {
+ return type == R_MICROMIPS_26_S1 || type == R_MICROMIPS_PC16_S1 ||
+ type == R_MICROMIPS_PC10_S1 || type == R_MICROMIPS_PC7_S1;
}
template <class ELFT>
-void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- const endianness E = ELFT::TargetEndianness;
+static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) {
+ // Here we need to detect jump/branch from regular MIPS code
+ // to a microMIPS target and vice versa. In that cases jump
+ // instructions need to be replaced by their "cross-mode"
+ // equivalents.
+ const endianness e = ELFT::TargetEndianness;
+ bool isMicroTgt = val & 0x1;
+ bool isCrossJump = (isMicroTgt && isBranchReloc(type)) ||
+ (!isMicroTgt && isMicroBranchReloc(type));
+ if (!isCrossJump)
+ return val;
+
+ switch (type) {
+ case R_MIPS_26: {
+ uint32_t inst = read32<e>(loc) >> 26;
+ if (inst == 0x3 || inst == 0x1d) { // JAL or JALX
+ writeValue<e>(loc, 0x1d << 26, 32, 0);
+ return val;
+ }
+ break;
+ }
+ case R_MICROMIPS_26_S1: {
+ uint32_t inst = readShuffle<e>(loc) >> 26;
+ if (inst == 0x3d || inst == 0x3c) { // JAL32 or JALX32
+ val >>= 1;
+ writeShuffleValue<e>(loc, 0x3c << 26, 32, 0);
+ return val;
+ }
+ break;
+ }
+ case R_MIPS_PC26_S2:
+ case R_MIPS_PC21_S2:
+ case R_MIPS_PC16:
+ case R_MICROMIPS_PC16_S1:
+ case R_MICROMIPS_PC10_S1:
+ case R_MICROMIPS_PC7_S1:
+ // FIXME (simon): Support valid branch relocations.
+ break;
+ default:
+ llvm_unreachable("unexpected jump/branch relocation");
+ }
+
+ error(getErrorLocation(loc) +
+ "unsupported jump/branch instruction between ISA modes referenced by " +
+ toString(type) + " relocation");
+ return val;
+}
+
+template <class ELFT>
+void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ const endianness e = ELFT::TargetEndianness;
+
+ if (ELFT::Is64Bits || config->mipsN32Abi)
+ std::tie(type, val) = calculateMipsRelChain(loc, type, val);
- if (ELFT::Is64Bits || Config->MipsN32Abi)
- std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
+ // Detect cross-mode jump/branch and fix instruction.
+ val = fixupCrossModeJump<ELFT>(loc, type, val);
// Thread pointer and DRP offsets from the start of TLS data area.
// https://www.linux-mips.org/wiki/NPTL
- if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 ||
- Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64 ||
- Type == R_MICROMIPS_TLS_DTPREL_HI16 ||
- Type == R_MICROMIPS_TLS_DTPREL_LO16) {
- Val -= 0x8000;
- } else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 ||
- Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64 ||
- Type == R_MICROMIPS_TLS_TPREL_HI16 ||
- Type == R_MICROMIPS_TLS_TPREL_LO16) {
- Val -= 0x7000;
+ if (type == R_MIPS_TLS_DTPREL_HI16 || type == R_MIPS_TLS_DTPREL_LO16 ||
+ type == R_MIPS_TLS_DTPREL32 || type == R_MIPS_TLS_DTPREL64 ||
+ type == R_MICROMIPS_TLS_DTPREL_HI16 ||
+ type == R_MICROMIPS_TLS_DTPREL_LO16) {
+ val -= 0x8000;
+ } else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 ||
+ type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 ||
+ type == R_MICROMIPS_TLS_TPREL_HI16 ||
+ type == R_MICROMIPS_TLS_TPREL_LO16) {
+ val -= 0x7000;
}
- switch (Type) {
+ switch (type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_TPREL32:
- write32<E>(Loc, Val);
+ write32<e>(loc, val);
break;
case R_MIPS_64:
case R_MIPS_TLS_DTPREL64:
case R_MIPS_TLS_TPREL64:
- write64<E>(Loc, Val);
+ write64<e>(loc, val);
break;
case R_MIPS_26:
- writeValue<E>(Loc, Val, 26, 2);
+ writeValue<e>(loc, val, 26, 2);
break;
case R_MIPS_GOT16:
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
// is updated addend (not a GOT index). In that case write high 16 bits
// to store a correct addend value.
- if (Config->Relocatable) {
- writeValue<E>(Loc, Val + 0x8000, 16, 16);
+ if (config->relocatable) {
+ writeValue<e>(loc, val + 0x8000, 16, 16);
} else {
- checkInt(Loc, Val, 16, Type);
- writeValue<E>(Loc, Val, 16, 0);
+ checkInt(loc, val, 16, type);
+ writeValue<e>(loc, val, 16, 0);
}
break;
case R_MICROMIPS_GOT16:
- if (Config->Relocatable) {
- writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
+ if (config->relocatable) {
+ writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
} else {
- checkInt(Loc, Val, 16, Type);
- writeShuffleValue<E>(Loc, Val, 16, 0);
+ checkInt(loc, val, 16, type);
+ writeShuffleValue<e>(loc, val, 16, 0);
}
break;
case R_MIPS_CALL16:
@@ -521,7 +586,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MIPS_TLS_GD:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
- checkInt(Loc, Val, 16, Type);
+ checkInt(loc, val, 16, type);
LLVM_FALLTHROUGH;
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
@@ -530,13 +595,13 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MIPS_PCLO16:
case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_TPREL_LO16:
- writeValue<E>(Loc, Val, 16, 0);
+ writeValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_LDM:
- checkInt(Loc, Val, 16, Type);
- writeShuffleValue<E>(Loc, Val, 16, 0);
+ checkInt(loc, val, 16, type);
+ writeShuffleValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_CALL16:
case R_MICROMIPS_CALL_LO16:
@@ -544,11 +609,11 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_GOTTPREL:
case R_MICROMIPS_TLS_TPREL_LO16:
- writeShuffleValue<E>(Loc, Val, 16, 0);
+ writeShuffleValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_GPREL7_S2:
- checkInt(Loc, Val, 7, Type);
- writeShuffleValue<E>(Loc, Val, 7, 2);
+ checkInt(loc, val, 7, type);
+ writeShuffleValue<e>(loc, val, 7, 2);
break;
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_HI16:
@@ -556,113 +621,113 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MIPS_PCHI16:
case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_TPREL_HI16:
- writeValue<E>(Loc, Val + 0x8000, 16, 16);
+ writeValue<e>(loc, val + 0x8000, 16, 16);
break;
case R_MICROMIPS_CALL_HI16:
case R_MICROMIPS_GOT_HI16:
case R_MICROMIPS_HI16:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_TPREL_HI16:
- writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
+ writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
break;
case R_MIPS_HIGHER:
- writeValue<E>(Loc, Val + 0x80008000, 16, 32);
+ writeValue<e>(loc, val + 0x80008000, 16, 32);
break;
case R_MIPS_HIGHEST:
- writeValue<E>(Loc, Val + 0x800080008000, 16, 48);
+ writeValue<e>(loc, val + 0x800080008000, 16, 48);
break;
case R_MIPS_JALR:
case R_MICROMIPS_JALR:
// Ignore this optimization relocation for now
break;
case R_MIPS_PC16:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 18, Type);
- writeValue<E>(Loc, Val, 16, 2);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 18, type);
+ writeValue<e>(loc, val, 16, 2);
break;
case R_MIPS_PC19_S2:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 21, Type);
- writeValue<E>(Loc, Val, 19, 2);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 21, type);
+ writeValue<e>(loc, val, 19, 2);
break;
case R_MIPS_PC21_S2:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 23, Type);
- writeValue<E>(Loc, Val, 21, 2);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 23, type);
+ writeValue<e>(loc, val, 21, 2);
break;
case R_MIPS_PC26_S2:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 28, Type);
- writeValue<E>(Loc, Val, 26, 2);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 28, type);
+ writeValue<e>(loc, val, 26, 2);
break;
case R_MIPS_PC32:
- writeValue<E>(Loc, Val, 32, 0);
+ writeValue<e>(loc, val, 32, 0);
break;
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC26_S1:
- checkInt(Loc, Val, 27, Type);
- writeShuffleValue<E>(Loc, Val, 26, 1);
+ checkInt(loc, val, 27, type);
+ writeShuffleValue<e>(loc, val, 26, 1);
break;
case R_MICROMIPS_PC7_S1:
- checkInt(Loc, Val, 8, Type);
- writeMicroRelocation16<E>(Loc, Val, 7, 1);
+ checkInt(loc, val, 8, type);
+ writeMicroRelocation16<e>(loc, val, 7, 1);
break;
case R_MICROMIPS_PC10_S1:
- checkInt(Loc, Val, 11, Type);
- writeMicroRelocation16<E>(Loc, Val, 10, 1);
+ checkInt(loc, val, 11, type);
+ writeMicroRelocation16<e>(loc, val, 10, 1);
break;
case R_MICROMIPS_PC16_S1:
- checkInt(Loc, Val, 17, Type);
- writeShuffleValue<E>(Loc, Val, 16, 1);
+ checkInt(loc, val, 17, type);
+ writeShuffleValue<e>(loc, val, 16, 1);
break;
case R_MICROMIPS_PC18_S3:
- checkInt(Loc, Val, 21, Type);
- writeShuffleValue<E>(Loc, Val, 18, 3);
+ checkInt(loc, val, 21, type);
+ writeShuffleValue<e>(loc, val, 18, 3);
break;
case R_MICROMIPS_PC19_S2:
- checkInt(Loc, Val, 21, Type);
- writeShuffleValue<E>(Loc, Val, 19, 2);
+ checkInt(loc, val, 21, type);
+ writeShuffleValue<e>(loc, val, 19, 2);
break;
case R_MICROMIPS_PC21_S1:
- checkInt(Loc, Val, 22, Type);
- writeShuffleValue<E>(Loc, Val, 21, 1);
+ checkInt(loc, val, 22, type);
+ writeShuffleValue<e>(loc, val, 21, 1);
break;
case R_MICROMIPS_PC23_S2:
- checkInt(Loc, Val, 25, Type);
- writeShuffleValue<E>(Loc, Val, 23, 2);
+ checkInt(loc, val, 25, type);
+ writeShuffleValue<e>(loc, val, 23, 2);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType Type) const {
- return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST ||
- Type == R_MICROMIPS_LO16;
+template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType type) const {
+ return type == R_MIPS_LO16 || type == R_MIPS_GOT_OFST ||
+ type == R_MICROMIPS_LO16;
}
// Return true if the symbol is a PIC function.
-template <class ELFT> bool elf::isMipsPIC(const Defined *Sym) {
- if (!Sym->isFunc())
+template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
+ if (!sym->isFunc())
return false;
- if (Sym->StOther & STO_MIPS_PIC)
+ if (sym->stOther & STO_MIPS_PIC)
return true;
- if (!Sym->Section)
+ if (!sym->section)
return false;
- ObjFile<ELFT> *File =
- cast<InputSectionBase>(Sym->Section)->template getFile<ELFT>();
- if (!File)
+ ObjFile<ELFT> *file =
+ cast<InputSectionBase>(sym->section)->template getFile<ELFT>();
+ if (!file)
return false;
- return File->getObj().getHeader()->e_flags & EF_MIPS_PIC;
+ return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
}
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
- static MIPS<ELFT> Target;
- return &Target;
+ static MIPS<ELFT> target;
+ return &target;
}
template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
diff --git a/ELF/Arch/MipsArchTree.cpp b/ELF/Arch/MipsArchTree.cpp
index 98ceac3075e0c..f64d03756457d 100644
--- a/ELF/Arch/MipsArchTree.cpp
+++ b/ELF/Arch/MipsArchTree.cpp
@@ -1,9 +1,8 @@
//===- MipsArchTree.cpp --------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===---------------------------------------------------------------------===//
//
@@ -29,18 +28,18 @@ using namespace lld::elf;
namespace {
struct ArchTreeEdge {
- uint32_t Child;
- uint32_t Parent;
+ uint32_t child;
+ uint32_t parent;
};
struct FileFlags {
- InputFile *File;
- uint32_t Flags;
+ InputFile *file;
+ uint32_t flags;
};
} // namespace
-static StringRef getAbiName(uint32_t Flags) {
- switch (Flags) {
+static StringRef getAbiName(uint32_t flags) {
+ switch (flags) {
case 0:
return "n64";
case EF_MIPS_ABI2:
@@ -58,76 +57,76 @@ static StringRef getAbiName(uint32_t Flags) {
}
}
-static StringRef getNanName(bool IsNan2008) {
- return IsNan2008 ? "2008" : "legacy";
+static StringRef getNanName(bool isNan2008) {
+ return isNan2008 ? "2008" : "legacy";
}
-static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; }
+static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; }
-static void checkFlags(ArrayRef<FileFlags> Files) {
- assert(!Files.empty() && "expected non-empty file list");
+static void checkFlags(ArrayRef<FileFlags> files) {
+ assert(!files.empty() && "expected non-empty file list");
- uint32_t ABI = Files[0].Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
- bool Nan = Files[0].Flags & EF_MIPS_NAN2008;
- bool Fp = Files[0].Flags & EF_MIPS_FP64;
+ uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
+ bool nan = files[0].flags & EF_MIPS_NAN2008;
+ bool fp = files[0].flags & EF_MIPS_FP64;
- for (const FileFlags &F : Files) {
- if (Config->Is64 && F.Flags & EF_MIPS_MICROMIPS)
- error(toString(F.File) + ": microMIPS 64-bit is not supported");
+ for (const FileFlags &f : files) {
+ if (config->is64 && f.flags & EF_MIPS_MICROMIPS)
+ error(toString(f.file) + ": microMIPS 64-bit is not supported");
- uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
- if (ABI != ABI2)
- error(toString(F.File) + ": ABI '" + getAbiName(ABI2) +
- "' is incompatible with target ABI '" + getAbiName(ABI) + "'");
+ uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
+ if (abi != abi2)
+ error(toString(f.file) + ": ABI '" + getAbiName(abi2) +
+ "' is incompatible with target ABI '" + getAbiName(abi) + "'");
- bool Nan2 = F.Flags & EF_MIPS_NAN2008;
- if (Nan != Nan2)
- error(toString(F.File) + ": -mnan=" + getNanName(Nan2) +
- " is incompatible with target -mnan=" + getNanName(Nan));
+ bool nan2 = f.flags & EF_MIPS_NAN2008;
+ if (nan != nan2)
+ error(toString(f.file) + ": -mnan=" + getNanName(nan2) +
+ " is incompatible with target -mnan=" + getNanName(nan));
- bool Fp2 = F.Flags & EF_MIPS_FP64;
- if (Fp != Fp2)
- error(toString(F.File) + ": -mfp" + getFpName(Fp2) +
- " is incompatible with target -mfp" + getFpName(Fp));
+ bool fp2 = f.flags & EF_MIPS_FP64;
+ if (fp != fp2)
+ error(toString(f.file) + ": -mfp" + getFpName(fp2) +
+ " is incompatible with target -mfp" + getFpName(fp));
}
}
-static uint32_t getMiscFlags(ArrayRef<FileFlags> Files) {
- uint32_t Ret = 0;
- for (const FileFlags &F : Files)
- Ret |= F.Flags &
+static uint32_t getMiscFlags(ArrayRef<FileFlags> files) {
+ uint32_t ret = 0;
+ for (const FileFlags &f : files)
+ ret |= f.flags &
(EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER |
EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE);
- return Ret;
+ return ret;
}
-static uint32_t getPicFlags(ArrayRef<FileFlags> Files) {
+static uint32_t getPicFlags(ArrayRef<FileFlags> files) {
// Check PIC/non-PIC compatibility.
- bool IsPic = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
- for (const FileFlags &F : Files.slice(1)) {
- bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
- if (IsPic && !IsPic2)
- warn(toString(F.File) +
+ bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ for (const FileFlags &f : files.slice(1)) {
+ bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ if (isPic && !isPic2)
+ warn(toString(f.file) +
": linking non-abicalls code with abicalls code " +
- toString(Files[0].File));
- if (!IsPic && IsPic2)
- warn(toString(F.File) +
+ toString(files[0].file));
+ if (!isPic && isPic2)
+ warn(toString(f.file) +
": linking abicalls code with non-abicalls code " +
- toString(Files[0].File));
+ toString(files[0].file));
}
// Compute the result PIC/non-PIC flag.
- uint32_t Ret = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
- for (const FileFlags &F : Files.slice(1))
- Ret &= F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ for (const FileFlags &f : files.slice(1))
+ ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
// PIC code is inherently CPIC and may not set CPIC flag explicitly.
- if (Ret & EF_MIPS_PIC)
- Ret |= EF_MIPS_CPIC;
- return Ret;
+ if (ret & EF_MIPS_PIC)
+ ret |= EF_MIPS_CPIC;
+ return ret;
}
-static ArchTreeEdge ArchTree[] = {
+static ArchTreeEdge archTree[] = {
// MIPS32R6 and MIPS64R6 are not compatible with other extensions
// MIPS64R2 extensions.
{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2},
@@ -167,25 +166,25 @@ static ArchTreeEdge ArchTree[] = {
{EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
};
-static bool isArchMatched(uint32_t New, uint32_t Res) {
- if (New == Res)
+static bool isArchMatched(uint32_t New, uint32_t res) {
+ if (New == res)
return true;
- if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, Res))
+ if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res))
return true;
- if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, Res))
+ if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res))
return true;
- for (const auto &Edge : ArchTree) {
- if (Res == Edge.Child) {
- Res = Edge.Parent;
- if (Res == New)
+ for (const auto &edge : archTree) {
+ if (res == edge.child) {
+ res = edge.parent;
+ if (res == New)
return true;
}
}
return false;
}
-static StringRef getMachName(uint32_t Flags) {
- switch (Flags & EF_MIPS_MACH) {
+static StringRef getMachName(uint32_t flags) {
+ switch (flags & EF_MIPS_MACH) {
case EF_MIPS_MACH_NONE:
return "";
case EF_MIPS_MACH_3900:
@@ -229,8 +228,8 @@ static StringRef getMachName(uint32_t Flags) {
}
}
-static StringRef getArchName(uint32_t Flags) {
- switch (Flags & EF_MIPS_ARCH) {
+static StringRef getArchName(uint32_t flags) {
+ switch (flags & EF_MIPS_ARCH) {
case EF_MIPS_ARCH_1:
return "mips1";
case EF_MIPS_ARCH_2:
@@ -258,12 +257,12 @@ static StringRef getArchName(uint32_t Flags) {
}
}
-static std::string getFullArchName(uint32_t Flags) {
- StringRef Arch = getArchName(Flags);
- StringRef Mach = getMachName(Flags);
- if (Mach.empty())
- return Arch.str();
- return (Arch + " (" + Mach + ")").str();
+static std::string getFullArchName(uint32_t flags) {
+ StringRef arch = getArchName(flags);
+ StringRef mach = getMachName(flags);
+ if (mach.empty())
+ return arch.str();
+ return (arch + " (" + mach + ")").str();
}
// There are (arguably too) many MIPS ISAs out there. Their relationships
@@ -275,55 +274,55 @@ static std::string getFullArchName(uint32_t Flags) {
// Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32
// are incompatible because nor mips3 is a parent for misp32, nor mips32
// is a parent for mips3.
-static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
- uint32_t Ret = Files[0].Flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
+static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
+ uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
- for (const FileFlags &F : Files.slice(1)) {
- uint32_t New = F.Flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
+ for (const FileFlags &f : files.slice(1)) {
+ uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
// Check ISA compatibility.
- if (isArchMatched(New, Ret))
+ if (isArchMatched(New, ret))
continue;
- if (!isArchMatched(Ret, New)) {
- error("incompatible target ISA:\n>>> " + toString(Files[0].File) + ": " +
- getFullArchName(Ret) + "\n>>> " + toString(F.File) + ": " +
+ if (!isArchMatched(ret, New)) {
+ error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
+ getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
getFullArchName(New));
return 0;
}
- Ret = New;
+ ret = New;
}
- return Ret;
+ return ret;
}
template <class ELFT> uint32_t elf::calcMipsEFlags() {
- std::vector<FileFlags> V;
- for (InputFile *F : ObjectFiles)
- V.push_back({F, cast<ObjFile<ELFT>>(F)->getObj().getHeader()->e_flags});
- if (V.empty())
+ std::vector<FileFlags> v;
+ for (InputFile *f : objectFiles)
+ v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
+ if (v.empty())
return 0;
- checkFlags(V);
- return getMiscFlags(V) | getPicFlags(V) | getArchFlags(V);
+ checkFlags(v);
+ return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v);
}
-static int compareMipsFpAbi(uint8_t FpA, uint8_t FpB) {
- if (FpA == FpB)
+static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) {
+ if (fpA == fpB)
return 0;
- if (FpB == Mips::Val_GNU_MIPS_ABI_FP_ANY)
+ if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY)
return 1;
- if (FpB == Mips::Val_GNU_MIPS_ABI_FP_64A &&
- FpA == Mips::Val_GNU_MIPS_ABI_FP_64)
+ if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A &&
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64)
return 1;
- if (FpB != Mips::Val_GNU_MIPS_ABI_FP_XX)
+ if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX)
return -1;
- if (FpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
- FpA == Mips::Val_GNU_MIPS_ABI_FP_64 ||
- FpA == Mips::Val_GNU_MIPS_ABI_FP_64A)
+ if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64 ||
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64A)
return 1;
return -1;
}
-static StringRef getMipsFpAbiName(uint8_t FpAbi) {
- switch (FpAbi) {
+static StringRef getMipsFpAbiName(uint8_t fpAbi) {
+ switch (fpAbi) {
case Mips::Val_GNU_MIPS_ABI_FP_ANY:
return "any";
case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
@@ -345,43 +344,43 @@ static StringRef getMipsFpAbiName(uint8_t FpAbi) {
}
}
-uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
- StringRef FileName) {
- if (compareMipsFpAbi(NewFlag, OldFlag) >= 0)
- return NewFlag;
- if (compareMipsFpAbi(OldFlag, NewFlag) < 0)
- error(FileName + ": floating point ABI '" + getMipsFpAbiName(NewFlag) +
+uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
+ StringRef fileName) {
+ if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
+ return newFlag;
+ if (compareMipsFpAbi(oldFlag, newFlag) < 0)
+ error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) +
"' is incompatible with target floating point ABI '" +
- getMipsFpAbiName(OldFlag) + "'");
- return OldFlag;
+ getMipsFpAbiName(oldFlag) + "'");
+ return oldFlag;
}
-template <class ELFT> static bool isN32Abi(const InputFile *F) {
- if (auto *EF = dyn_cast<ELFFileBase<ELFT>>(F))
- return EF->getObj().getHeader()->e_flags & EF_MIPS_ABI2;
+template <class ELFT> static bool isN32Abi(const InputFile *f) {
+ if (auto *ef = dyn_cast<ELFFileBase>(f))
+ return ef->template getObj<ELFT>().getHeader()->e_flags & EF_MIPS_ABI2;
return false;
}
-bool elf::isMipsN32Abi(const InputFile *F) {
- switch (Config->EKind) {
+bool elf::isMipsN32Abi(const InputFile *f) {
+ switch (config->ekind) {
case ELF32LEKind:
- return isN32Abi<ELF32LE>(F);
+ return isN32Abi<ELF32LE>(f);
case ELF32BEKind:
- return isN32Abi<ELF32BE>(F);
+ return isN32Abi<ELF32BE>(f);
case ELF64LEKind:
- return isN32Abi<ELF64LE>(F);
+ return isN32Abi<ELF64LE>(f);
case ELF64BEKind:
- return isN32Abi<ELF64BE>(F);
+ return isN32Abi<ELF64BE>(f);
default:
llvm_unreachable("unknown Config->EKind");
}
}
-bool elf::isMicroMips() { return Config->EFlags & EF_MIPS_MICROMIPS; }
+bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
bool elf::isMipsR6() {
- uint32_t Arch = Config->EFlags & EF_MIPS_ARCH;
- return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
+ uint32_t arch = config->eflags & EF_MIPS_ARCH;
+ return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
}
template uint32_t elf::calcMipsEFlags<ELF32LE>();
diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp
index 7673780673413..46c5891e4f8a7 100644
--- a/ELF/Arch/PPC.cpp
+++ b/ELF/Arch/PPC.cpp
@@ -1,13 +1,14 @@
//===- PPC.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+#include "OutputSections.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"
@@ -22,60 +23,410 @@ namespace {
class PPC final : public TargetInfo {
public:
PPC();
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotHeader(uint8_t *buf) const override;
+ void writePltHeader(uint8_t *buf) const override {
+ llvm_unreachable("should call writePPC32GlinkSection() instead");
+ }
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override {
+ llvm_unreachable("should call writePPC32GlinkSection() instead");
+ }
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const override;
+ uint32_t getThunkSectionSpacing() const override;
+ bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const override;
+ int getTlsGdRelaxSkip(RelType type) const override;
+ void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
+static uint16_t lo(uint32_t v) { return v; }
+static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; }
+
+static uint32_t readFromHalf16(const uint8_t *loc) {
+ return read32(config->isLE ? loc : loc - 2);
+}
+
+static void writeFromHalf16(uint8_t *loc, uint32_t insn) {
+ write32(config->isLE ? loc : loc - 2, insn);
+}
+
+void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) {
+ // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an
+ // absolute address from a specific .plt slot (usually called .got.plt on
+ // other targets) and jumps there.
+ //
+ // a) With immediate binding (BIND_NOW), the .plt entry is resolved at load
+ // time. The .glink section is not used.
+ // b) With lazy binding, the .plt entry points to a `b PLTresolve`
+ // instruction in .glink, filled in by PPC::writeGotPlt().
+
+ // Write N `b PLTresolve` first.
+ for (size_t i = 0; i != numEntries; ++i)
+ write32(buf + 4 * i, 0x48000000 | 4 * (numEntries - i));
+ buf += 4 * numEntries;
+
+ // Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve()
+ // computes the PLT index (by computing the distance from the landing b to
+ // itself) and calls _dl_runtime_resolve() (in glibc).
+ uint32_t got = in.got->getVA();
+ uint32_t glink = in.plt->getVA(); // VA of .glink
+ const uint8_t *end = buf + 64;
+ if (config->isPic) {
+ uint32_t afterBcl = in.plt->getSize() - target->pltHeaderSize + 12;
+ uint32_t gotBcl = got + 4 - (glink + afterBcl);
+ write32(buf + 0, 0x3d6b0000 | ha(afterBcl)); // addis r11,r11,1f-glink@ha
+ write32(buf + 4, 0x7c0802a6); // mflr r0
+ write32(buf + 8, 0x429f0005); // bcl 20,30,.+4
+ write32(buf + 12, 0x396b0000 | lo(afterBcl)); // 1: addi r11,r11,1b-.glink@l
+ write32(buf + 16, 0x7d8802a6); // mflr r12
+ write32(buf + 20, 0x7c0803a6); // mtlr r0
+ write32(buf + 24, 0x7d6c5850); // sub r11,r11,r12
+ write32(buf + 28, 0x3d8c0000 | ha(gotBcl)); // addis 12,12,GOT+4-1b@ha
+ if (ha(gotBcl) == ha(gotBcl + 4)) {
+ write32(buf + 32, 0x800c0000 | lo(gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12)
+ write32(buf + 36,
+ 0x818c0000 | lo(gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12)
+ } else {
+ write32(buf + 32, 0x840c0000 | lo(gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12)
+ write32(buf + 36, 0x818c0000 | 4); // lwz r12,r12,4(r12)
+ }
+ write32(buf + 40, 0x7c0903a6); // mtctr 0
+ write32(buf + 44, 0x7c0b5a14); // add r0,11,11
+ write32(buf + 48, 0x7d605a14); // add r11,0,11
+ write32(buf + 52, 0x4e800420); // bctr
+ buf += 56;
+ } else {
+ write32(buf + 0, 0x3d800000 | ha(got + 4)); // lis r12,GOT+4@ha
+ write32(buf + 4, 0x3d6b0000 | ha(-glink)); // addis r11,r11,-Glink@ha
+ if (ha(got + 4) == ha(got + 8))
+ write32(buf + 8, 0x800c0000 | lo(got + 4)); // lwz r0,GOT+4@l(r12)
+ else
+ write32(buf + 8, 0x840c0000 | lo(got + 4)); // lwzu r0,GOT+4@l(r12)
+ write32(buf + 12, 0x396b0000 | lo(-glink)); // addi r11,r11,-Glink@l
+ write32(buf + 16, 0x7c0903a6); // mtctr r0
+ write32(buf + 20, 0x7c0b5a14); // add r0,r11,r11
+ if (ha(got + 4) == ha(got + 8))
+ write32(buf + 24, 0x818c0000 | lo(got + 8)); // lwz r12,GOT+8@ha(r12)
+ else
+ write32(buf + 24, 0x818c0000 | 4); // lwz r12,4(r12)
+ write32(buf + 28, 0x7d605a14); // add r11,r0,r11
+ write32(buf + 32, 0x4e800420); // bctr
+ buf += 36;
+ }
+
+ // Pad with nop. They should not be executed.
+ for (; buf < end; buf += 4)
+ write32(buf, 0x60000000);
+}
+
PPC::PPC() {
- NoneRel = R_PPC_NONE;
- GotBaseSymOff = 0x8000;
- GotBaseSymInGotPlt = false;
+ gotRel = R_PPC_GLOB_DAT;
+ noneRel = R_PPC_NONE;
+ pltRel = R_PPC_JMP_SLOT;
+ relativeRel = R_PPC_RELATIVE;
+ iRelativeRel = R_PPC_IRELATIVE;
+ symbolicRel = R_PPC_ADDR32;
+ gotBaseSymInGotPlt = false;
+ gotHeaderEntriesNum = 3;
+ gotPltHeaderEntriesNum = 0;
+ pltHeaderSize = 64; // size of PLTresolve in .glink
+ pltEntrySize = 4;
+
+ needsThunks = true;
+
+ tlsModuleIndexRel = R_PPC_DTPMOD32;
+ tlsOffsetRel = R_PPC_DTPREL32;
+ tlsGotRel = R_PPC_TPREL32;
+
+ defaultMaxPageSize = 65536;
+ defaultImageBase = 0x10000000;
+
+ write32(trapInstr.data(), 0x7fe00008);
+}
+
+void PPC::writeGotHeader(uint8_t *buf) const {
+ // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC
+ // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1],
+ // link_map in _GLOBAL_OFFSET_TABLE_[2].
+ write32(buf, mainPart->dynamic->getVA());
+}
+
+void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ // Address of the symbol resolver stub in .glink .
+ write32(buf, in.plt->getVA() + 4 * s.pltIndex);
+}
+
+bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
+ if (type != R_PPC_REL24 && type != R_PPC_PLTREL24)
+ return false;
+ if (s.isInPlt())
+ return true;
+ if (s.isUndefWeak())
+ return false;
+ return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA()));
}
-RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; }
+
+bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ uint64_t offset = dst - src;
+ if (type == R_PPC_REL24 || type == R_PPC_PLTREL24)
+ return isInt<26>(offset);
+ llvm_unreachable("unsupported relocation type used in branch");
+}
+
+RelExpr PPC::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
+ case R_PPC_DTPREL16:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_DTPREL16_LO:
+ case R_PPC_DTPREL32:
+ return R_DTPREL;
case R_PPC_REL14:
- case R_PPC_REL24:
case R_PPC_REL32:
+ case R_PPC_LOCAL24PC:
+ case R_PPC_REL16_LO:
+ case R_PPC_REL16_HI:
+ case R_PPC_REL16_HA:
return R_PC;
- case R_PPC_PLTREL24:
+ case R_PPC_GOT16:
+ return R_GOT_OFF;
+ case R_PPC_REL24:
return R_PLT_PC;
+ case R_PPC_PLTREL24:
+ return R_PPC32_PLTREL;
+ case R_PPC_GOT_TLSGD16:
+ return R_TLSGD_GOT;
+ case R_PPC_GOT_TLSLD16:
+ return R_TLSLD_GOT;
+ case R_PPC_GOT_TPREL16:
+ return R_GOT_OFF;
+ case R_PPC_TLS:
+ return R_TLSIE_HINT;
+ case R_PPC_TLSGD:
+ return R_TLSDESC_CALL;
+ case R_PPC_TLSLD:
+ return R_TLSLD_HINT;
+ case R_PPC_TPREL16:
+ case R_PPC_TPREL16_HA:
+ case R_PPC_TPREL16_LO:
+ case R_PPC_TPREL16_HI:
+ return R_TLS;
default:
return R_ABS;
}
}
-void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+RelType PPC::getDynRel(RelType type) const {
+ if (type == R_PPC_ADDR32)
+ return type;
+ return R_PPC_NONE;
+}
+
+static std::pair<RelType, uint64_t> fromDTPREL(RelType type, uint64_t val) {
+ uint64_t dtpBiasedVal = val - 0x8000;
+ switch (type) {
+ case R_PPC_DTPREL16:
+ return {R_PPC64_ADDR16, dtpBiasedVal};
+ case R_PPC_DTPREL16_HA:
+ return {R_PPC_ADDR16_HA, dtpBiasedVal};
+ case R_PPC_DTPREL16_HI:
+ return {R_PPC_ADDR16_HI, dtpBiasedVal};
+ case R_PPC_DTPREL16_LO:
+ return {R_PPC_ADDR16_LO, dtpBiasedVal};
+ case R_PPC_DTPREL32:
+ return {R_PPC_ADDR32, dtpBiasedVal};
+ default:
+ return {type, val};
+ }
+}
+
+void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ RelType newType;
+ std::tie(newType, val) = fromDTPREL(type, val);
+ switch (newType) {
+ case R_PPC_ADDR16:
+ checkIntUInt(loc, val, 16, type);
+ write16(loc, val);
+ break;
+ case R_PPC_GOT16:
+ case R_PPC_GOT_TLSGD16:
+ case R_PPC_GOT_TLSLD16:
+ case R_PPC_GOT_TPREL16:
+ case R_PPC_TPREL16:
+ checkInt(loc, val, 16, type);
+ write16(loc, val);
+ break;
case R_PPC_ADDR16_HA:
- write16be(Loc, (Val + 0x8000) >> 16);
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_GOT_TLSGD16_HA:
+ case R_PPC_GOT_TLSLD16_HA:
+ case R_PPC_GOT_TPREL16_HA:
+ case R_PPC_REL16_HA:
+ case R_PPC_TPREL16_HA:
+ write16(loc, ha(val));
break;
case R_PPC_ADDR16_HI:
- write16be(Loc, Val >> 16);
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_GOT_TLSGD16_HI:
+ case R_PPC_GOT_TLSLD16_HI:
+ case R_PPC_GOT_TPREL16_HI:
+ case R_PPC_REL16_HI:
+ case R_PPC_TPREL16_HI:
+ write16(loc, val >> 16);
break;
case R_PPC_ADDR16_LO:
- write16be(Loc, Val);
+ case R_PPC_DTPREL16_LO:
+ case R_PPC_GOT_TLSGD16_LO:
+ case R_PPC_GOT_TLSLD16_LO:
+ case R_PPC_GOT_TPREL16_LO:
+ case R_PPC_REL16_LO:
+ case R_PPC_TPREL16_LO:
+ write16(loc, val);
break;
case R_PPC_ADDR32:
case R_PPC_REL32:
- write32be(Loc, Val);
+ write32(loc, val);
break;
- case R_PPC_REL14:
- write32be(Loc, read32be(Loc) | (Val & 0xFFFC));
+ case R_PPC_REL14: {
+ uint32_t mask = 0x0000FFFC;
+ checkInt(loc, val, 16, type);
+ checkAlignment(loc, val, 4, type);
+ write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
- case R_PPC_PLTREL24:
+ }
case R_PPC_REL24:
- write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC));
+ case R_PPC_LOCAL24PC:
+ case R_PPC_PLTREL24: {
+ uint32_t mask = 0x03FFFFFC;
+ checkInt(loc, val, 26, type);
+ checkAlignment(loc, val, 4, type);
+ write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
+ }
+ default:
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
+ }
+}
+
+RelExpr PPC::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ if (expr == R_RELAX_TLS_GD_TO_IE)
+ return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
+ if (expr == R_RELAX_TLS_LD_TO_LE)
+ return R_RELAX_TLS_LD_TO_LE_ABS;
+ return expr;
+}
+
+int PPC::getTlsGdRelaxSkip(RelType type) const {
+ // A __tls_get_addr call instruction is marked with 2 relocations:
+ //
+ // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation
+ // R_PPC_REL24: __tls_get_addr
+ //
+ // After the relaxation we no longer call __tls_get_addr and should skip both
+ // relocations to not create a false dependence on __tls_get_addr being
+ // defined.
+ if (type == R_PPC_TLSGD || type == R_PPC_TLSLD)
+ return 2;
+ return 1;
+}
+
+void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
+ case R_PPC_GOT_TLSGD16: {
+ // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA)
+ uint32_t insn = readFromHalf16(loc);
+ writeFromHalf16(loc, 0x80000000 | (insn & 0x03ff0000));
+ relocateOne(loc, R_PPC_GOT_TPREL16, val);
+ break;
+ }
+ case R_PPC_TLSGD:
+ // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2
+ write32(loc, 0x7c631214);
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
+ }
+}
+
+void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
+ case R_PPC_GOT_TLSGD16:
+ // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha
+ writeFromHalf16(loc, 0x3c620000 | ha(val));
+ break;
+ case R_PPC_TLSGD:
+ // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l
+ write32(loc, 0x38630000 | lo(val));
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
+ }
+}
+
+void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
+ case R_PPC_GOT_TLSLD16:
+ // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0
+ writeFromHalf16(loc, 0x3c620000);
+ break;
+ case R_PPC_TLSLD:
+ // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel
+ // = r3+x-0x7000, so add 4096 to r3.
+ // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096
+ write32(loc, 0x38631000);
+ break;
+ case R_PPC_DTPREL16:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_DTPREL16_LO:
+ relocateOne(loc, type, val);
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
+ }
+}
+
+void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
+ case R_PPC_GOT_TPREL16: {
+ // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha
+ uint32_t rt = readFromHalf16(loc) & 0x03e00000;
+ writeFromHalf16(loc, 0x3c020000 | rt | ha(val));
+ break;
+ }
+ case R_PPC_TLS: {
+ uint32_t insn = read32(loc);
+ if (insn >> 26 != 31)
+ error("unrecognized instruction for IE to LE R_PPC_TLS");
+ // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l
+ uint32_t dFormOp = getPPCDFormOp((read32(loc) & 0x000007fe) >> 1);
+ if (dFormOp == 0)
+ error("unrecognized instruction for IE to LE R_PPC_TLS");
+ write32(loc, (dFormOp << 26) | (insn & 0x03ff0000) | lo(val));
+ break;
+ }
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unsupported relocation for TLS IE to LE relaxation");
}
}
TargetInfo *elf::getPPCTargetInfo() {
- static PPC Target;
- return &Target;
+ static PPC target;
+ return &target;
}
diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp
index 8a320c9a4e9ef..70d284cfad713 100644
--- a/ELF/Arch/PPC64.cpp
+++ b/ELF/Arch/PPC64.cpp
@@ -1,9 +1,8 @@
//===- PPC64.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -20,8 +19,8 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-static uint64_t PPC64TocOffset = 0x8000;
-static uint64_t DynamicThreadPointerOffset = 0x8000;
+static uint64_t ppc64TocOffset = 0x8000;
+static uint64_t dynamicThreadPointerOffset = 0x8000;
// The instruction encoding of bits 21-30 from the ISA for the Xform and Dform
// instructions that can be used as part of the initial exec TLS sequence.
@@ -65,16 +64,16 @@ uint64_t elf::getPPC64TocBase() {
// TOC starts where the first of these sections starts. We always create a
// .got when we see a relocation that uses it, so for us the start is always
// the .got.
- uint64_t TocVA = In.Got->getVA();
+ uint64_t tocVA = in.got->getVA();
// Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000
// thus permitting a full 64 Kbytes segment. Note that the glibc startup
// code (crt1.o) assumes that you can get from the TOC base to the
// start of the .toc section with only a single (signed) 16-bit relocation.
- return TocVA + PPC64TocOffset;
+ return tocVA + ppc64TocOffset;
}
-unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) {
+unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
// The offset is encoded into the 3 most significant bits of the st_other
// field, with some special values described in section 3.4.1 of the ABI:
// 0 --> Zero offset between the GEP and LEP, and the function does NOT use
@@ -86,43 +85,134 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) {
// 2 --> 2^2 = 4 bytes --> 1 instruction.
// 6 --> 2^6 = 64 bytes --> 16 instructions.
// 7 --> Reserved.
- uint8_t GepToLep = (StOther >> 5) & 7;
- if (GepToLep < 2)
+ uint8_t gepToLep = (stOther >> 5) & 7;
+ if (gepToLep < 2)
return 0;
// The value encoded in the st_other bits is the
// log-base-2(offset).
- if (GepToLep < 7)
- return 1 << GepToLep;
+ if (gepToLep < 7)
+ return 1 << gepToLep;
error("reserved value of 7 in the 3 most-significant-bits of st_other");
return 0;
}
+bool elf::isPPC64SmallCodeModelTocReloc(RelType type) {
+ // The only small code model relocations that access the .toc section.
+ return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
+}
+
+// Find the R_PPC64_ADDR64 in .rela.toc with matching offset.
+template <typename ELFT>
+static std::pair<Defined *, int64_t>
+getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) {
+ if (tocSec->numRelocations == 0)
+ return {};
+
+ // .rela.toc contains exclusively R_PPC64_ADDR64 relocations sorted by
+ // r_offset: 0, 8, 16, etc. For a given Offset, Offset / 8 gives us the
+ // relocation index in most cases.
+ //
+ // In rare cases a TOC entry may store a constant that doesn't need an
+ // R_PPC64_ADDR64, the corresponding r_offset is therefore missing. Offset / 8
+ // points to a relocation with larger r_offset. Do a linear probe then.
+ // Constants are extremely uncommon in .toc and the extra number of array
+ // accesses can be seen as a small constant.
+ ArrayRef<typename ELFT::Rela> relas = tocSec->template relas<ELFT>();
+ uint64_t index = std::min<uint64_t>(offset / 8, relas.size() - 1);
+ for (;;) {
+ if (relas[index].r_offset == offset) {
+ Symbol &sym = tocSec->getFile<ELFT>()->getRelocTargetSym(relas[index]);
+ return {dyn_cast<Defined>(&sym), getAddend<ELFT>(relas[index])};
+ }
+ if (relas[index].r_offset < offset || index == 0)
+ break;
+ --index;
+ }
+ return {};
+}
+
+// When accessing a symbol defined in another translation unit, compilers
+// reserve a .toc entry, allocate a local label and generate toc-indirect
+// instuctions:
+//
+// addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA
+// ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry
+// ld/lwa 3, 0(3) # load the value from the address
+//
+// .section .toc,"aw",@progbits
+// .LC0: .tc var[TC],var
+//
+// If var is defined, non-preemptable and addressable with a 32-bit signed
+// offset from the toc base, the address of var can be computed by adding an
+// offset to the toc base, saving a load.
+//
+// addis 3,2,var@toc@ha # this may be relaxed to a nop,
+// addi 3,3,var@toc@l # then this becomes addi 3,2,var@toc
+// ld/lwa 3, 0(3) # load the value from the address
+//
+// Returns true if the relaxation is performed.
+bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
+ uint8_t *bufLoc) {
+ assert(config->tocOptimize);
+ if (rel.addend < 0)
+ return false;
+
+ // If the symbol is not the .toc section, this isn't a toc-indirection.
+ Defined *defSym = dyn_cast<Defined>(rel.sym);
+ if (!defSym || !defSym->isSection() || defSym->section->name != ".toc")
+ return false;
+
+ Defined *d;
+ int64_t addend;
+ auto *tocISB = cast<InputSectionBase>(defSym->section);
+ std::tie(d, addend) =
+ config->isLE ? getRelaTocSymAndAddend<ELF64LE>(tocISB, rel.addend)
+ : getRelaTocSymAndAddend<ELF64BE>(tocISB, rel.addend);
+
+ // Only non-preemptable defined symbols can be relaxed.
+ if (!d || d->isPreemptible)
+ return false;
+
+ // Two instructions can materialize a 32-bit signed offset from the toc base.
+ uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase();
+ if (!isInt<32>(tocRelative))
+ return false;
+
+ // Add PPC64TocOffset that will be subtracted by relocateOne().
+ target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset);
+ return true;
+}
+
namespace {
class PPC64 final : public TargetInfo {
public:
PPC64();
+ int getTlsGdRelaxSkip(RelType type) const override;
uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void writeGotHeader(uint8_t *Buf) const override;
- bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const override;
- bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
- RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const override;
- void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
-
- bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void writeGotHeader(uint8_t *buf) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const override;
+ uint32_t getThunkSectionSpacing() const override;
+ bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
+ RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const override;
+ void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+
+ bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const override;
};
} // namespace
@@ -130,19 +220,19 @@ public:
// #higher(value), #highera(value), #highest(value), and #highesta(value)
// macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi
// document.
-static uint16_t lo(uint64_t V) { return V; }
-static uint16_t hi(uint64_t V) { return V >> 16; }
-static uint16_t ha(uint64_t V) { return (V + 0x8000) >> 16; }
-static uint16_t higher(uint64_t V) { return V >> 32; }
-static uint16_t highera(uint64_t V) { return (V + 0x8000) >> 32; }
-static uint16_t highest(uint64_t V) { return V >> 48; }
-static uint16_t highesta(uint64_t V) { return (V + 0x8000) >> 48; }
+static uint16_t lo(uint64_t v) { return v; }
+static uint16_t hi(uint64_t v) { return v >> 16; }
+static uint16_t ha(uint64_t v) { return (v + 0x8000) >> 16; }
+static uint16_t higher(uint64_t v) { return v >> 32; }
+static uint16_t highera(uint64_t v) { return (v + 0x8000) >> 32; }
+static uint16_t highest(uint64_t v) { return v >> 48; }
+static uint16_t highesta(uint64_t v) { return (v + 0x8000) >> 48; }
// Extracts the 'PO' field of an instruction encoding.
-static uint8_t getPrimaryOpCode(uint32_t Encoding) { return (Encoding >> 26); }
+static uint8_t getPrimaryOpCode(uint32_t encoding) { return (encoding >> 26); }
-static bool isDQFormInstruction(uint32_t Encoding) {
- switch (getPrimaryOpCode(Encoding)) {
+static bool isDQFormInstruction(uint32_t encoding) {
+ switch (getPrimaryOpCode(encoding)) {
default:
return false;
case 56:
@@ -152,12 +242,12 @@ static bool isDQFormInstruction(uint32_t Encoding) {
// There are both DS and DQ instruction forms with this primary opcode.
// Namely `lxv` and `stxv` are the DQ-forms that use it.
// The DS 'XO' bits being set to 01 is restricted to DQ form.
- return (Encoding & 3) == 0x1;
+ return (encoding & 3) == 0x1;
}
}
-static bool isInstructionUpdateForm(uint32_t Encoding) {
- switch (getPrimaryOpCode(Encoding)) {
+static bool isInstructionUpdateForm(uint32_t encoding) {
+ switch (getPrimaryOpCode(encoding)) {
default:
return false;
case LBZU:
@@ -176,7 +266,7 @@ static bool isInstructionUpdateForm(uint32_t Encoding) {
// between LD/LDU/LWA
case LD:
case STD:
- return (Encoding & 3) == 1;
+ return (encoding & 3) == 1;
}
}
@@ -185,40 +275,38 @@ static bool isInstructionUpdateForm(uint32_t Encoding) {
// pointer is pointing into the middle of the word we want to extract, and on
// little-endian it is pointing to the start of the word. These 2 helpers are to
// simplify reading and writing in that context.
-static void writeInstrFromHalf16(uint8_t *Loc, uint32_t Instr) {
- write32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0), Instr);
+static void writeFromHalf16(uint8_t *loc, uint32_t insn) {
+ write32(config->isLE ? loc : loc - 2, insn);
}
-static uint32_t readInstrFromHalf16(const uint8_t *Loc) {
- return read32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0));
+static uint32_t readFromHalf16(const uint8_t *loc) {
+ return read32(config->isLE ? loc : loc - 2);
}
PPC64::PPC64() {
- GotRel = R_PPC64_GLOB_DAT;
- NoneRel = R_PPC64_NONE;
- PltRel = R_PPC64_JMP_SLOT;
- RelativeRel = R_PPC64_RELATIVE;
- IRelativeRel = R_PPC64_IRELATIVE;
- GotEntrySize = 8;
- PltEntrySize = 4;
- GotPltEntrySize = 8;
- GotBaseSymInGotPlt = false;
- GotBaseSymOff = 0x8000;
- GotHeaderEntriesNum = 1;
- GotPltHeaderEntriesNum = 2;
- PltHeaderSize = 60;
- NeedsThunks = true;
-
- TlsModuleIndexRel = R_PPC64_DTPMOD64;
- TlsOffsetRel = R_PPC64_DTPREL64;
-
- TlsGotRel = R_PPC64_TPREL64;
-
- NeedsMoreStackNonSplit = false;
+ gotRel = R_PPC64_GLOB_DAT;
+ noneRel = R_PPC64_NONE;
+ pltRel = R_PPC64_JMP_SLOT;
+ relativeRel = R_PPC64_RELATIVE;
+ iRelativeRel = R_PPC64_IRELATIVE;
+ symbolicRel = R_PPC64_ADDR64;
+ pltEntrySize = 4;
+ gotBaseSymInGotPlt = false;
+ gotHeaderEntriesNum = 1;
+ gotPltHeaderEntriesNum = 2;
+ pltHeaderSize = 60;
+ needsThunks = true;
+
+ tlsModuleIndexRel = R_PPC64_DTPMOD64;
+ tlsOffsetRel = R_PPC64_DTPREL64;
+
+ tlsGotRel = R_PPC64_TPREL64;
+
+ needsMoreStackNonSplit = false;
// We need 64K pages (at least under glibc/Linux, the loader won't
// set different permissions on a finer granularity than that).
- DefaultMaxPageSize = 65536;
+ defaultMaxPageSize = 65536;
// The PPC64 ELF ABI v1 spec, says:
//
@@ -228,31 +316,66 @@ PPC64::PPC64() {
//
// And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers
// use 0x10000000 as the starting address.
- DefaultImageBase = 0x10000000;
+ defaultImageBase = 0x10000000;
- write32(TrapInstr.data(), 0x7fe00008);
+ write32(trapInstr.data(), 0x7fe00008);
+}
+
+int PPC64::getTlsGdRelaxSkip(RelType type) const {
+ // A __tls_get_addr call instruction is marked with 2 relocations:
+ //
+ // R_PPC64_TLSGD / R_PPC64_TLSLD: marker relocation
+ // R_PPC64_REL24: __tls_get_addr
+ //
+ // After the relaxation we no longer call __tls_get_addr and should skip both
+ // relocations to not create a false dependence on __tls_get_addr being
+ // defined.
+ if (type == R_PPC64_TLSGD || type == R_PPC64_TLSLD)
+ return 2;
+ return 1;
}
-static uint32_t getEFlags(InputFile *File) {
- if (Config->EKind == ELF64BEKind)
- return cast<ObjFile<ELF64BE>>(File)->getObj().getHeader()->e_flags;
- return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags;
+static uint32_t getEFlags(InputFile *file) {
+ if (config->ekind == ELF64BEKind)
+ return cast<ObjFile<ELF64BE>>(file)->getObj().getHeader()->e_flags;
+ return cast<ObjFile<ELF64LE>>(file)->getObj().getHeader()->e_flags;
}
// This file implements v2 ABI. This function makes sure that all
// object files have v2 or an unspecified version as an ABI version.
uint32_t PPC64::calcEFlags() const {
- for (InputFile *F : ObjectFiles) {
- uint32_t Flag = getEFlags(F);
- if (Flag == 1)
- error(toString(F) + ": ABI version 1 is not supported");
- else if (Flag > 2)
- error(toString(F) + ": unrecognized e_flags: " + Twine(Flag));
+ for (InputFile *f : objectFiles) {
+ uint32_t flag = getEFlags(f);
+ if (flag == 1)
+ error(toString(f) + ": ABI version 1 is not supported");
+ else if (flag > 2)
+ error(toString(f) + ": unrecognized e_flags: " + Twine(flag));
}
return 2;
}
-void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
+ case R_PPC64_TOC16_HA:
+ // Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop".
+ relocateOne(loc, type, val);
+ break;
+ case R_PPC64_TOC16_LO_DS: {
+ // Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or
+ // "addi reg, 2, var@toc".
+ uint32_t insn = readFromHalf16(loc);
+ if (getPrimaryOpCode(insn) != LD)
+ error("expected a 'ld' for got-indirect to toc-relative relaxing");
+ writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000);
+ relocateOne(loc, R_PPC64_TOC16_LO, val);
+ break;
+ }
+ default:
+ llvm_unreachable("unexpected relocation type");
+ }
+}
+
+void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement.
// The general dynamic code sequence for a global `x` will look like:
// Instruction Relocation Symbol
@@ -268,30 +391,30 @@ void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, x@tprel@l
- switch (Type) {
+ switch (type) {
case R_PPC64_GOT_TLSGD16_HA:
- writeInstrFromHalf16(Loc, 0x60000000); // nop
+ writeFromHalf16(loc, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO:
- writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13
- relocateOne(Loc, R_PPC64_TPREL16_HA, Val);
+ writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13
+ relocateOne(loc, R_PPC64_TPREL16_HA, val);
break;
case R_PPC64_TLSGD:
- write32(Loc, 0x60000000); // nop
- write32(Loc + 4, 0x38630000); // addi r3, r3
+ write32(loc, 0x60000000); // nop
+ write32(loc + 4, 0x38630000); // addi r3, r3
// Since we are relocating a half16 type relocation and Loc + 4 points to
// the start of an instruction we need to advance the buffer by an extra
// 2 bytes on BE.
- relocateOne(Loc + 4 + (Config->EKind == ELF64BEKind ? 2 : 0),
- R_PPC64_TPREL16_LO, Val);
+ relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0),
+ R_PPC64_TPREL16_LO, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement.
// The local dynamic code sequence for a global `x` will look like:
// Instruction Relocation Symbol
@@ -307,16 +430,16 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, 4096
- switch (Type) {
+ switch (type) {
case R_PPC64_GOT_TLSLD16_HA:
- writeInstrFromHalf16(Loc, 0x60000000); // nop
+ writeFromHalf16(loc, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSLD16_LO:
- writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13, 0
+ writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13, 0
break;
case R_PPC64_TLSLD:
- write32(Loc, 0x60000000); // nop
- write32(Loc + 4, 0x38631000); // addi r3, r3, 4096
+ write32(loc, 0x60000000); // nop
+ write32(loc + 4, 0x38631000); // addi r3, r3, 4096
break;
case R_PPC64_DTPREL16:
case R_PPC64_DTPREL16_HA:
@@ -324,19 +447,15 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_PPC64_DTPREL16_DS:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
- case R_PPC64_GOT_DTPREL16_HA:
- case R_PPC64_GOT_DTPREL16_LO_DS:
- case R_PPC64_GOT_DTPREL16_DS:
- case R_PPC64_GOT_DTPREL16_HI:
- relocateOne(Loc, Type, Val);
+ relocateOne(loc, type, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
}
}
-static unsigned getDFormOp(unsigned SecondaryOp) {
- switch (SecondaryOp) {
+unsigned elf::getPPCDFormOp(unsigned secondaryOp) {
+ switch (secondaryOp) {
case LBZX:
return LBZ;
case LHZX:
@@ -356,12 +475,11 @@ static unsigned getDFormOp(unsigned SecondaryOp) {
case ADD:
return ADDI;
default:
- error("unrecognized instruction for IE to LE R_PPC64_TLS");
return 0;
}
}
-void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
// The initial exec code sequence for a global `x` will look like:
// Instruction Relocation Symbol
// addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x
@@ -381,26 +499,28 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// instruction, if we are accessing memory it will use any of the X-form
// indexed load or store instructions.
- unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0;
- switch (Type) {
+ unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0;
+ switch (type) {
case R_PPC64_GOT_TPREL16_HA:
- write32(Loc - Offset, 0x60000000); // nop
+ write32(loc - offset, 0x60000000); // nop
break;
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_TPREL16_DS: {
- uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10
- write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13
- relocateOne(Loc, R_PPC64_TPREL16_HA, Val);
+ uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10
+ write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13
+ relocateOne(loc, R_PPC64_TPREL16_HA, val);
break;
}
case R_PPC64_TLS: {
- uint32_t PrimaryOp = getPrimaryOpCode(read32(Loc));
- if (PrimaryOp != 31)
+ uint32_t primaryOp = getPrimaryOpCode(read32(loc));
+ if (primaryOp != 31)
error("unrecognized instruction for IE to LE R_PPC64_TLS");
- uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30
- uint32_t DFormOp = getDFormOp(SecondaryOp);
- write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF)));
- relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val);
+ uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30
+ uint32_t dFormOp = getPPCDFormOp(secondaryOp);
+ if (dFormOp == 0)
+ error("unrecognized instruction for IE to LE R_PPC64_TLS");
+ write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF)));
+ relocateOne(loc + offset, R_PPC64_TPREL16_LO, val);
break;
}
default:
@@ -409,9 +529,9 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
}
-RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_PPC64_GOT16:
case R_PPC64_GOT16_DS:
case R_PPC64_GOT16_HA:
@@ -421,16 +541,17 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
return R_GOT_OFF;
case R_PPC64_TOC16:
case R_PPC64_TOC16_DS:
- case R_PPC64_TOC16_HA:
case R_PPC64_TOC16_HI:
case R_PPC64_TOC16_LO:
- case R_PPC64_TOC16_LO_DS:
return R_GOTREL;
+ case R_PPC64_TOC16_HA:
+ case R_PPC64_TOC16_LO_DS:
+ return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL;
case R_PPC64_TOC:
- return R_PPC_TOC;
+ return R_PPC64_TOCBASE;
case R_PPC64_REL14:
case R_PPC64_REL24:
- return R_PPC_CALL_PLT;
+ return R_PPC64_CALL_PLT;
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HA:
case R_PPC64_REL32:
@@ -478,7 +599,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
case R_PPC64_DTPREL64:
- return R_ABS;
+ return R_DTPREL;
case R_PPC64_TLSGD:
return R_TLSDESC_CALL;
case R_PPC64_TLSLD:
@@ -490,115 +611,121 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
}
}
-void PPC64::writeGotHeader(uint8_t *Buf) const {
- write64(Buf, getPPC64TocBase());
+RelType PPC64::getDynRel(RelType type) const {
+ if (type == R_PPC64_ADDR64 || type == R_PPC64_TOC)
+ return R_PPC64_ADDR64;
+ return R_PPC64_NONE;
}
-void PPC64::writePltHeader(uint8_t *Buf) const {
+void PPC64::writeGotHeader(uint8_t *buf) const {
+ write64(buf, getPPC64TocBase());
+}
+
+void PPC64::writePltHeader(uint8_t *buf) const {
// The generic resolver stub goes first.
- write32(Buf + 0, 0x7c0802a6); // mflr r0
- write32(Buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8>
- write32(Buf + 8, 0x7d6802a6); // mflr r11
- write32(Buf + 12, 0x7c0803a6); // mtlr r0
- write32(Buf + 16, 0x7d8b6050); // subf r12, r11, r12
- write32(Buf + 20, 0x380cffcc); // subi r0,r12,52
- write32(Buf + 24, 0x7800f082); // srdi r0,r0,62,2
- write32(Buf + 28, 0xe98b002c); // ld r12,44(r11)
- write32(Buf + 32, 0x7d6c5a14); // add r11,r12,r11
- write32(Buf + 36, 0xe98b0000); // ld r12,0(r11)
- write32(Buf + 40, 0xe96b0008); // ld r11,8(r11)
- write32(Buf + 44, 0x7d8903a6); // mtctr r12
- write32(Buf + 48, 0x4e800420); // bctr
+ write32(buf + 0, 0x7c0802a6); // mflr r0
+ write32(buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8>
+ write32(buf + 8, 0x7d6802a6); // mflr r11
+ write32(buf + 12, 0x7c0803a6); // mtlr r0
+ write32(buf + 16, 0x7d8b6050); // subf r12, r11, r12
+ write32(buf + 20, 0x380cffcc); // subi r0,r12,52
+ write32(buf + 24, 0x7800f082); // srdi r0,r0,62,2
+ write32(buf + 28, 0xe98b002c); // ld r12,44(r11)
+ write32(buf + 32, 0x7d6c5a14); // add r11,r12,r11
+ write32(buf + 36, 0xe98b0000); // ld r12,0(r11)
+ write32(buf + 40, 0xe96b0008); // ld r11,8(r11)
+ write32(buf + 44, 0x7d8903a6); // mtctr r12
+ write32(buf + 48, 0x4e800420); // bctr
// The 'bcl' instruction will set the link register to the address of the
// following instruction ('mflr r11'). Here we store the offset from that
// instruction to the first entry in the GotPlt section.
- int64_t GotPltOffset = In.GotPlt->getVA() - (In.Plt->getVA() + 8);
- write64(Buf + 52, GotPltOffset);
+ int64_t gotPltOffset = in.gotPlt->getVA() - (in.plt->getVA() + 8);
+ write64(buf + 52, gotPltOffset);
}
-void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- int32_t Offset = PltHeaderSize + Index * PltEntrySize;
+void PPC64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ int32_t offset = pltHeaderSize + index * pltEntrySize;
// bl __glink_PLTresolve
- write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc));
+ write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc));
}
-static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
+static std::pair<RelType, uint64_t> toAddr16Rel(RelType type, uint64_t val) {
// Relocations relative to the toc-base need to be adjusted by the Toc offset.
- uint64_t TocBiasedVal = Val - PPC64TocOffset;
+ uint64_t tocBiasedVal = val - ppc64TocOffset;
// Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset.
- uint64_t DTPBiasedVal = Val - DynamicThreadPointerOffset;
+ uint64_t dtpBiasedVal = val - dynamicThreadPointerOffset;
- switch (Type) {
+ switch (type) {
// TOC biased relocation.
case R_PPC64_GOT16:
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSLD16:
case R_PPC64_TOC16:
- return {R_PPC64_ADDR16, TocBiasedVal};
+ return {R_PPC64_ADDR16, tocBiasedVal};
case R_PPC64_GOT16_DS:
case R_PPC64_TOC16_DS:
case R_PPC64_GOT_TPREL16_DS:
case R_PPC64_GOT_DTPREL16_DS:
- return {R_PPC64_ADDR16_DS, TocBiasedVal};
+ return {R_PPC64_ADDR16_DS, tocBiasedVal};
case R_PPC64_GOT16_HA:
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TPREL16_HA:
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_TOC16_HA:
- return {R_PPC64_ADDR16_HA, TocBiasedVal};
+ return {R_PPC64_ADDR16_HA, tocBiasedVal};
case R_PPC64_GOT16_HI:
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_DTPREL16_HI:
case R_PPC64_TOC16_HI:
- return {R_PPC64_ADDR16_HI, TocBiasedVal};
+ return {R_PPC64_ADDR16_HI, tocBiasedVal};
case R_PPC64_GOT16_LO:
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_TOC16_LO:
- return {R_PPC64_ADDR16_LO, TocBiasedVal};
+ return {R_PPC64_ADDR16_LO, tocBiasedVal};
case R_PPC64_GOT16_LO_DS:
case R_PPC64_TOC16_LO_DS:
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_DTPREL16_LO_DS:
- return {R_PPC64_ADDR16_LO_DS, TocBiasedVal};
+ return {R_PPC64_ADDR16_LO_DS, tocBiasedVal};
// Dynamic Thread pointer biased relocation types.
case R_PPC64_DTPREL16:
- return {R_PPC64_ADDR16, DTPBiasedVal};
+ return {R_PPC64_ADDR16, dtpBiasedVal};
case R_PPC64_DTPREL16_DS:
- return {R_PPC64_ADDR16_DS, DTPBiasedVal};
+ return {R_PPC64_ADDR16_DS, dtpBiasedVal};
case R_PPC64_DTPREL16_HA:
- return {R_PPC64_ADDR16_HA, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HA, dtpBiasedVal};
case R_PPC64_DTPREL16_HI:
- return {R_PPC64_ADDR16_HI, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HI, dtpBiasedVal};
case R_PPC64_DTPREL16_HIGHER:
- return {R_PPC64_ADDR16_HIGHER, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HIGHER, dtpBiasedVal};
case R_PPC64_DTPREL16_HIGHERA:
- return {R_PPC64_ADDR16_HIGHERA, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HIGHERA, dtpBiasedVal};
case R_PPC64_DTPREL16_HIGHEST:
- return {R_PPC64_ADDR16_HIGHEST, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HIGHEST, dtpBiasedVal};
case R_PPC64_DTPREL16_HIGHESTA:
- return {R_PPC64_ADDR16_HIGHESTA, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HIGHESTA, dtpBiasedVal};
case R_PPC64_DTPREL16_LO:
- return {R_PPC64_ADDR16_LO, DTPBiasedVal};
+ return {R_PPC64_ADDR16_LO, dtpBiasedVal};
case R_PPC64_DTPREL16_LO_DS:
- return {R_PPC64_ADDR16_LO_DS, DTPBiasedVal};
+ return {R_PPC64_ADDR16_LO_DS, dtpBiasedVal};
case R_PPC64_DTPREL64:
- return {R_PPC64_ADDR64, DTPBiasedVal};
+ return {R_PPC64_ADDR64, dtpBiasedVal};
default:
- return {Type, Val};
+ return {type, val};
}
}
-static bool isTocOptType(RelType Type) {
- switch (Type) {
+static bool isTocOptType(RelType type) {
+ switch (type) {
case R_PPC64_GOT16_HA:
case R_PPC64_GOT16_LO_DS:
case R_PPC64_TOC16_HA:
@@ -610,66 +737,69 @@ static bool isTocOptType(RelType Type) {
}
}
-void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
// We need to save the original relocation type to use in diagnostics, and
// use the original type to determine if we should toc-optimize the
// instructions being relocated.
- RelType OriginalType = Type;
- bool ShouldTocOptimize = isTocOptType(Type);
+ RelType originalType = type;
+ bool shouldTocOptimize = isTocOptType(type);
// For dynamic thread pointer relative, toc-relative, and got-indirect
// relocations, proceed in terms of the corresponding ADDR16 relocation type.
- std::tie(Type, Val) = toAddr16Rel(Type, Val);
+ std::tie(type, val) = toAddr16Rel(type, val);
- switch (Type) {
+ switch (type) {
case R_PPC64_ADDR14: {
- checkAlignment(Loc, Val, 4, Type);
+ checkAlignment(loc, val, 4, type);
// Preserve the AA/LK bits in the branch instruction
- uint8_t AALK = Loc[3];
- write16(Loc + 2, (AALK & 3) | (Val & 0xfffc));
+ uint8_t aalk = loc[3];
+ write16(loc + 2, (aalk & 3) | (val & 0xfffc));
break;
}
case R_PPC64_ADDR16:
- case R_PPC64_TPREL16:
- checkInt(Loc, Val, 16, OriginalType);
- write16(Loc, Val);
+ checkIntUInt(loc, val, 16, originalType);
+ write16(loc, val);
+ break;
+ case R_PPC64_ADDR32:
+ checkIntUInt(loc, val, 32, originalType);
+ write32(loc, val);
break;
case R_PPC64_ADDR16_DS:
case R_PPC64_TPREL16_DS: {
- checkInt(Loc, Val, 16, OriginalType);
+ checkInt(loc, val, 16, originalType);
// DQ-form instructions use bits 28-31 as part of the instruction encoding
// DS-form instructions only use bits 30-31.
- uint16_t Mask = isDQFormInstruction(readInstrFromHalf16(Loc)) ? 0xF : 0x3;
- checkAlignment(Loc, lo(Val), Mask + 1, OriginalType);
- write16(Loc, (read16(Loc) & Mask) | lo(Val));
+ uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3;
+ checkAlignment(loc, lo(val), mask + 1, originalType);
+ write16(loc, (read16(loc) & mask) | lo(val));
} break;
case R_PPC64_ADDR16_HA:
case R_PPC64_REL16_HA:
case R_PPC64_TPREL16_HA:
- if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0)
- writeInstrFromHalf16(Loc, 0x60000000);
+ if (config->tocOptimize && shouldTocOptimize && ha(val) == 0)
+ writeFromHalf16(loc, 0x60000000);
else
- write16(Loc, ha(Val));
+ write16(loc, ha(val));
break;
case R_PPC64_ADDR16_HI:
case R_PPC64_REL16_HI:
case R_PPC64_TPREL16_HI:
- write16(Loc, hi(Val));
+ write16(loc, hi(val));
break;
case R_PPC64_ADDR16_HIGHER:
case R_PPC64_TPREL16_HIGHER:
- write16(Loc, higher(Val));
+ write16(loc, higher(val));
break;
case R_PPC64_ADDR16_HIGHERA:
case R_PPC64_TPREL16_HIGHERA:
- write16(Loc, highera(Val));
+ write16(loc, highera(val));
break;
case R_PPC64_ADDR16_HIGHEST:
case R_PPC64_TPREL16_HIGHEST:
- write16(Loc, highest(Val));
+ write16(loc, highest(val));
break;
case R_PPC64_ADDR16_HIGHESTA:
case R_PPC64_TPREL16_HIGHESTA:
- write16(Loc, highesta(Val));
+ write16(loc, highesta(val));
break;
case R_PPC64_ADDR16_LO:
case R_PPC64_REL16_LO:
@@ -677,104 +807,119 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// When the high-adjusted part of a toc relocation evalutes to 0, it is
// changed into a nop. The lo part then needs to be updated to use the
// toc-pointer register r2, as the base register.
- if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) {
- uint32_t Instr = readInstrFromHalf16(Loc);
- if (isInstructionUpdateForm(Instr))
- error(getErrorLocation(Loc) +
+ if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) {
+ uint32_t insn = readFromHalf16(loc);
+ if (isInstructionUpdateForm(insn))
+ error(getErrorLocation(loc) +
"can't toc-optimize an update instruction: 0x" +
- utohexstr(Instr));
- Instr = (Instr & 0xFFE00000) | 0x00020000;
- writeInstrFromHalf16(Loc, Instr);
+ utohexstr(insn));
+ writeFromHalf16(loc, (insn & 0xffe00000) | 0x00020000 | lo(val));
+ } else {
+ write16(loc, lo(val));
}
- write16(Loc, lo(Val));
break;
case R_PPC64_ADDR16_LO_DS:
case R_PPC64_TPREL16_LO_DS: {
// DQ-form instructions use bits 28-31 as part of the instruction encoding
// DS-form instructions only use bits 30-31.
- uint32_t Inst = readInstrFromHalf16(Loc);
- uint16_t Mask = isDQFormInstruction(Inst) ? 0xF : 0x3;
- checkAlignment(Loc, lo(Val), Mask + 1, OriginalType);
- if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) {
+ uint32_t insn = readFromHalf16(loc);
+ uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3;
+ checkAlignment(loc, lo(val), mask + 1, originalType);
+ if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) {
// When the high-adjusted part of a toc relocation evalutes to 0, it is
// changed into a nop. The lo part then needs to be updated to use the toc
// pointer register r2, as the base register.
- if (isInstructionUpdateForm(Inst))
- error(getErrorLocation(Loc) +
+ if (isInstructionUpdateForm(insn))
+ error(getErrorLocation(loc) +
"Can't toc-optimize an update instruction: 0x" +
- Twine::utohexstr(Inst));
- Inst = (Inst & 0xFFE0000F) | 0x00020000;
- writeInstrFromHalf16(Loc, Inst);
+ Twine::utohexstr(insn));
+ insn &= 0xffe00000 | mask;
+ writeFromHalf16(loc, insn | 0x00020000 | lo(val));
+ } else {
+ write16(loc, (read16(loc) & mask) | lo(val));
}
- write16(Loc, (read16(Loc) & Mask) | lo(Val));
} break;
- case R_PPC64_ADDR32:
+ case R_PPC64_TPREL16:
+ checkInt(loc, val, 16, originalType);
+ write16(loc, val);
+ break;
case R_PPC64_REL32:
- checkInt(Loc, Val, 32, Type);
- write32(Loc, Val);
+ checkInt(loc, val, 32, type);
+ write32(loc, val);
break;
case R_PPC64_ADDR64:
case R_PPC64_REL64:
case R_PPC64_TOC:
- write64(Loc, Val);
+ write64(loc, val);
break;
case R_PPC64_REL14: {
- uint32_t Mask = 0x0000FFFC;
- checkInt(Loc, Val, 16, Type);
- checkAlignment(Loc, Val, 4, Type);
- write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
+ uint32_t mask = 0x0000FFFC;
+ checkInt(loc, val, 16, type);
+ checkAlignment(loc, val, 4, type);
+ write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
case R_PPC64_REL24: {
- uint32_t Mask = 0x03FFFFFC;
- checkInt(Loc, Val, 26, Type);
- checkAlignment(Loc, Val, 4, Type);
- write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
+ uint32_t mask = 0x03FFFFFC;
+ checkInt(loc, val, 26, type);
+ checkAlignment(loc, val, 4, type);
+ write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
case R_PPC64_DTPREL64:
- write64(Loc, Val - DynamicThreadPointerOffset);
+ write64(loc, val - dynamicThreadPointerOffset);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
-bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
- if (Type != R_PPC64_REL14 && Type != R_PPC64_REL24)
+bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
+ if (type != R_PPC64_REL14 && type != R_PPC64_REL24)
return false;
// If a function is in the Plt it needs to be called with a call-stub.
- if (S.isInPlt())
+ if (s.isInPlt())
return true;
// If a symbol is a weak undefined and we are compiling an executable
// it doesn't need a range-extending thunk since it can't be called.
- if (S.isUndefWeak() && !Config->Shared)
+ if (s.isUndefWeak() && !config->shared)
return false;
// If the offset exceeds the range of the branch type then it will need
// a range-extending thunk.
- return !inBranchRange(Type, BranchAddr, S.getVA());
+ // See the comment in getRelocTargetVA() about R_PPC64_CALL.
+ return !inBranchRange(type, branchAddr,
+ s.getVA() +
+ getPPC64GlobalEntryToLocalEntryOffset(s.stOther));
}
-bool PPC64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
- int64_t Offset = Dst - Src;
- if (Type == R_PPC64_REL14)
- return isInt<16>(Offset);
- if (Type == R_PPC64_REL24)
- return isInt<26>(Offset);
+uint32_t PPC64::getThunkSectionSpacing() const {
+ // See comment in Arch/ARM.cpp for a more detailed explanation of
+ // getThunkSectionSpacing(). For PPC64 we pick the constant here based on
+ // R_PPC64_REL24, which is used by unconditional branch instructions.
+ // 0x2000000 = (1 << 24-1) * 4
+ return 0x2000000;
+}
+
+bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ int64_t offset = dst - src;
+ if (type == R_PPC64_REL14)
+ return isInt<16>(offset);
+ if (type == R_PPC64_REL24)
+ return isInt<26>(offset);
llvm_unreachable("unsupported relocation type used in branch");
}
-RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const {
- if (Expr == R_RELAX_TLS_GD_TO_IE)
+RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ if (expr == R_RELAX_TLS_GD_TO_IE)
return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
- if (Expr == R_RELAX_TLS_LD_TO_LE)
+ if (expr == R_RELAX_TLS_LD_TO_LE)
return R_RELAX_TLS_LD_TO_LE_ABS;
- return Expr;
+ return expr;
}
// Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement.
@@ -794,24 +939,25 @@ RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
// thread pointer.
// Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is
// used as the relaxation hint for both steps 2 and 3.
-void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_PPC64_GOT_TLSGD16_HA:
// This is relaxed from addis rT, r2, sym@got@tlsgd@ha to
// addis rT, r2, sym@got@tprel@ha.
- relocateOne(Loc, R_PPC64_GOT_TPREL16_HA, Val);
+ relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val);
return;
+ case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO: {
// Relax from addi r3, rA, sym@got@tlsgd@l to
// ld r3, sym@got@tprel@l(rA)
- uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16));
- writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister);
- relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val);
+ uint32_t ra = (readFromHalf16(loc) & (0x1f << 16));
+ writeFromHalf16(loc, 0xe8600000 | ra);
+ relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val);
return;
}
case R_PPC64_TLSGD:
- write32(Loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop
- write32(Loc + 4, 0x7c636A14); // nop --> add r3, r3, r13
+ write32(loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop
+ write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13
return;
default:
llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
@@ -846,86 +992,86 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// pair by split-stack-size-adjust.
// addis r12, r1, ha(-stack-frame size - split-stack-adjust-size)
// addi r12, r12, l(-stack-frame size - split-stack-adjust-size)
-bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const {
+bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const {
// If the caller has a global entry point adjust the buffer past it. The start
// of the split-stack prologue will be at the local entry point.
- Loc += getPPC64GlobalEntryToLocalEntryOffset(StOther);
+ loc += getPPC64GlobalEntryToLocalEntryOffset(stOther);
// At the very least we expect to see a load of some split-stack data from the
// tcb, and 2 instructions that calculate the ending stack address this
// function will require. If there is not enough room for at least 3
// instructions it can't be a split-stack prologue.
- if (Loc + 12 >= End)
+ if (loc + 12 >= end)
return false;
// First instruction must be `ld r0, -0x7000-64(r13)`
- if (read32(Loc) != 0xe80d8fc0)
+ if (read32(loc) != 0xe80d8fc0)
return false;
- int16_t HiImm = 0;
- int16_t LoImm = 0;
+ int16_t hiImm = 0;
+ int16_t loImm = 0;
// First instruction can be either an addis if the frame size is larger then
// 32K, or an addi if the size is less then 32K.
- int32_t FirstInstr = read32(Loc + 4);
- if (getPrimaryOpCode(FirstInstr) == 15) {
- HiImm = FirstInstr & 0xFFFF;
- } else if (getPrimaryOpCode(FirstInstr) == 14) {
- LoImm = FirstInstr & 0xFFFF;
+ int32_t firstInstr = read32(loc + 4);
+ if (getPrimaryOpCode(firstInstr) == 15) {
+ hiImm = firstInstr & 0xFFFF;
+ } else if (getPrimaryOpCode(firstInstr) == 14) {
+ loImm = firstInstr & 0xFFFF;
} else {
return false;
}
// Second instruction is either an addi or a nop. If the first instruction was
// an addi then LoImm is set and the second instruction must be a nop.
- uint32_t SecondInstr = read32(Loc + 8);
- if (!LoImm && getPrimaryOpCode(SecondInstr) == 14) {
- LoImm = SecondInstr & 0xFFFF;
- } else if (SecondInstr != 0x60000000) {
+ uint32_t secondInstr = read32(loc + 8);
+ if (!loImm && getPrimaryOpCode(secondInstr) == 14) {
+ loImm = secondInstr & 0xFFFF;
+ } else if (secondInstr != 0x60000000) {
return false;
}
// The register operands of the first instruction should be the stack-pointer
// (r1) as the input (RA) and r12 as the output (RT). If the second
// instruction is not a nop, then it should use r12 as both input and output.
- auto CheckRegOperands = [](uint32_t Instr, uint8_t ExpectedRT,
- uint8_t ExpectedRA) {
- return ((Instr & 0x3E00000) >> 21 == ExpectedRT) &&
- ((Instr & 0x1F0000) >> 16 == ExpectedRA);
+ auto checkRegOperands = [](uint32_t instr, uint8_t expectedRT,
+ uint8_t expectedRA) {
+ return ((instr & 0x3E00000) >> 21 == expectedRT) &&
+ ((instr & 0x1F0000) >> 16 == expectedRA);
};
- if (!CheckRegOperands(FirstInstr, 12, 1))
+ if (!checkRegOperands(firstInstr, 12, 1))
return false;
- if (SecondInstr != 0x60000000 && !CheckRegOperands(SecondInstr, 12, 12))
+ if (secondInstr != 0x60000000 && !checkRegOperands(secondInstr, 12, 12))
return false;
- int32_t StackFrameSize = (HiImm * 65536) + LoImm;
+ int32_t stackFrameSize = (hiImm * 65536) + loImm;
// Check that the adjusted size doesn't overflow what we can represent with 2
// instructions.
- if (StackFrameSize < Config->SplitStackAdjustSize + INT32_MIN) {
- error(getErrorLocation(Loc) + "split-stack prologue adjustment overflows");
+ if (stackFrameSize < config->splitStackAdjustSize + INT32_MIN) {
+ error(getErrorLocation(loc) + "split-stack prologue adjustment overflows");
return false;
}
- int32_t AdjustedStackFrameSize =
- StackFrameSize - Config->SplitStackAdjustSize;
+ int32_t adjustedStackFrameSize =
+ stackFrameSize - config->splitStackAdjustSize;
- LoImm = AdjustedStackFrameSize & 0xFFFF;
- HiImm = (AdjustedStackFrameSize + 0x8000) >> 16;
- if (HiImm) {
- write32(Loc + 4, 0x3D810000 | (uint16_t)HiImm);
+ loImm = adjustedStackFrameSize & 0xFFFF;
+ hiImm = (adjustedStackFrameSize + 0x8000) >> 16;
+ if (hiImm) {
+ write32(loc + 4, 0x3D810000 | (uint16_t)hiImm);
// If the low immediate is zero the second instruction will be a nop.
- SecondInstr = LoImm ? 0x398C0000 | (uint16_t)LoImm : 0x60000000;
- write32(Loc + 8, SecondInstr);
+ secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : 0x60000000;
+ write32(loc + 8, secondInstr);
} else {
// addi r12, r1, imm
- write32(Loc + 4, (0x39810000) | (uint16_t)LoImm);
- write32(Loc + 8, 0x60000000);
+ write32(loc + 4, (0x39810000) | (uint16_t)loImm);
+ write32(loc + 8, 0x60000000);
}
return true;
}
TargetInfo *elf::getPPC64TargetInfo() {
- static PPC64 Target;
- return &Target;
+ static PPC64 target;
+ return &target;
}
diff --git a/ELF/Arch/RISCV.cpp b/ELF/Arch/RISCV.cpp
index 461e8d35c3e6c..6f16ade571773 100644
--- a/ELF/Arch/RISCV.cpp
+++ b/ELF/Arch/RISCV.cpp
@@ -1,13 +1,13 @@
//===- RISCV.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
+#include "SyntheticSections.h"
#include "Target.h"
using namespace llvm;
@@ -23,59 +23,207 @@ class RISCV final : public TargetInfo {
public:
RISCV();
uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void writeGotHeader(uint8_t *buf) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ RelType getDynRel(RelType type) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // end anonymous namespace
-RISCV::RISCV() { NoneRel = R_RISCV_NONE; }
+const uint64_t dtpOffset = 0x800;
+
+enum Op {
+ ADDI = 0x13,
+ AUIPC = 0x17,
+ JALR = 0x67,
+ LD = 0x3003,
+ LW = 0x2003,
+ SRLI = 0x5013,
+ SUB = 0x40000033,
+};
+
+enum Reg {
+ X_RA = 1,
+ X_T0 = 5,
+ X_T1 = 6,
+ X_T2 = 7,
+ X_T3 = 28,
+};
+
+static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; }
+static uint32_t lo12(uint32_t val) { return val & 4095; }
+
+static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) {
+ return op | (rd << 7) | (rs1 << 15) | (imm << 20);
+}
+static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) {
+ return op | (rd << 7) | (rs1 << 15) | (rs2 << 20);
+}
+static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) {
+ return op | (rd << 7) | (imm << 12);
+}
+
+RISCV::RISCV() {
+ copyRel = R_RISCV_COPY;
+ noneRel = R_RISCV_NONE;
+ pltRel = R_RISCV_JUMP_SLOT;
+ relativeRel = R_RISCV_RELATIVE;
+ if (config->is64) {
+ symbolicRel = R_RISCV_64;
+ tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64;
+ tlsOffsetRel = R_RISCV_TLS_DTPREL64;
+ tlsGotRel = R_RISCV_TLS_TPREL64;
+ } else {
+ symbolicRel = R_RISCV_32;
+ tlsModuleIndexRel = R_RISCV_TLS_DTPMOD32;
+ tlsOffsetRel = R_RISCV_TLS_DTPREL32;
+ tlsGotRel = R_RISCV_TLS_TPREL32;
+ }
+ gotRel = symbolicRel;
+
+ // .got[0] = _DYNAMIC
+ gotBaseSymInGotPlt = false;
+ gotHeaderEntriesNum = 1;
+
+ // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map
+ gotPltHeaderEntriesNum = 2;
-static uint32_t getEFlags(InputFile *F) {
- if (Config->Is64)
- return cast<ObjFile<ELF64LE>>(F)->getObj().getHeader()->e_flags;
- return cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
+}
+
+static uint32_t getEFlags(InputFile *f) {
+ if (config->is64)
+ return cast<ObjFile<ELF64LE>>(f)->getObj().getHeader()->e_flags;
+ return cast<ObjFile<ELF32LE>>(f)->getObj().getHeader()->e_flags;
}
uint32_t RISCV::calcEFlags() const {
- assert(!ObjectFiles.empty());
+ assert(!objectFiles.empty());
- uint32_t Target = getEFlags(ObjectFiles.front());
+ uint32_t target = getEFlags(objectFiles.front());
- for (InputFile *F : ObjectFiles) {
- uint32_t EFlags = getEFlags(F);
- if (EFlags & EF_RISCV_RVC)
- Target |= EF_RISCV_RVC;
+ for (InputFile *f : objectFiles) {
+ uint32_t eflags = getEFlags(f);
+ if (eflags & EF_RISCV_RVC)
+ target |= EF_RISCV_RVC;
- if ((EFlags & EF_RISCV_FLOAT_ABI) != (Target & EF_RISCV_FLOAT_ABI))
- error(toString(F) +
+ if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI))
+ error(toString(f) +
": cannot link object files with different floating-point ABI");
- if ((EFlags & EF_RISCV_RVE) != (Target & EF_RISCV_RVE))
- error(toString(F) +
+ if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE))
+ error(toString(f) +
": cannot link object files with different EF_RISCV_RVE");
}
- return Target;
+ return target;
+}
+
+void RISCV::writeGotHeader(uint8_t *buf) const {
+ if (config->is64)
+ write64le(buf, mainPart->dynamic->getVA());
+ else
+ write32le(buf, mainPart->dynamic->getVA());
+}
+
+void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ if (config->is64)
+ write64le(buf, in.plt->getVA());
+ else
+ write32le(buf, in.plt->getVA());
+}
+
+void RISCV::writePltHeader(uint8_t *buf) const {
+ // 1: auipc t2, %pcrel_hi(.got.plt)
+ // sub t1, t1, t3
+ // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve
+ // addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0]
+ // addi t0, t2, %pcrel_lo(1b)
+ // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0]
+ // l[wd] t0, Wordsize(t0); t0 = link_map
+ // jr t3
+ uint32_t offset = in.gotPlt->getVA() - in.plt->getVA();
+ uint32_t load = config->is64 ? LD : LW;
+ write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset)));
+ write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3));
+ write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset)));
+ write32le(buf + 12, itype(ADDI, X_T1, X_T1, -target->pltHeaderSize - 12));
+ write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset)));
+ write32le(buf + 20, itype(SRLI, X_T1, X_T1, config->is64 ? 1 : 2));
+ write32le(buf + 24, itype(load, X_T0, X_T0, config->wordsize));
+ write32le(buf + 28, itype(JALR, 0, X_T3, 0));
}
-RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+void RISCV::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ // 1: auipc t3, %pcrel_hi(f@.got.plt)
+ // l[wd] t3, %pcrel_lo(1b)(t3)
+ // jalr t1, t3
+ // nop
+ uint32_t offset = gotPltEntryAddr - pltEntryAddr;
+ write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
+ write32le(buf + 4, itype(config->is64 ? LD : LW, X_T3, X_T3, lo12(offset)));
+ write32le(buf + 8, itype(JALR, X_T1, X_T3, 0));
+ write32le(buf + 12, itype(ADDI, 0, 0, 0));
+}
+
+RelType RISCV::getDynRel(RelType type) const {
+ return type == target->symbolicRel ? type
+ : static_cast<RelType>(R_RISCV_NONE);
+}
+
+RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
+ case R_RISCV_ADD8:
+ case R_RISCV_ADD16:
+ case R_RISCV_ADD32:
+ case R_RISCV_ADD64:
+ case R_RISCV_SET6:
+ case R_RISCV_SET8:
+ case R_RISCV_SET16:
+ case R_RISCV_SET32:
+ case R_RISCV_SUB6:
+ case R_RISCV_SUB8:
+ case R_RISCV_SUB16:
+ case R_RISCV_SUB32:
+ case R_RISCV_SUB64:
+ return R_RISCV_ADD;
case R_RISCV_JAL:
case R_RISCV_BRANCH:
- case R_RISCV_CALL:
case R_RISCV_PCREL_HI20:
case R_RISCV_RVC_BRANCH:
case R_RISCV_RVC_JUMP:
case R_RISCV_32_PCREL:
return R_PC;
+ case R_RISCV_CALL:
+ case R_RISCV_CALL_PLT:
+ return R_PLT_PC;
+ case R_RISCV_GOT_HI20:
+ return R_GOT_PC;
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
return R_RISCV_PC_INDIRECT;
+ case R_RISCV_TLS_GD_HI20:
+ return R_TLSGD_PC;
+ case R_RISCV_TLS_GOT_HI20:
+ config->hasStaticTlsModel = true;
+ return R_GOT_PC;
+ case R_RISCV_TPREL_HI20:
+ case R_RISCV_TPREL_LO12_I:
+ case R_RISCV_TPREL_LO12_S:
+ return R_TLS;
case R_RISCV_RELAX:
case R_RISCV_ALIGN:
+ case R_RISCV_TPREL_ADD:
return R_HINT;
default:
return R_ABS;
@@ -83,175 +231,190 @@ RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S,
}
// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63.
-static uint32_t extractBits(uint64_t V, uint32_t Begin, uint32_t End) {
- return (V & ((1ULL << (Begin + 1)) - 1)) >> End;
+static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) {
+ return (v & ((1ULL << (begin + 1)) - 1)) >> end;
}
-void RISCV::relocateOne(uint8_t *Loc, const RelType Type,
- const uint64_t Val) const {
- switch (Type) {
+void RISCV::relocateOne(uint8_t *loc, const RelType type,
+ const uint64_t val) const {
+ const unsigned bits = config->wordsize * 8;
+
+ switch (type) {
case R_RISCV_32:
- write32le(Loc, Val);
+ write32le(loc, val);
return;
case R_RISCV_64:
- write64le(Loc, Val);
+ write64le(loc, val);
return;
case R_RISCV_RVC_BRANCH: {
- checkInt(Loc, static_cast<int64_t>(Val) >> 1, 8, Type);
- checkAlignment(Loc, Val, 2, Type);
- uint16_t Insn = read16le(Loc) & 0xE383;
- uint16_t Imm8 = extractBits(Val, 8, 8) << 12;
- uint16_t Imm4_3 = extractBits(Val, 4, 3) << 10;
- uint16_t Imm7_6 = extractBits(Val, 7, 6) << 5;
- uint16_t Imm2_1 = extractBits(Val, 2, 1) << 3;
- uint16_t Imm5 = extractBits(Val, 5, 5) << 2;
- Insn |= Imm8 | Imm4_3 | Imm7_6 | Imm2_1 | Imm5;
-
- write16le(Loc, Insn);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 8, type);
+ checkAlignment(loc, val, 2, type);
+ uint16_t insn = read16le(loc) & 0xE383;
+ uint16_t imm8 = extractBits(val, 8, 8) << 12;
+ uint16_t imm4_3 = extractBits(val, 4, 3) << 10;
+ uint16_t imm7_6 = extractBits(val, 7, 6) << 5;
+ uint16_t imm2_1 = extractBits(val, 2, 1) << 3;
+ uint16_t imm5 = extractBits(val, 5, 5) << 2;
+ insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5;
+
+ write16le(loc, insn);
return;
}
case R_RISCV_RVC_JUMP: {
- checkInt(Loc, static_cast<int64_t>(Val) >> 1, 11, Type);
- checkAlignment(Loc, Val, 2, Type);
- uint16_t Insn = read16le(Loc) & 0xE003;
- uint16_t Imm11 = extractBits(Val, 11, 11) << 12;
- uint16_t Imm4 = extractBits(Val, 4, 4) << 11;
- uint16_t Imm9_8 = extractBits(Val, 9, 8) << 9;
- uint16_t Imm10 = extractBits(Val, 10, 10) << 8;
- uint16_t Imm6 = extractBits(Val, 6, 6) << 7;
- uint16_t Imm7 = extractBits(Val, 7, 7) << 6;
- uint16_t Imm3_1 = extractBits(Val, 3, 1) << 3;
- uint16_t Imm5 = extractBits(Val, 5, 5) << 2;
- Insn |= Imm11 | Imm4 | Imm9_8 | Imm10 | Imm6 | Imm7 | Imm3_1 | Imm5;
-
- write16le(Loc, Insn);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 11, type);
+ checkAlignment(loc, val, 2, type);
+ uint16_t insn = read16le(loc) & 0xE003;
+ uint16_t imm11 = extractBits(val, 11, 11) << 12;
+ uint16_t imm4 = extractBits(val, 4, 4) << 11;
+ uint16_t imm9_8 = extractBits(val, 9, 8) << 9;
+ uint16_t imm10 = extractBits(val, 10, 10) << 8;
+ uint16_t imm6 = extractBits(val, 6, 6) << 7;
+ uint16_t imm7 = extractBits(val, 7, 7) << 6;
+ uint16_t imm3_1 = extractBits(val, 3, 1) << 3;
+ uint16_t imm5 = extractBits(val, 5, 5) << 2;
+ insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5;
+
+ write16le(loc, insn);
return;
}
case R_RISCV_RVC_LUI: {
- int32_t Imm = ((Val + 0x800) >> 12);
- checkUInt(Loc, Imm, 6, Type);
- if (Imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
- write16le(Loc, (read16le(Loc) & 0x0F83) | 0x4000);
+ int64_t imm = SignExtend64(val + 0x800, bits) >> 12;
+ checkInt(loc, imm, 6, type);
+ if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
+ write16le(loc, (read16le(loc) & 0x0F83) | 0x4000);
} else {
- uint16_t Imm17 = extractBits(Val + 0x800, 17, 17) << 12;
- uint16_t Imm16_12 = extractBits(Val + 0x800, 16, 12) << 2;
- write16le(Loc, (read16le(Loc) & 0xEF83) | Imm17 | Imm16_12);
+ uint16_t imm17 = extractBits(val + 0x800, 17, 17) << 12;
+ uint16_t imm16_12 = extractBits(val + 0x800, 16, 12) << 2;
+ write16le(loc, (read16le(loc) & 0xEF83) | imm17 | imm16_12);
}
return;
}
case R_RISCV_JAL: {
- checkInt(Loc, static_cast<int64_t>(Val) >> 1, 20, Type);
- checkAlignment(Loc, Val, 2, Type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 20, type);
+ checkAlignment(loc, val, 2, type);
- uint32_t Insn = read32le(Loc) & 0xFFF;
- uint32_t Imm20 = extractBits(Val, 20, 20) << 31;
- uint32_t Imm10_1 = extractBits(Val, 10, 1) << 21;
- uint32_t Imm11 = extractBits(Val, 11, 11) << 20;
- uint32_t Imm19_12 = extractBits(Val, 19, 12) << 12;
- Insn |= Imm20 | Imm10_1 | Imm11 | Imm19_12;
+ uint32_t insn = read32le(loc) & 0xFFF;
+ uint32_t imm20 = extractBits(val, 20, 20) << 31;
+ uint32_t imm10_1 = extractBits(val, 10, 1) << 21;
+ uint32_t imm11 = extractBits(val, 11, 11) << 20;
+ uint32_t imm19_12 = extractBits(val, 19, 12) << 12;
+ insn |= imm20 | imm10_1 | imm11 | imm19_12;
- write32le(Loc, Insn);
+ write32le(loc, insn);
return;
}
case R_RISCV_BRANCH: {
- checkInt(Loc, static_cast<int64_t>(Val) >> 1, 12, Type);
- checkAlignment(Loc, Val, 2, Type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 12, type);
+ checkAlignment(loc, val, 2, type);
- uint32_t Insn = read32le(Loc) & 0x1FFF07F;
- uint32_t Imm12 = extractBits(Val, 12, 12) << 31;
- uint32_t Imm10_5 = extractBits(Val, 10, 5) << 25;
- uint32_t Imm4_1 = extractBits(Val, 4, 1) << 8;
- uint32_t Imm11 = extractBits(Val, 11, 11) << 7;
- Insn |= Imm12 | Imm10_5 | Imm4_1 | Imm11;
+ uint32_t insn = read32le(loc) & 0x1FFF07F;
+ uint32_t imm12 = extractBits(val, 12, 12) << 31;
+ uint32_t imm10_5 = extractBits(val, 10, 5) << 25;
+ uint32_t imm4_1 = extractBits(val, 4, 1) << 8;
+ uint32_t imm11 = extractBits(val, 11, 11) << 7;
+ insn |= imm12 | imm10_5 | imm4_1 | imm11;
- write32le(Loc, Insn);
+ write32le(loc, insn);
return;
}
// auipc + jalr pair
- case R_RISCV_CALL: {
- checkInt(Loc, Val, 32, Type);
- if (isInt<32>(Val)) {
- relocateOne(Loc, R_RISCV_PCREL_HI20, Val);
- relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val);
+ case R_RISCV_CALL:
+ case R_RISCV_CALL_PLT: {
+ int64_t hi = SignExtend64(val + 0x800, bits) >> 12;
+ checkInt(loc, hi, 20, type);
+ if (isInt<20>(hi)) {
+ relocateOne(loc, R_RISCV_PCREL_HI20, val);
+ relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val);
}
return;
}
+ case R_RISCV_GOT_HI20:
case R_RISCV_PCREL_HI20:
+ case R_RISCV_TLS_GD_HI20:
+ case R_RISCV_TLS_GOT_HI20:
+ case R_RISCV_TPREL_HI20:
case R_RISCV_HI20: {
- checkInt(Loc, Val, 32, Type);
- uint32_t Hi = Val + 0x800;
- write32le(Loc, (read32le(Loc) & 0xFFF) | (Hi & 0xFFFFF000));
+ uint64_t hi = val + 0x800;
+ checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type);
+ write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000));
return;
}
case R_RISCV_PCREL_LO12_I:
+ case R_RISCV_TPREL_LO12_I:
case R_RISCV_LO12_I: {
- checkInt(Loc, Val, 32, Type);
- uint32_t Hi = Val + 0x800;
- uint32_t Lo = Val - (Hi & 0xFFFFF000);
- write32le(Loc, (read32le(Loc) & 0xFFFFF) | ((Lo & 0xFFF) << 20));
+ uint64_t hi = (val + 0x800) >> 12;
+ uint64_t lo = val - (hi << 12);
+ write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20));
return;
}
case R_RISCV_PCREL_LO12_S:
+ case R_RISCV_TPREL_LO12_S:
case R_RISCV_LO12_S: {
- checkInt(Loc, Val, 32, Type);
- uint32_t Hi = Val + 0x800;
- uint32_t Lo = Val - (Hi & 0xFFFFF000);
- uint32_t Imm11_5 = extractBits(Lo, 11, 5) << 25;
- uint32_t Imm4_0 = extractBits(Lo, 4, 0) << 7;
- write32le(Loc, (read32le(Loc) & 0x1FFF07F) | Imm11_5 | Imm4_0);
+ uint64_t hi = (val + 0x800) >> 12;
+ uint64_t lo = val - (hi << 12);
+ uint32_t imm11_5 = extractBits(lo, 11, 5) << 25;
+ uint32_t imm4_0 = extractBits(lo, 4, 0) << 7;
+ write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0);
return;
}
case R_RISCV_ADD8:
- *Loc += Val;
+ *loc += val;
return;
case R_RISCV_ADD16:
- write16le(Loc, read16le(Loc) + Val);
+ write16le(loc, read16le(loc) + val);
return;
case R_RISCV_ADD32:
- write32le(Loc, read32le(Loc) + Val);
+ write32le(loc, read32le(loc) + val);
return;
case R_RISCV_ADD64:
- write64le(Loc, read64le(Loc) + Val);
+ write64le(loc, read64le(loc) + val);
return;
case R_RISCV_SUB6:
- *Loc = (*Loc & 0xc0) | (((*Loc & 0x3f) - Val) & 0x3f);
+ *loc = (*loc & 0xc0) | (((*loc & 0x3f) - val) & 0x3f);
return;
case R_RISCV_SUB8:
- *Loc -= Val;
+ *loc -= val;
return;
case R_RISCV_SUB16:
- write16le(Loc, read16le(Loc) - Val);
+ write16le(loc, read16le(loc) - val);
return;
case R_RISCV_SUB32:
- write32le(Loc, read32le(Loc) - Val);
+ write32le(loc, read32le(loc) - val);
return;
case R_RISCV_SUB64:
- write64le(Loc, read64le(Loc) - Val);
+ write64le(loc, read64le(loc) - val);
return;
case R_RISCV_SET6:
- *Loc = (*Loc & 0xc0) | (Val & 0x3f);
+ *loc = (*loc & 0xc0) | (val & 0x3f);
return;
case R_RISCV_SET8:
- *Loc = Val;
+ *loc = val;
return;
case R_RISCV_SET16:
- write16le(Loc, Val);
+ write16le(loc, val);
return;
case R_RISCV_SET32:
case R_RISCV_32_PCREL:
- write32le(Loc, Val);
+ write32le(loc, val);
return;
+ case R_RISCV_TLS_DTPREL32:
+ write32le(loc, val - dtpOffset);
+ break;
+ case R_RISCV_TLS_DTPREL64:
+ write64le(loc, val - dtpOffset);
+ break;
+
case R_RISCV_ALIGN:
case R_RISCV_RELAX:
return; // Ignored (for now)
@@ -267,13 +430,13 @@ void RISCV::relocateOne(uint8_t *Loc, const RelType Type,
case R_RISCV_GPREL_I:
case R_RISCV_GPREL_S:
default:
- error(getErrorLocation(Loc) +
- "unimplemented relocation: " + toString(Type));
+ error(getErrorLocation(loc) +
+ "unimplemented relocation: " + toString(type));
return;
}
}
TargetInfo *elf::getRISCVTargetInfo() {
- static RISCV Target;
- return &Target;
+ static RISCV target;
+ return &target;
}
diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp
index 831aa2028e7fd..5299206dd919f 100644
--- a/ELF/Arch/SPARCV9.cpp
+++ b/ELF/Arch/SPARCV9.cpp
@@ -1,9 +1,8 @@
//===- SPARCV9.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -24,32 +23,32 @@ namespace {
class SPARCV9 final : public TargetInfo {
public:
SPARCV9();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ void writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
SPARCV9::SPARCV9() {
- CopyRel = R_SPARC_COPY;
- GotRel = R_SPARC_GLOB_DAT;
- NoneRel = R_SPARC_NONE;
- PltRel = R_SPARC_JMP_SLOT;
- RelativeRel = R_SPARC_RELATIVE;
- GotEntrySize = 8;
- PltEntrySize = 32;
- PltHeaderSize = 4 * PltEntrySize;
+ copyRel = R_SPARC_COPY;
+ gotRel = R_SPARC_GLOB_DAT;
+ noneRel = R_SPARC_NONE;
+ pltRel = R_SPARC_JMP_SLOT;
+ relativeRel = R_SPARC_RELATIVE;
+ symbolicRel = R_SPARC_64;
+ pltEntrySize = 32;
+ pltHeaderSize = 4 * pltEntrySize;
- PageSize = 8192;
- DefaultMaxPageSize = 0x100000;
- DefaultImageBase = 0x100000;
+ defaultCommonPageSize = 8192;
+ defaultMaxPageSize = 0x100000;
+ defaultImageBase = 0x100000;
}
-RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_SPARC_32:
case R_SPARC_UA32:
case R_SPARC_64:
@@ -69,64 +68,65 @@ RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S,
case R_SPARC_NONE:
return R_NONE;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
-void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_SPARC_32:
case R_SPARC_UA32:
// V-word32
- checkUInt(Loc, Val, 32, Type);
- write32be(Loc, Val);
+ checkUInt(loc, val, 32, type);
+ write32be(loc, val);
break;
case R_SPARC_DISP32:
// V-disp32
- checkInt(Loc, Val, 32, Type);
- write32be(Loc, Val);
+ checkInt(loc, val, 32, type);
+ write32be(loc, val);
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
// V-disp30
- checkInt(Loc, Val, 32, Type);
- write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff));
+ checkInt(loc, val, 32, type);
+ write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff));
break;
case R_SPARC_22:
// V-imm22
- checkUInt(Loc, Val, 22, Type);
- write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff));
+ checkUInt(loc, val, 22, type);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
break;
case R_SPARC_GOT22:
case R_SPARC_PC22:
// T-imm22
- write32be(Loc, (read32be(Loc) & ~0x003fffff) | ((Val >> 10) & 0x003fffff));
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
break;
case R_SPARC_WDISP19:
// V-disp19
- checkInt(Loc, Val, 21, Type);
- write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff));
+ checkInt(loc, val, 21, type);
+ write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
break;
case R_SPARC_GOT10:
case R_SPARC_PC10:
// T-simm10
- write32be(Loc, (read32be(Loc) & ~0x000003ff) | (Val & 0x000003ff));
+ write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
break;
case R_SPARC_64:
case R_SPARC_UA64:
- case R_SPARC_GLOB_DAT:
// V-xword64
- write64be(Loc, Val);
+ write64be(loc, val);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t PltData[] = {
+void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t pltData[] = {
0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1
0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1
0x01, 0x00, 0x00, 0x00, // nop
@@ -136,14 +136,14 @@ void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
0x01, 0x00, 0x00, 0x00, // nop
0x01, 0x00, 0x00, 0x00 // nop
};
- memcpy(Buf, PltData, sizeof(PltData));
+ memcpy(buf, pltData, sizeof(pltData));
- uint64_t Off = getPltEntryOffset(Index);
- relocateOne(Buf, R_SPARC_22, Off);
- relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize));
+ uint64_t off = pltHeaderSize + pltEntrySize * index;
+ relocateOne(buf, R_SPARC_22, off);
+ relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
}
TargetInfo *elf::getSPARCV9TargetInfo() {
- static SPARCV9 Target;
- return &Target;
+ static SPARCV9 target;
+ return &target;
}
diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp
index e910375d2fc75..e1dd231e8e8d4 100644
--- a/ELF/Arch/X86.cpp
+++ b/ELF/Arch/X86.cpp
@@ -1,9 +1,8 @@
//===- X86.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -24,63 +23,73 @@ namespace {
class X86 : public TargetInfo {
public:
X86();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
- void writeGotPltHeader(uint8_t *Buf) const override;
- RelType getDynRel(RelType Type) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
-
- RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const override;
- void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ int getTlsGdRelaxSkip(RelType type) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ void writeGotPltHeader(uint8_t *buf) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+
+ RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const override;
+ void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
X86::X86() {
- CopyRel = R_386_COPY;
- GotRel = R_386_GLOB_DAT;
- NoneRel = R_386_NONE;
- PltRel = R_386_JUMP_SLOT;
- IRelativeRel = R_386_IRELATIVE;
- RelativeRel = R_386_RELATIVE;
- TlsGotRel = R_386_TLS_TPOFF;
- TlsModuleIndexRel = R_386_TLS_DTPMOD32;
- TlsOffsetRel = R_386_TLS_DTPOFF32;
- GotEntrySize = 4;
- GotPltEntrySize = 4;
- PltEntrySize = 16;
- PltHeaderSize = 16;
- TlsGdRelaxSkip = 2;
- TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
+ copyRel = R_386_COPY;
+ gotRel = R_386_GLOB_DAT;
+ noneRel = R_386_NONE;
+ pltRel = R_386_JUMP_SLOT;
+ iRelativeRel = R_386_IRELATIVE;
+ relativeRel = R_386_RELATIVE;
+ symbolicRel = R_386_32;
+ tlsGotRel = R_386_TLS_TPOFF;
+ tlsModuleIndexRel = R_386_TLS_DTPMOD32;
+ tlsOffsetRel = R_386_TLS_DTPOFF32;
+ pltEntrySize = 16;
+ pltHeaderSize = 16;
+ trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
// Align to the non-PAE large page size (known as a superpage or huge page).
// FreeBSD automatically promotes large, superpage-aligned allocations.
- DefaultImageBase = 0x400000;
+ defaultImageBase = 0x400000;
}
-static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; }
+int X86::getTlsGdRelaxSkip(RelType type) const {
+ return 2;
+}
-RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr X86::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ // There are 4 different TLS variable models with varying degrees of
+ // flexibility and performance. LocalExec and InitialExec models are fast but
+ // less-flexible models. If they are in use, we set DF_STATIC_TLS flag in the
+ // dynamic section to let runtime know about that.
+ if (type == R_386_TLS_LE || type == R_386_TLS_LE_32 || type == R_386_TLS_IE ||
+ type == R_386_TLS_GOTIE)
+ config->hasStaticTlsModel = true;
+
+ switch (type) {
case R_386_8:
case R_386_16:
case R_386_32:
- case R_386_TLS_LDO_32:
return R_ABS;
+ case R_386_TLS_LDO_32:
+ return R_DTPREL;
case R_386_TLS_GD:
- return R_TLSGD_GOT_FROM_END;
+ return R_TLSGD_GOTPLT;
case R_386_TLS_LDM:
- return R_TLSLD_GOT_FROM_END;
+ return R_TLSLD_GOTPLT;
case R_386_PLT32:
return R_PLT_PC;
case R_386_PC8:
@@ -88,7 +97,7 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
case R_386_PC32:
return R_PC;
case R_386_GOTPC:
- return R_GOTONLY_PC_FROM_END;
+ return R_GOTPLTONLY_PC;
case R_386_TLS_IE:
return R_GOT;
case R_386_GOT32:
@@ -108,14 +117,14 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
// load an GOT address to a register, which is usually %ebx.
//
// So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or
- // foo@GOT(%reg).
+ // foo@GOT(%ebx).
//
// foo@GOT is not usable in PIC. If we are creating a PIC output and if we
// find such relocation, we should report an error. foo@GOT is resolved to
// an *absolute* address of foo's GOT entry, because both GOT address and
// foo's offset are known. In other words, it's G + A.
//
- // foo@GOT(%reg) needs to be resolved to a *relative* offset from a GOT to
+ // foo@GOT(%ebx) needs to be resolved to a *relative* offset from a GOT to
// foo's GOT entry in the table, because GOT address is not known but foo's
// offset in the table is known. It's G + A - GOT.
//
@@ -123,16 +132,16 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
// different use cases. In order to distinguish them, we have to read a
// machine instruction.
//
- // The following code implements it. We assume that Loc[0] is the first
- // byte of a displacement or an immediate field of a valid machine
+ // The following code implements it. We assume that Loc[0] is the first byte
+ // of a displacement or an immediate field of a valid machine
// instruction. That means a ModRM byte is at Loc[-1]. By taking a look at
- // the byte, we can determine whether the instruction is register-relative
- // (i.e. it was generated for foo@GOT(%reg)) or absolute (i.e. foo@GOT).
- return hasBaseReg(Loc[-1]) ? R_GOT_FROM_END : R_GOT;
+ // the byte, we can determine whether the instruction uses the operand as an
+ // absolute address (R_GOT) or a register-relative address (R_GOTPLT).
+ return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT;
case R_386_TLS_GOTIE:
- return R_GOT_FROM_END;
+ return R_GOTPLT;
case R_386_GOTOFF:
- return R_GOTREL_FROM_END;
+ return R_GOTPLTREL;
case R_386_TLS_LE:
return R_TLS;
case R_386_TLS_LE_32:
@@ -140,105 +149,102 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
case R_386_NONE:
return R_NONE;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
-RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const {
- switch (Expr) {
+RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ switch (expr) {
default:
- return Expr;
+ return expr;
case R_RELAX_TLS_GD_TO_IE:
- return R_RELAX_TLS_GD_TO_IE_END;
+ return R_RELAX_TLS_GD_TO_IE_GOTPLT;
case R_RELAX_TLS_GD_TO_LE:
return R_RELAX_TLS_GD_TO_LE_NEG;
}
}
-void X86::writeGotPltHeader(uint8_t *Buf) const {
- write32le(Buf, In.Dynamic->getVA());
+void X86::writeGotPltHeader(uint8_t *buf) const {
+ write32le(buf, mainPart->dynamic->getVA());
}
-void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
+void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const {
// Entries in .got.plt initially points back to the corresponding
// PLT entries with a fixed offset to skip the first instruction.
- write32le(Buf, S.getPltVA() + 6);
+ write32le(buf, s.getPltVA() + 6);
}
-void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
+void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
// An x86 entry is the address of the ifunc resolver function.
- write32le(Buf, S.getVA());
+ write32le(buf, s.getVA());
}
-RelType X86::getDynRel(RelType Type) const {
- if (Type == R_386_TLS_LE)
+RelType X86::getDynRel(RelType type) const {
+ if (type == R_386_TLS_LE)
return R_386_TLS_TPOFF;
- if (Type == R_386_TLS_LE_32)
+ if (type == R_386_TLS_LE_32)
return R_386_TLS_TPOFF32;
- return Type;
+ return type;
}
-void X86::writePltHeader(uint8_t *Buf) const {
- if (Config->Pic) {
- const uint8_t V[] = {
- 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx)
- 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx)
+void X86::writePltHeader(uint8_t *buf) const {
+ if (config->isPic) {
+ const uint8_t v[] = {
+ 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx)
+ 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx)
0x90, 0x90, 0x90, 0x90 // nop
};
- memcpy(Buf, V, sizeof(V));
-
- uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
- uint32_t GotPlt = In.GotPlt->getVA() - Ebx;
- write32le(Buf + 2, GotPlt + 4);
- write32le(Buf + 8, GotPlt + 8);
+ memcpy(buf, v, sizeof(v));
return;
}
- const uint8_t PltData[] = {
+ const uint8_t pltData[] = {
0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4)
0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8)
0x90, 0x90, 0x90, 0x90, // nop
};
- memcpy(Buf, PltData, sizeof(PltData));
- uint32_t GotPlt = In.GotPlt->getVA();
- write32le(Buf + 2, GotPlt + 4);
- write32le(Buf + 8, GotPlt + 8);
+ memcpy(buf, pltData, sizeof(pltData));
+ uint32_t gotPlt = in.gotPlt->getVA();
+ write32le(buf + 2, gotPlt + 4);
+ write32le(buf + 8, gotPlt + 8);
}
-void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Inst[] = {
- 0xff, 0x00, 0, 0, 0, 0, // jmp *foo_in_GOT or jmp *foo@GOT(%ebx)
- 0x68, 0, 0, 0, 0, // pushl $reloc_offset
- 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
- };
- memcpy(Buf, Inst, sizeof(Inst));
-
- if (Config->Pic) {
- // jmp *foo@GOT(%ebx)
- uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
- Buf[1] = 0xa3;
- write32le(Buf + 2, GotPltEntryAddr - Ebx);
+void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ if (config->isPic) {
+ const uint8_t inst[] = {
+ 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
+ 0x68, 0, 0, 0, 0, // pushl $reloc_offset
+ 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
+ };
+ memcpy(buf, inst, sizeof(inst));
+ write32le(buf + 2, gotPltEntryAddr - in.gotPlt->getVA());
} else {
- // jmp *foo_in_GOT
- Buf[1] = 0x25;
- write32le(Buf + 2, GotPltEntryAddr);
+ const uint8_t inst[] = {
+ 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
+ 0x68, 0, 0, 0, 0, // pushl $reloc_offset
+ 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
+ };
+ memcpy(buf, inst, sizeof(inst));
+ write32le(buf + 2, gotPltEntryAddr);
}
- write32le(Buf + 7, RelOff);
- write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
+ write32le(buf + 7, relOff);
+ write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16);
}
-int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
- switch (Type) {
+int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
+ switch (type) {
case R_386_8:
case R_386_PC8:
- return SignExtend64<8>(*Buf);
+ return SignExtend64<8>(*buf);
case R_386_16:
case R_386_PC16:
- return SignExtend64<16>(read16le(Buf));
+ return SignExtend64<16>(read16le(buf));
case R_386_32:
case R_386_GOT32:
case R_386_GOT32X:
@@ -248,28 +254,28 @@ int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
case R_386_PLT32:
case R_386_TLS_LDO_32:
case R_386_TLS_LE:
- return SignExtend64<32>(read32le(Buf));
+ return SignExtend64<32>(read32le(buf));
default:
return 0;
}
}
-void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_386_8:
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
// being used for some 16-bit programs such as boot loaders, so
// we want to support them.
- checkIntUInt(Loc, Val, 8, Type);
- *Loc = Val;
+ checkIntUInt(loc, val, 8, type);
+ *loc = val;
break;
case R_386_PC8:
- checkInt(Loc, Val, 8, Type);
- *Loc = Val;
+ checkInt(loc, val, 8, type);
+ *loc = val;
break;
case R_386_16:
- checkIntUInt(Loc, Val, 16, Type);
- write16le(Loc, Val);
+ checkIntUInt(loc, val, 16, type);
+ write16le(loc, val);
break;
case R_386_PC16:
// R_386_PC16 is normally used with 16 bit code. In that situation
@@ -282,11 +288,10 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// current location subtracted from it.
// We just check that Val fits in 17 bits. This misses some cases, but
// should have no false positives.
- checkInt(Loc, Val, 17, Type);
- write16le(Loc, Val);
+ checkInt(loc, val, 17, type);
+ write16le(loc, val);
break;
case R_386_32:
- case R_386_GLOB_DAT:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTOFF:
@@ -305,86 +310,86 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_386_TLS_LE_32:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
- checkInt(Loc, Val, 32, Type);
- write32le(Loc, Val);
+ checkInt(loc, val, 32, type);
+ write32le(loc, val);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-void X86::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
// to
// movl %gs:0,%eax
// subl $x@ntpoff,%eax
- const uint8_t Inst[] = {
+ const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax
};
- memcpy(Loc - 3, Inst, sizeof(Inst));
- write32le(Loc + 5, Val);
+ memcpy(loc - 3, inst, sizeof(inst));
+ write32le(loc + 5, val);
}
-void X86::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
// to
// movl %gs:0, %eax
// addl x@gotntpoff(%ebx), %eax
- const uint8_t Inst[] = {
+ const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax
};
- memcpy(Loc - 3, Inst, sizeof(Inst));
- write32le(Loc + 5, Val);
+ memcpy(loc - 3, inst, sizeof(inst));
+ write32le(loc + 5, val);
}
// In some conditions, relocations can be optimized to avoid using GOT.
// This function does that for Initial Exec to Local Exec case.
-void X86::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
// Ulrich's document section 6.2 says that @gotntpoff can
// be used with MOVL or ADDL instructions.
// @indntpoff is similar to @gotntpoff, but for use in
// position dependent code.
- uint8_t Reg = (Loc[-1] >> 3) & 7;
+ uint8_t reg = (loc[-1] >> 3) & 7;
- if (Type == R_386_TLS_IE) {
- if (Loc[-1] == 0xa1) {
+ if (type == R_386_TLS_IE) {
+ if (loc[-1] == 0xa1) {
// "movl foo@indntpoff,%eax" -> "movl $foo,%eax"
// This case is different from the generic case below because
// this is a 5 byte instruction while below is 6 bytes.
- Loc[-1] = 0xb8;
- } else if (Loc[-2] == 0x8b) {
+ loc[-1] = 0xb8;
+ } else if (loc[-2] == 0x8b) {
// "movl foo@indntpoff,%reg" -> "movl $foo,%reg"
- Loc[-2] = 0xc7;
- Loc[-1] = 0xc0 | Reg;
+ loc[-2] = 0xc7;
+ loc[-1] = 0xc0 | reg;
} else {
// "addl foo@indntpoff,%reg" -> "addl $foo,%reg"
- Loc[-2] = 0x81;
- Loc[-1] = 0xc0 | Reg;
+ loc[-2] = 0x81;
+ loc[-1] = 0xc0 | reg;
}
} else {
- assert(Type == R_386_TLS_GOTIE);
- if (Loc[-2] == 0x8b) {
+ assert(type == R_386_TLS_GOTIE);
+ if (loc[-2] == 0x8b) {
// "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg"
- Loc[-2] = 0xc7;
- Loc[-1] = 0xc0 | Reg;
+ loc[-2] = 0xc7;
+ loc[-1] = 0xc0 | reg;
} else {
// "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg"
- Loc[-2] = 0x8d;
- Loc[-1] = 0x80 | (Reg << 3) | Reg;
+ loc[-2] = 0x8d;
+ loc[-1] = 0x80 | (reg << 3) | reg;
}
}
- write32le(Loc, Val);
+ write32le(loc, val);
}
-void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
- if (Type == R_386_TLS_LDO_32) {
- write32le(Loc, Val);
+void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ if (type == R_386_TLS_LDO_32) {
+ write32le(loc, val);
return;
}
@@ -395,48 +400,48 @@ void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// movl %gs:0,%eax
// nop
// leal 0(%esi,1),%esi
- const uint8_t Inst[] = {
+ const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
0x90, // nop
0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi
};
- memcpy(Loc - 2, Inst, sizeof(Inst));
+ memcpy(loc - 2, inst, sizeof(inst));
}
namespace {
class RetpolinePic : public X86 {
public:
RetpolinePic();
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
class RetpolineNoPic : public X86 {
public:
RetpolineNoPic();
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
} // namespace
RetpolinePic::RetpolinePic() {
- PltHeaderSize = 48;
- PltEntrySize = 32;
+ pltHeaderSize = 48;
+ pltEntrySize = 32;
}
-void RetpolinePic::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
- write32le(Buf, S.getPltVA() + 17);
+void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ write32le(buf, s.getPltVA() + 17);
}
-void RetpolinePic::writePltHeader(uint8_t *Buf) const {
- const uint8_t Insn[] = {
- 0xff, 0xb3, 0, 0, 0, 0, // 0: pushl GOTPLT+4(%ebx)
+void RetpolinePic::writePltHeader(uint8_t *buf) const {
+ const uint8_t insn[] = {
+ 0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx)
0x50, // 6: pushl %eax
- 0x8b, 0x83, 0, 0, 0, 0, // 7: mov GOTPLT+8(%ebx), %eax
+ 0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax
0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next
0xf3, 0x90, // 12: loop: pause
0x0f, 0xae, 0xe8, // 14: lfence
@@ -450,18 +455,13 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const {
0xc3, // 2e: ret
0xcc, // 2f: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
-
- uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
- uint32_t GotPlt = In.GotPlt->getVA() - Ebx;
- write32le(Buf + 2, GotPlt + 4);
- write32le(Buf + 9, GotPlt + 8);
+ memcpy(buf, insn, sizeof(insn));
}
-void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Insn[] = {
+void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t insn[] = {
0x50, // pushl %eax
0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax
0xe8, 0, 0, 0, 0, // call plt+0x20
@@ -470,28 +470,28 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
0xe9, 0, 0, 0, 0, // jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
-
- uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
- unsigned Off = getPltEntryOffset(Index);
- write32le(Buf + 3, GotPltEntryAddr - Ebx);
- write32le(Buf + 8, -Off - 12 + 32);
- write32le(Buf + 13, -Off - 17 + 18);
- write32le(Buf + 18, RelOff);
- write32le(Buf + 23, -Off - 27);
+ memcpy(buf, insn, sizeof(insn));
+
+ uint32_t ebx = in.gotPlt->getVA();
+ unsigned off = pltHeaderSize + pltEntrySize * index;
+ write32le(buf + 3, gotPltEntryAddr - ebx);
+ write32le(buf + 8, -off - 12 + 32);
+ write32le(buf + 13, -off - 17 + 18);
+ write32le(buf + 18, relOff);
+ write32le(buf + 23, -off - 27);
}
RetpolineNoPic::RetpolineNoPic() {
- PltHeaderSize = 48;
- PltEntrySize = 32;
+ pltHeaderSize = 48;
+ pltEntrySize = 32;
}
-void RetpolineNoPic::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
- write32le(Buf, S.getPltVA() + 16);
+void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ write32le(buf, s.getPltVA() + 16);
}
-void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
- const uint8_t Insn[] = {
+void RetpolineNoPic::writePltHeader(uint8_t *buf) const {
+ const uint8_t insn[] = {
0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4
0x50, // 6: pushl %eax
0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax
@@ -509,17 +509,17 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
0xc3, // 2e: ret
0xcc, // 2f: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
- uint32_t GotPlt = In.GotPlt->getVA();
- write32le(Buf + 2, GotPlt + 4);
- write32le(Buf + 8, GotPlt + 8);
+ uint32_t gotPlt = in.gotPlt->getVA();
+ write32le(buf + 2, gotPlt + 4);
+ write32le(buf + 8, gotPlt + 8);
}
-void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Insn[] = {
+void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t insn[] = {
0x50, // 0: pushl %eax
0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax
0xe8, 0, 0, 0, 0, // 6: call plt+0x20
@@ -529,26 +529,26 @@ void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
0xcc, // 1f: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
-
- unsigned Off = getPltEntryOffset(Index);
- write32le(Buf + 2, GotPltEntryAddr);
- write32le(Buf + 7, -Off - 11 + 32);
- write32le(Buf + 12, -Off - 16 + 17);
- write32le(Buf + 17, RelOff);
- write32le(Buf + 22, -Off - 26);
+ memcpy(buf, insn, sizeof(insn));
+
+ unsigned off = pltHeaderSize + pltEntrySize * index;
+ write32le(buf + 2, gotPltEntryAddr);
+ write32le(buf + 7, -off - 11 + 32);
+ write32le(buf + 12, -off - 16 + 17);
+ write32le(buf + 17, relOff);
+ write32le(buf + 22, -off - 26);
}
TargetInfo *elf::getX86TargetInfo() {
- if (Config->ZRetpolineplt) {
- if (Config->Pic) {
- static RetpolinePic T;
- return &T;
+ if (config->zRetpolineplt) {
+ if (config->isPic) {
+ static RetpolinePic t;
+ return &t;
}
- static RetpolineNoPic T;
- return &T;
+ static RetpolineNoPic t;
+ return &t;
}
- static X86 T;
- return &T;
+ static X86 t;
+ return &t;
}
diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp
index 06314155dcc92..de67aa5c33dc4 100644
--- a/ELF/Arch/X86_64.cpp
+++ b/ELF/Arch/X86_64.cpp
@@ -1,9 +1,8 @@
//===- X86_64.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -23,71 +22,74 @@ using namespace lld;
using namespace lld::elf;
namespace {
-template <class ELFT> class X86_64 : public TargetInfo {
+class X86_64 : public TargetInfo {
public:
X86_64();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- RelType getDynRel(RelType Type) const override;
- void writeGotPltHeader(uint8_t *Buf) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
-
- RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const override;
- void relaxGot(uint8_t *Loc, uint64_t Val) const override;
- void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const override;
-
-private:
- void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
- uint8_t ModRm) const;
+ int getTlsGdRelaxSkip(RelType type) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotPltHeader(uint8_t *buf) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+
+ RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const override;
+ void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const override;
};
} // namespace
-template <class ELFT> X86_64<ELFT>::X86_64() {
- CopyRel = R_X86_64_COPY;
- GotRel = R_X86_64_GLOB_DAT;
- NoneRel = R_X86_64_NONE;
- PltRel = R_X86_64_JUMP_SLOT;
- RelativeRel = R_X86_64_RELATIVE;
- IRelativeRel = R_X86_64_IRELATIVE;
- TlsGotRel = R_X86_64_TPOFF64;
- TlsModuleIndexRel = R_X86_64_DTPMOD64;
- TlsOffsetRel = R_X86_64_DTPOFF64;
- GotEntrySize = 8;
- GotPltEntrySize = 8;
- PltEntrySize = 16;
- PltHeaderSize = 16;
- TlsGdRelaxSkip = 2;
- TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
+X86_64::X86_64() {
+ copyRel = R_X86_64_COPY;
+ gotRel = R_X86_64_GLOB_DAT;
+ noneRel = R_X86_64_NONE;
+ pltRel = R_X86_64_JUMP_SLOT;
+ relativeRel = R_X86_64_RELATIVE;
+ iRelativeRel = R_X86_64_IRELATIVE;
+ symbolicRel = R_X86_64_64;
+ tlsDescRel = R_X86_64_TLSDESC;
+ tlsGotRel = R_X86_64_TPOFF64;
+ tlsModuleIndexRel = R_X86_64_DTPMOD64;
+ tlsOffsetRel = R_X86_64_DTPOFF64;
+ pltEntrySize = 16;
+ pltHeaderSize = 16;
+ trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
// Align to the large page size (known as a superpage or huge page).
// FreeBSD automatically promotes large, superpage-aligned allocations.
- DefaultImageBase = 0x200000;
+ defaultImageBase = 0x200000;
}
-template <class ELFT>
-RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; }
+
+RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ if (type == R_X86_64_GOTTPOFF)
+ config->hasStaticTlsModel = true;
+
+ switch (type) {
case R_X86_64_8:
case R_X86_64_16:
case R_X86_64_32:
case R_X86_64_32S:
case R_X86_64_64:
+ return R_ABS;
case R_X86_64_DTPOFF32:
case R_X86_64_DTPOFF64:
- return R_ABS;
+ return R_DTPREL;
case R_X86_64_TPOFF32:
return R_TLS;
+ case R_X86_64_TLSDESC_CALL:
+ return R_TLSDESC_CALL;
case R_X86_64_TLSLD:
return R_TLSLD_PC;
case R_X86_64_TLSGD:
@@ -97,218 +99,280 @@ RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S,
return R_SIZE;
case R_X86_64_PLT32:
return R_PLT_PC;
+ case R_X86_64_PC8:
+ case R_X86_64_PC16:
case R_X86_64_PC32:
case R_X86_64_PC64:
return R_PC;
case R_X86_64_GOT32:
case R_X86_64_GOT64:
- return R_GOT_FROM_END;
+ return R_GOTPLT;
+ case R_X86_64_GOTPC32_TLSDESC:
+ return R_TLSDESC_PC;
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_GOTTPOFF:
return R_GOT_PC;
case R_X86_64_GOTOFF64:
- return R_GOTREL_FROM_END;
+ return R_GOTPLTREL;
case R_X86_64_GOTPC32:
case R_X86_64_GOTPC64:
- return R_GOTONLY_PC_FROM_END;
+ return R_GOTPLTONLY_PC;
case R_X86_64_NONE:
return R_NONE;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
-template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
+void X86_64::writeGotPltHeader(uint8_t *buf) const {
// The first entry holds the value of _DYNAMIC. It is not clear why that is
// required, but it is documented in the psabi and the glibc dynamic linker
// seems to use it (note that this is relevant for linking ld.so, not any
// other program).
- write64le(Buf, In.Dynamic->getVA());
+ write64le(buf, mainPart->dynamic->getVA());
}
-template <class ELFT>
-void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
+void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const {
// See comments in X86::writeGotPlt.
- write64le(Buf, S.getPltVA() + 6);
+ write64le(buf, s.getPltVA() + 6);
}
-template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const {
- const uint8_t PltData[] = {
+void X86_64::writePltHeader(uint8_t *buf) const {
+ const uint8_t pltData[] = {
0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip)
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
0x0f, 0x1f, 0x40, 0x00, // nop
};
- memcpy(Buf, PltData, sizeof(PltData));
- uint64_t GotPlt = In.GotPlt->getVA();
- uint64_t Plt = In.Plt->getVA();
- write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8
- write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16
+ memcpy(buf, pltData, sizeof(pltData));
+ uint64_t gotPlt = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
+ write32le(buf + 2, gotPlt - plt + 2); // GOTPLT+8
+ write32le(buf + 8, gotPlt - plt + 4); // GOTPLT+16
}
-template <class ELFT>
-void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Inst[] = {
+void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t inst[] = {
0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip)
0x68, 0, 0, 0, 0, // pushq <relocation index>
0xe9, 0, 0, 0, 0, // jmpq plt[0]
};
- memcpy(Buf, Inst, sizeof(Inst));
+ memcpy(buf, inst, sizeof(inst));
- write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6);
- write32le(Buf + 7, Index);
- write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
+ write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6);
+ write32le(buf + 7, index);
+ write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16);
}
-template <class ELFT> RelType X86_64<ELFT>::getDynRel(RelType Type) const {
- if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 ||
- Type == R_X86_64_SIZE64)
- return Type;
+RelType X86_64::getDynRel(RelType type) const {
+ if (type == R_X86_64_64 || type == R_X86_64_PC64 || type == R_X86_64_SIZE32 ||
+ type == R_X86_64_SIZE64)
+ return type;
return R_X86_64_NONE;
}
-template <class ELFT>
-void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
- // Convert
- // .byte 0x66
- // leaq x@tlsgd(%rip), %rdi
- // .word 0x6666
- // rex64
- // call __tls_get_addr@plt
- // to
- // mov %fs:0x0,%rax
- // lea x@tpoff,%rax
- const uint8_t Inst[] = {
- 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax
- 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax
- };
- memcpy(Loc - 4, Inst, sizeof(Inst));
-
- // The original code used a pc relative relocation and so we have to
- // compensate for the -4 in had in the addend.
- write32le(Loc + 8, Val + 4);
+void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ if (type == R_X86_64_TLSGD) {
+ // Convert
+ // .byte 0x66
+ // leaq x@tlsgd(%rip), %rdi
+ // .word 0x6666
+ // rex64
+ // call __tls_get_addr@plt
+ // to the following two instructions.
+ const uint8_t inst[] = {
+ 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00,
+ 0x00, 0x00, // mov %fs:0x0,%rax
+ 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax
+ };
+ memcpy(loc - 4, inst, sizeof(inst));
+
+ // The original code used a pc relative relocation and so we have to
+ // compensate for the -4 in had in the addend.
+ write32le(loc + 8, val + 4);
+ } else {
+ // Convert
+ // lea x@tlsgd(%rip), %rax
+ // call *(%rax)
+ // to the following two instructions.
+ assert(type == R_X86_64_GOTPC32_TLSDESC);
+ if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
+ error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
+ "in callq *x@tlsdesc(%rip), %rax");
+ return;
+ }
+ // movq $x@tpoff(%rip),%rax
+ loc[-2] = 0xc7;
+ loc[-1] = 0xc0;
+ write32le(loc, val + 4);
+ // xchg ax,ax
+ loc[4] = 0x66;
+ loc[5] = 0x90;
+ }
}
-template <class ELFT>
-void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
- // Convert
- // .byte 0x66
- // leaq x@tlsgd(%rip), %rdi
- // .word 0x6666
- // rex64
- // call __tls_get_addr@plt
- // to
- // mov %fs:0x0,%rax
- // addq x@tpoff,%rax
- const uint8_t Inst[] = {
- 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax
- 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@tpoff,%rax
- };
- memcpy(Loc - 4, Inst, sizeof(Inst));
-
- // Both code sequences are PC relatives, but since we are moving the constant
- // forward by 8 bytes we have to subtract the value by 8.
- write32le(Loc + 8, Val - 8);
+void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+ if (type == R_X86_64_TLSGD) {
+ // Convert
+ // .byte 0x66
+ // leaq x@tlsgd(%rip), %rdi
+ // .word 0x6666
+ // rex64
+ // call __tls_get_addr@plt
+ // to the following two instructions.
+ const uint8_t inst[] = {
+ 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00,
+ 0x00, 0x00, // mov %fs:0x0,%rax
+ 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax
+ };
+ memcpy(loc - 4, inst, sizeof(inst));
+
+ // Both code sequences are PC relatives, but since we are moving the
+ // constant forward by 8 bytes we have to subtract the value by 8.
+ write32le(loc + 8, val - 8);
+ } else {
+ // Convert
+ // lea x@tlsgd(%rip), %rax
+ // call *(%rax)
+ // to the following two instructions.
+ assert(type == R_X86_64_GOTPC32_TLSDESC);
+ if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
+ error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
+ "in callq *x@tlsdesc(%rip), %rax");
+ return;
+ }
+ // movq x@gottpoff(%rip),%rax
+ loc[-2] = 0x8b;
+ write32le(loc, val);
+ // xchg ax,ax
+ loc[4] = 0x66;
+ loc[5] = 0x90;
+ }
}
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
// R_X86_64_TPOFF32 so that it does not use GOT.
-template <class ELFT>
-void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
- uint8_t *Inst = Loc - 3;
- uint8_t Reg = Loc[-1] >> 3;
- uint8_t *RegSlot = Loc - 1;
+void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ uint8_t *inst = loc - 3;
+ uint8_t reg = loc[-1] >> 3;
+ uint8_t *regSlot = loc - 1;
// Note that ADD with RSP or R12 is converted to ADD instead of LEA
// because LEA with these registers needs 4 bytes to encode and thus
// wouldn't fit the space.
- if (memcmp(Inst, "\x48\x03\x25", 3) == 0) {
+ if (memcmp(inst, "\x48\x03\x25", 3) == 0) {
// "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp"
- memcpy(Inst, "\x48\x81\xc4", 3);
- } else if (memcmp(Inst, "\x4c\x03\x25", 3) == 0) {
+ memcpy(inst, "\x48\x81\xc4", 3);
+ } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) {
// "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12"
- memcpy(Inst, "\x49\x81\xc4", 3);
- } else if (memcmp(Inst, "\x4c\x03", 2) == 0) {
+ memcpy(inst, "\x49\x81\xc4", 3);
+ } else if (memcmp(inst, "\x4c\x03", 2) == 0) {
// "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]"
- memcpy(Inst, "\x4d\x8d", 2);
- *RegSlot = 0x80 | (Reg << 3) | Reg;
- } else if (memcmp(Inst, "\x48\x03", 2) == 0) {
+ memcpy(inst, "\x4d\x8d", 2);
+ *regSlot = 0x80 | (reg << 3) | reg;
+ } else if (memcmp(inst, "\x48\x03", 2) == 0) {
// "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg"
- memcpy(Inst, "\x48\x8d", 2);
- *RegSlot = 0x80 | (Reg << 3) | Reg;
- } else if (memcmp(Inst, "\x4c\x8b", 2) == 0) {
+ memcpy(inst, "\x48\x8d", 2);
+ *regSlot = 0x80 | (reg << 3) | reg;
+ } else if (memcmp(inst, "\x4c\x8b", 2) == 0) {
// "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]"
- memcpy(Inst, "\x49\xc7", 2);
- *RegSlot = 0xc0 | Reg;
- } else if (memcmp(Inst, "\x48\x8b", 2) == 0) {
+ memcpy(inst, "\x49\xc7", 2);
+ *regSlot = 0xc0 | reg;
+ } else if (memcmp(inst, "\x48\x8b", 2) == 0) {
// "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg"
- memcpy(Inst, "\x48\xc7", 2);
- *RegSlot = 0xc0 | Reg;
+ memcpy(inst, "\x48\xc7", 2);
+ *regSlot = 0xc0 | reg;
} else {
- error(getErrorLocation(Loc - 3) +
+ error(getErrorLocation(loc - 3) +
"R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only");
}
// The original code used a PC relative relocation.
// Need to compensate for the -4 it had in the addend.
- write32le(Loc, Val + 4);
+ write32le(loc, val + 4);
}
-template <class ELFT>
-void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
- // Convert
- // leaq bar@tlsld(%rip), %rdi
- // callq __tls_get_addr@PLT
- // leaq bar@dtpoff(%rax), %rcx
- // to
- // .word 0x6666
- // .byte 0x66
- // mov %fs:0,%rax
- // leaq bar@tpoff(%rax), %rcx
- if (Type == R_X86_64_DTPOFF64) {
- write64le(Loc, Val);
+void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ if (type == R_X86_64_DTPOFF64) {
+ write64le(loc, val);
return;
}
- if (Type == R_X86_64_DTPOFF32) {
- write32le(Loc, Val);
+ if (type == R_X86_64_DTPOFF32) {
+ write32le(loc, val);
return;
}
- const uint8_t Inst[] = {
+ const uint8_t inst[] = {
0x66, 0x66, // .word 0x6666
0x66, // .byte 0x66
0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax
};
- memcpy(Loc - 3, Inst, sizeof(Inst));
+
+ if (loc[4] == 0xe8) {
+ // Convert
+ // leaq bar@tlsld(%rip), %rdi # 48 8d 3d <Loc>
+ // callq __tls_get_addr@PLT # e8 <disp32>
+ // leaq bar@dtpoff(%rax), %rcx
+ // to
+ // .word 0x6666
+ // .byte 0x66
+ // mov %fs:0,%rax
+ // leaq bar@tpoff(%rax), %rcx
+ memcpy(loc - 3, inst, sizeof(inst));
+ return;
+ }
+
+ if (loc[4] == 0xff && loc[5] == 0x15) {
+ // Convert
+ // leaq x@tlsld(%rip),%rdi # 48 8d 3d <Loc>
+ // call *__tls_get_addr@GOTPCREL(%rip) # ff 15 <disp32>
+ // to
+ // .long 0x66666666
+ // movq %fs:0,%rax
+ // See "Table 11.9: LD -> LE Code Transition (LP64)" in
+ // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf
+ loc[-3] = 0x66;
+ memcpy(loc - 2, inst, sizeof(inst));
+ return;
+ }
+
+ error(getErrorLocation(loc - 3) +
+ "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD");
}
-template <class ELFT>
-void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_X86_64_8:
- checkUInt(Loc, Val, 8, Type);
- *Loc = Val;
+ checkIntUInt(loc, val, 8, type);
+ *loc = val;
+ break;
+ case R_X86_64_PC8:
+ checkInt(loc, val, 8, type);
+ *loc = val;
break;
case R_X86_64_16:
- checkUInt(Loc, Val, 16, Type);
- write16le(Loc, Val);
+ checkIntUInt(loc, val, 16, type);
+ write16le(loc, val);
+ break;
+ case R_X86_64_PC16:
+ checkInt(loc, val, 16, type);
+ write16le(loc, val);
break;
case R_X86_64_32:
- checkUInt(Loc, Val, 32, Type);
- write32le(Loc, Val);
+ checkUInt(loc, val, 32, type);
+ write32le(loc, val);
break;
case R_X86_64_32S:
case R_X86_64_TPOFF32:
case R_X86_64_GOT32:
case R_X86_64_GOTPC32:
+ case R_X86_64_GOTPC32_TLSDESC:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
@@ -319,49 +383,47 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_SIZE32:
- checkInt(Loc, Val, 32, Type);
- write32le(Loc, Val);
+ checkInt(loc, val, 32, type);
+ write32le(loc, val);
break;
case R_X86_64_64:
case R_X86_64_DTPOFF64:
- case R_X86_64_GLOB_DAT:
case R_X86_64_PC64:
case R_X86_64_SIZE64:
case R_X86_64_GOT64:
case R_X86_64_GOTOFF64:
case R_X86_64_GOTPC64:
- write64le(Loc, Val);
+ write64le(loc, val);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-template <class ELFT>
-RelExpr X86_64<ELFT>::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr RelExpr) const {
- if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX)
- return RelExpr;
- const uint8_t Op = Data[-2];
- const uint8_t ModRm = Data[-1];
+RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr relExpr) const {
+ if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX)
+ return relExpr;
+ const uint8_t op = data[-2];
+ const uint8_t modRm = data[-1];
// FIXME: When PIC is disabled and foo is defined locally in the
// lower 32 bit address space, memory operand in mov can be converted into
// immediate operand. Otherwise, mov must be changed to lea. We support only
// latter relaxation at this moment.
- if (Op == 0x8b)
+ if (op == 0x8b)
return R_RELAX_GOT_PC;
// Relax call and jmp.
- if (Op == 0xff && (ModRm == 0x15 || ModRm == 0x25))
+ if (op == 0xff && (modRm == 0x15 || modRm == 0x25))
return R_RELAX_GOT_PC;
// Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor.
// If PIC then no relaxation is available.
// We also don't relax test/binop instructions without REX byte,
// they are 32bit operations and not common to have.
- assert(Type == R_X86_64_REX_GOTPCRELX);
- return Config->Pic ? RelExpr : R_RELAX_GOT_PC_NOPIC;
+ assert(type == R_X86_64_REX_GOTPCRELX);
+ return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC;
}
// A subset of relaxations can only be applied for no-PIC. This method
@@ -369,12 +431,11 @@ RelExpr X86_64<ELFT>::adjustRelaxExpr(RelType Type, const uint8_t *Data,
// "Intel 64 and IA-32 Architectures Software Developer's Manual V2"
// (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/
// 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf)
-template <class ELFT>
-void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
- uint8_t ModRm) const {
- const uint8_t Rex = Loc[-3];
+static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op,
+ uint8_t modRm) {
+ const uint8_t rex = loc[-3];
// Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg".
- if (Op == 0x85) {
+ if (op == 0x85) {
// See "TEST-Logical Compare" (4-428 Vol. 2B),
// TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension).
@@ -391,11 +452,11 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
// 0x38 == 00 111 000 binary.
// We transfer reg2 to reg1 here as operand.
// See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3).
- Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3; // ModR/M byte.
+ loc[-1] = 0xc0 | (modRm & 0x38) >> 3; // ModR/M byte.
// Change opcode from TEST r/m64, r64 to TEST r/m64, imm32
// See "TEST-Logical Compare" (4-428 Vol. 2B).
- Loc[-2] = 0xf7;
+ loc[-2] = 0xf7;
// Move R bit to the B bit in REX byte.
// REX byte is encoded as 0100WRXB, where
@@ -408,8 +469,8 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
// REX.B This 1-bit value is an extension to the MODRM.rm field or the
// SIB.base field.
// See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A).
- Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2;
- write32le(Loc, Val);
+ loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2;
+ write32le(loc, val);
return;
}
@@ -419,7 +480,7 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
// Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg".
// Logic is close to one for test instruction above, but we also
// write opcode extension here, see below for details.
- Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3 | (Op & 0x3c); // ModR/M byte.
+ loc[-1] = 0xc0 | (modRm & 0x38) >> 3 | (op & 0x3c); // ModR/M byte.
// Primary opcode is 0x81, opcode extension is one of:
// 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB,
@@ -428,69 +489,67 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
// See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15),
// "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for
// descriptions about each operation.
- Loc[-2] = 0x81;
- Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2;
- write32le(Loc, Val);
+ loc[-2] = 0x81;
+ loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2;
+ write32le(loc, val);
}
-template <class ELFT>
-void X86_64<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const {
- const uint8_t Op = Loc[-2];
- const uint8_t ModRm = Loc[-1];
+void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
+ const uint8_t op = loc[-2];
+ const uint8_t modRm = loc[-1];
// Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".
- if (Op == 0x8b) {
- Loc[-2] = 0x8d;
- write32le(Loc, Val);
+ if (op == 0x8b) {
+ loc[-2] = 0x8d;
+ write32le(loc, val);
return;
}
- if (Op != 0xff) {
+ if (op != 0xff) {
// We are relaxing a rip relative to an absolute, so compensate
// for the old -4 addend.
- assert(!Config->Pic);
- relaxGotNoPic(Loc, Val + 4, Op, ModRm);
+ assert(!config->isPic);
+ relaxGotNoPic(loc, val + 4, op, modRm);
return;
}
// Convert call/jmp instructions.
- if (ModRm == 0x15) {
+ if (modRm == 0x15) {
// ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo".
// Instead we convert to "addr32 call foo" where addr32 is an instruction
// prefix. That makes result expression to be a single instruction.
- Loc[-2] = 0x67; // addr32 prefix
- Loc[-1] = 0xe8; // call
- write32le(Loc, Val);
+ loc[-2] = 0x67; // addr32 prefix
+ loc[-1] = 0xe8; // call
+ write32le(loc, val);
return;
}
// Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop".
// jmp doesn't return, so it is fine to use nop here, it is just a stub.
- assert(ModRm == 0x25);
- Loc[-2] = 0xe9; // jmp
- Loc[3] = 0x90; // nop
- write32le(Loc - 1, Val + 1);
+ assert(modRm == 0x25);
+ loc[-2] = 0xe9; // jmp
+ loc[3] = 0x90; // nop
+ write32le(loc - 1, val + 1);
}
-// This anonymous namespace works around a warning bug in
-// old versions of gcc. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
-namespace {
-
// A split-stack prologue starts by checking the amount of stack remaining
// in one of two ways:
// A) Comparing of the stack pointer to a field in the tcb.
// B) Or a load of a stack pointer offset with an lea to r10 or r11.
-template <>
-bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
- uint8_t *End,
- uint8_t StOther) const {
- if (Loc + 8 >= End)
+bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const {
+ if (!config->is64) {
+ error("Target doesn't support split stacks.");
+ return false;
+ }
+
+ if (loc + 8 >= end)
return false;
// Replace "cmp %fs:0x70,%rsp" and subsequent branch
// with "stc, nopl 0x0(%rax,%rax,1)"
- if (memcmp(Loc, "\x64\x48\x3b\x24\x25", 5) == 0) {
- memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8);
+ if (memcmp(loc, "\x64\x48\x3b\x24\x25", 5) == 0) {
+ memcpy(loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8);
return true;
}
@@ -498,25 +557,16 @@ bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
// be r10 or r11. The lea instruction feeds a subsequent compare which checks
// if there is X available stack space. Making X larger effectively reserves
// that much additional space. The stack grows downward so subtract the value.
- if (memcmp(Loc, "\x4c\x8d\x94\x24", 4) == 0 ||
- memcmp(Loc, "\x4c\x8d\x9c\x24", 4) == 0) {
+ if (memcmp(loc, "\x4c\x8d\x94\x24", 4) == 0 ||
+ memcmp(loc, "\x4c\x8d\x9c\x24", 4) == 0) {
// The offset bytes are encoded four bytes after the start of the
// instruction.
- write32le(Loc + 4, read32le(Loc + 4) - 0x4000);
+ write32le(loc + 4, read32le(loc + 4) - 0x4000);
return true;
}
return false;
}
-template <>
-bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
- uint8_t *End,
- uint8_t StOther) const {
- llvm_unreachable("Target doesn't support split stacks.");
-}
-
-} // namespace
-
// These nonstandard PLT entries are to migtigate Spectre v2 security
// vulnerability. In order to mitigate Spectre v2, we want to avoid indirect
// branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT
@@ -527,37 +577,36 @@ bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
// is specified, all dynamic symbols are resolved at load-time. Thus, when
// that option is given, we can omit code for symbol lazy resolution.
namespace {
-template <class ELFT> class Retpoline : public X86_64<ELFT> {
+class Retpoline : public X86_64 {
public:
Retpoline();
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
-template <class ELFT> class RetpolineZNow : public X86_64<ELFT> {
+class RetpolineZNow : public X86_64 {
public:
RetpolineZNow();
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override {}
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override {}
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
} // namespace
-template <class ELFT> Retpoline<ELFT>::Retpoline() {
- TargetInfo::PltHeaderSize = 48;
- TargetInfo::PltEntrySize = 32;
+Retpoline::Retpoline() {
+ pltHeaderSize = 48;
+ pltEntrySize = 32;
}
-template <class ELFT>
-void Retpoline<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
- write64le(Buf, S.getPltVA() + 17);
+void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ write64le(buf, s.getPltVA() + 17);
}
-template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const {
- const uint8_t Insn[] = {
+void Retpoline::writePltHeader(uint8_t *buf) const {
+ const uint8_t insn[] = {
0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip)
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11
0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next
@@ -570,19 +619,18 @@ template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const {
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding
0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
- uint64_t GotPlt = In.GotPlt->getVA();
- uint64_t Plt = In.Plt->getVA();
- write32le(Buf + 2, GotPlt - Plt - 6 + 8);
- write32le(Buf + 9, GotPlt - Plt - 13 + 16);
+ uint64_t gotPlt = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
+ write32le(buf + 2, gotPlt - plt - 6 + 8);
+ write32le(buf + 9, gotPlt - plt - 13 + 16);
}
-template <class ELFT>
-void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Insn[] = {
+void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t insn[] = {
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11
0xe8, 0, 0, 0, 0, // 7: callq plt+0x20
0xe9, 0, 0, 0, 0, // c: jmp plt+0x12
@@ -590,25 +638,24 @@ void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
0xe9, 0, 0, 0, 0, // 16: jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
- uint64_t Off = getPltEntryOffset(Index);
+ uint64_t off = pltHeaderSize + pltEntrySize * index;
- write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
- write32le(Buf + 8, -Off - 12 + 32);
- write32le(Buf + 13, -Off - 17 + 18);
- write32le(Buf + 18, Index);
- write32le(Buf + 23, -Off - 27);
+ write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7);
+ write32le(buf + 8, -off - 12 + 32);
+ write32le(buf + 13, -off - 17 + 18);
+ write32le(buf + 18, index);
+ write32le(buf + 23, -off - 27);
}
-template <class ELFT> RetpolineZNow<ELFT>::RetpolineZNow() {
- TargetInfo::PltHeaderSize = 32;
- TargetInfo::PltEntrySize = 16;
+RetpolineZNow::RetpolineZNow() {
+ pltHeaderSize = 32;
+ pltEntrySize = 16;
}
-template <class ELFT>
-void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const {
- const uint8_t Insn[] = {
+void RetpolineZNow::writePltHeader(uint8_t *buf) const {
+ const uint8_t insn[] = {
0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next
0xf3, 0x90, // 5: loop: pause
0x0f, 0xae, 0xe8, // 7: lfence
@@ -620,37 +667,35 @@ void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const {
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
0xcc, // 1f: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
}
-template <class ELFT>
-void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Insn[] = {
+void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t insn[] = {
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11
0xe9, 0, 0, 0, 0, // jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
- write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
- write32le(Buf + 8, -getPltEntryOffset(Index) - 12);
+ write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7);
+ write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12);
}
-template <class ELFT> static TargetInfo *getTargetInfo() {
- if (Config->ZRetpolineplt) {
- if (Config->ZNow) {
- static RetpolineZNow<ELFT> T;
- return &T;
+static TargetInfo *getTargetInfo() {
+ if (config->zRetpolineplt) {
+ if (config->zNow) {
+ static RetpolineZNow t;
+ return &t;
}
- static Retpoline<ELFT> T;
- return &T;
+ static Retpoline t;
+ return &t;
}
- static X86_64<ELFT> T;
- return &T;
+ static X86_64 t;
+ return &t;
}
-TargetInfo *elf::getX32TargetInfo() { return getTargetInfo<ELF32LE>(); }
-TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo<ELF64LE>(); }
+TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); }
diff --git a/ELF/Bits.h b/ELF/Bits.h
deleted file mode 100644
index 13d40322265ea..0000000000000
--- a/ELF/Bits.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//===- Bits.h ---------------------------------------------------*- C++ -*-===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_ELF_BITS_H
-#define LLD_ELF_BITS_H
-
-#include "Config.h"
-#include "llvm/Support/Endian.h"
-
-namespace lld {
-namespace elf {
-
-inline uint64_t readUint(uint8_t *Buf) {
- if (Config->Is64)
- return llvm::support::endian::read64(Buf, Config->Endianness);
- return llvm::support::endian::read32(Buf, Config->Endianness);
-}
-
-inline void writeUint(uint8_t *Buf, uint64_t Val) {
- if (Config->Is64)
- llvm::support::endian::write64(Buf, Val, Config->Endianness);
- else
- llvm::support::endian::write32(Buf, Val, Config->Endianness);
-}
-
-} // namespace elf
-} // namespace lld
-
-#endif
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
index a1c23b0d49ac7..70578746483df 100644
--- a/ELF/CMakeLists.txt
+++ b/ELF/CMakeLists.txt
@@ -27,7 +27,6 @@ add_lld_library(lldELF
Driver.cpp
DriverUtils.cpp
EhFrame.cpp
- Filesystem.cpp
ICF.cpp
InputFiles.cpp
InputSection.cpp
diff --git a/ELF/CallGraphSort.cpp b/ELF/CallGraphSort.cpp
index 2a7d78664b8e4..9aaadd4818336 100644
--- a/ELF/CallGraphSort.cpp
+++ b/ELF/CallGraphSort.cpp
@@ -1,9 +1,8 @@
//===- CallGraphSort.cpp --------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
@@ -52,24 +51,24 @@ using namespace lld::elf;
namespace {
struct Edge {
- int From;
- uint64_t Weight;
+ int from;
+ uint64_t weight;
};
struct Cluster {
- Cluster(int Sec, size_t S) : Sections{Sec}, Size(S) {}
+ Cluster(int sec, size_t s) : sections{sec}, size(s) {}
double getDensity() const {
- if (Size == 0)
+ if (size == 0)
return 0;
- return double(Weight) / double(Size);
+ return double(weight) / double(size);
}
- std::vector<int> Sections;
- size_t Size = 0;
- uint64_t Weight = 0;
- uint64_t InitialWeight = 0;
- Edge BestPred = {-1, 0};
+ std::vector<int> sections;
+ size_t size = 0;
+ uint64_t weight = 0;
+ uint64_t initialWeight = 0;
+ Edge bestPred = {-1, 0};
};
class CallGraphSort {
@@ -79,8 +78,8 @@ public:
DenseMap<const InputSectionBase *, int> run();
private:
- std::vector<Cluster> Clusters;
- std::vector<const InputSectionBase *> Sections;
+ std::vector<Cluster> clusters;
+ std::vector<const InputSectionBase *> sections;
void groupClusters();
};
@@ -93,30 +92,30 @@ constexpr int MAX_DENSITY_DEGRADATION = 8;
constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
} // end anonymous namespace
-typedef std::pair<const InputSectionBase *, const InputSectionBase *>
- SectionPair;
+using SectionPair =
+ std::pair<const InputSectionBase *, const InputSectionBase *>;
// Take the edge list in Config->CallGraphProfile, resolve symbol names to
// Symbols, and generate a graph between InputSections with the provided
// weights.
CallGraphSort::CallGraphSort() {
- MapVector<SectionPair, uint64_t> &Profile = Config->CallGraphProfile;
- DenseMap<const InputSectionBase *, int> SecToCluster;
-
- auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int {
- auto Res = SecToCluster.insert(std::make_pair(IS, Clusters.size()));
- if (Res.second) {
- Sections.push_back(IS);
- Clusters.emplace_back(Clusters.size(), IS->getSize());
+ MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
+ DenseMap<const InputSectionBase *, int> secToCluster;
+
+ auto getOrCreateNode = [&](const InputSectionBase *isec) -> int {
+ auto res = secToCluster.insert(std::make_pair(isec, clusters.size()));
+ if (res.second) {
+ sections.push_back(isec);
+ clusters.emplace_back(clusters.size(), isec->getSize());
}
- return Res.first->second;
+ return res.first->second;
};
// Create the graph.
- for (std::pair<SectionPair, uint64_t> &C : Profile) {
- const auto *FromSB = cast<InputSectionBase>(C.first.first->Repl);
- const auto *ToSB = cast<InputSectionBase>(C.first.second->Repl);
- uint64_t Weight = C.second;
+ for (std::pair<SectionPair, uint64_t> &c : profile) {
+ const auto *fromSB = cast<InputSectionBase>(c.first.first->repl);
+ const auto *toSB = cast<InputSectionBase>(c.first.second->repl);
+ uint64_t weight = c.second;
// Ignore edges between input sections belonging to different output
// sections. This is done because otherwise we would end up with clusters
@@ -124,110 +123,130 @@ CallGraphSort::CallGraphSort() {
// output. This messes with the cluster size and density calculations. We
// would also end up moving input sections in other output sections without
// moving them closer to what calls them.
- if (FromSB->getOutputSection() != ToSB->getOutputSection())
+ if (fromSB->getOutputSection() != toSB->getOutputSection())
continue;
- int From = GetOrCreateNode(FromSB);
- int To = GetOrCreateNode(ToSB);
+ int from = getOrCreateNode(fromSB);
+ int to = getOrCreateNode(toSB);
- Clusters[To].Weight += Weight;
+ clusters[to].weight += weight;
- if (From == To)
+ if (from == to)
continue;
// Remember the best edge.
- Cluster &ToC = Clusters[To];
- if (ToC.BestPred.From == -1 || ToC.BestPred.Weight < Weight) {
- ToC.BestPred.From = From;
- ToC.BestPred.Weight = Weight;
+ Cluster &toC = clusters[to];
+ if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) {
+ toC.bestPred.from = from;
+ toC.bestPred.weight = weight;
}
}
- for (Cluster &C : Clusters)
- C.InitialWeight = C.Weight;
+ for (Cluster &c : clusters)
+ c.initialWeight = c.weight;
}
// It's bad to merge clusters which would degrade the density too much.
-static bool isNewDensityBad(Cluster &A, Cluster &B) {
- double NewDensity = double(A.Weight + B.Weight) / double(A.Size + B.Size);
- return NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION;
+static bool isNewDensityBad(Cluster &a, Cluster &b) {
+ double newDensity = double(a.weight + b.weight) / double(a.size + b.size);
+ return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
}
-static void mergeClusters(Cluster &Into, Cluster &From) {
- Into.Sections.insert(Into.Sections.end(), From.Sections.begin(),
- From.Sections.end());
- Into.Size += From.Size;
- Into.Weight += From.Weight;
- From.Sections.clear();
- From.Size = 0;
- From.Weight = 0;
+static void mergeClusters(Cluster &into, Cluster &from) {
+ into.sections.insert(into.sections.end(), from.sections.begin(),
+ from.sections.end());
+ into.size += from.size;
+ into.weight += from.weight;
+ from.sections.clear();
+ from.size = 0;
+ from.weight = 0;
}
// Group InputSections into clusters using the Call-Chain Clustering heuristic
// then sort the clusters by density.
void CallGraphSort::groupClusters() {
- std::vector<int> SortedSecs(Clusters.size());
- std::vector<Cluster *> SecToCluster(Clusters.size());
+ std::vector<int> sortedSecs(clusters.size());
+ std::vector<Cluster *> secToCluster(clusters.size());
- for (size_t I = 0; I < Clusters.size(); ++I) {
- SortedSecs[I] = I;
- SecToCluster[I] = &Clusters[I];
+ for (size_t i = 0; i < clusters.size(); ++i) {
+ sortedSecs[i] = i;
+ secToCluster[i] = &clusters[i];
}
- std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) {
- return Clusters[B].getDensity() < Clusters[A].getDensity();
+ llvm::stable_sort(sortedSecs, [&](int a, int b) {
+ return clusters[a].getDensity() > clusters[b].getDensity();
});
- for (int SI : SortedSecs) {
- // Clusters[SI] is the same as SecToClusters[SI] here because it has not
+ for (int si : sortedSecs) {
+ // clusters[si] is the same as secToClusters[si] here because it has not
// been merged into another cluster yet.
- Cluster &C = Clusters[SI];
+ Cluster &c = clusters[si];
// Don't consider merging if the edge is unlikely.
- if (C.BestPred.From == -1 || C.BestPred.Weight * 10 <= C.InitialWeight)
+ if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
continue;
- Cluster *PredC = SecToCluster[C.BestPred.From];
- if (PredC == &C)
+ Cluster *predC = secToCluster[c.bestPred.from];
+ if (predC == &c)
continue;
- if (C.Size + PredC->Size > MAX_CLUSTER_SIZE)
+ if (c.size + predC->size > MAX_CLUSTER_SIZE)
continue;
- if (isNewDensityBad(*PredC, C))
+ if (isNewDensityBad(*predC, c))
continue;
// NOTE: Consider using a disjoint-set to track section -> cluster mapping
// if this is ever slow.
- for (int SI : C.Sections)
- SecToCluster[SI] = PredC;
+ for (int si : c.sections)
+ secToCluster[si] = predC;
- mergeClusters(*PredC, C);
+ mergeClusters(*predC, c);
}
// Remove empty or dead nodes. Invalidates all cluster indices.
- llvm::erase_if(Clusters, [](const Cluster &C) {
- return C.Size == 0 || C.Sections.empty();
+ llvm::erase_if(clusters, [](const Cluster &c) {
+ return c.size == 0 || c.sections.empty();
});
// Sort by density.
- std::stable_sort(Clusters.begin(), Clusters.end(),
- [](const Cluster &A, const Cluster &B) {
- return A.getDensity() > B.getDensity();
- });
+ llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) {
+ return a.getDensity() > b.getDensity();
+ });
}
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
groupClusters();
// Generate order.
- DenseMap<const InputSectionBase *, int> OrderMap;
- ssize_t CurOrder = 1;
+ DenseMap<const InputSectionBase *, int> orderMap;
+ ssize_t curOrder = 1;
+
+ for (const Cluster &c : clusters)
+ for (int secIndex : c.sections)
+ orderMap[sections[secIndex]] = curOrder++;
+
+ if (!config->printSymbolOrder.empty()) {
+ std::error_code ec;
+ raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None);
+ if (ec) {
+ error("cannot open " + config->printSymbolOrder + ": " + ec.message());
+ return orderMap;
+ }
- for (const Cluster &C : Clusters)
- for (int SecIndex : C.Sections)
- OrderMap[Sections[SecIndex]] = CurOrder++;
+ // Print the symbols ordered by C3, in the order of increasing curOrder
+ // Instead of sorting all the orderMap, just repeat the loops above.
+ for (const Cluster &c : clusters)
+ for (int secIndex : c.sections)
+ // Search all the symbols in the file of the section
+ // and find out a Defined symbol with name that is within the section.
+ for (Symbol *sym: sections[secIndex]->file->getSymbols())
+ if (!sym->isSection()) // Filter out section-type symbols here.
+ if (auto *d = dyn_cast<Defined>(sym))
+ if (sections[secIndex] == d->section)
+ os << sym->getName() << "\n";
+ }
- return OrderMap;
+ return orderMap;
}
// Sort sections by the profile data provided by -callgraph-profile-file
diff --git a/ELF/CallGraphSort.h b/ELF/CallGraphSort.h
index 3f96dc88f435b..5a092278a4164 100644
--- a/ELF/CallGraphSort.h
+++ b/ELF/CallGraphSort.h
@@ -1,9 +1,8 @@
//===- CallGraphSort.h ------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/ELF/Config.h b/ELF/Config.h
index 8fb760e592eb1..ff9d3dc0933c0 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -1,9 +1,8 @@
//===- Config.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -18,6 +17,7 @@
#include "llvm/Support/CachePruning.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Endian.h"
+#include <atomic>
#include <vector>
namespace lld {
@@ -62,18 +62,17 @@ enum class Target2Policy { Abs, Rel, GotRel };
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
struct SymbolVersion {
- llvm::StringRef Name;
- bool IsExternCpp;
- bool HasWildcard;
+ llvm::StringRef name;
+ bool isExternCpp;
+ bool hasWildcard;
};
// This struct contains symbols version definition that
// can be found in version script if it is used for link.
struct VersionDefinition {
- llvm::StringRef Name;
- uint16_t Id = 0;
- std::vector<SymbolVersion> Globals;
- size_t NameOff = 0; // Offset in the string table
+ llvm::StringRef name;
+ uint16_t id = 0;
+ std::vector<SymbolVersion> globals;
};
// This struct contains the global configuration for the linker.
@@ -81,162 +80,177 @@ struct VersionDefinition {
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Configuration {
- uint8_t OSABI = 0;
- llvm::CachePruningPolicy ThinLTOCachePolicy;
- llvm::StringMap<uint64_t> SectionStartMap;
- llvm::StringRef Chroot;
- llvm::StringRef DynamicLinker;
- llvm::StringRef DwoDir;
- llvm::StringRef Entry;
- llvm::StringRef Emulation;
- llvm::StringRef Fini;
- llvm::StringRef Init;
- llvm::StringRef LTOAAPipeline;
- llvm::StringRef LTONewPmPasses;
- llvm::StringRef LTOObjPath;
- llvm::StringRef LTOSampleProfile;
- llvm::StringRef MapFile;
- llvm::StringRef OutputFile;
- llvm::StringRef OptRemarksFilename;
- llvm::StringRef ProgName;
- llvm::StringRef SoName;
- llvm::StringRef Sysroot;
- llvm::StringRef ThinLTOCacheDir;
- llvm::StringRef ThinLTOIndexOnlyArg;
- std::pair<llvm::StringRef, llvm::StringRef> ThinLTOObjectSuffixReplace;
- std::pair<llvm::StringRef, llvm::StringRef> ThinLTOPrefixReplace;
- std::string Rpath;
- std::vector<VersionDefinition> VersionDefinitions;
- std::vector<llvm::StringRef> AuxiliaryList;
- std::vector<llvm::StringRef> FilterList;
- std::vector<llvm::StringRef> SearchPaths;
- std::vector<llvm::StringRef> SymbolOrderingFile;
- std::vector<llvm::StringRef> Undefined;
- std::vector<SymbolVersion> DynamicList;
- std::vector<SymbolVersion> VersionScriptGlobals;
- std::vector<SymbolVersion> VersionScriptLocals;
- std::vector<uint8_t> BuildIdVector;
+ uint8_t osabi = 0;
+ uint32_t andFeatures = 0;
+ llvm::CachePruningPolicy thinLTOCachePolicy;
+ llvm::StringMap<uint64_t> sectionStartMap;
+ llvm::StringRef chroot;
+ llvm::StringRef dynamicLinker;
+ llvm::StringRef dwoDir;
+ llvm::StringRef entry;
+ llvm::StringRef emulation;
+ llvm::StringRef fini;
+ llvm::StringRef init;
+ llvm::StringRef ltoAAPipeline;
+ llvm::StringRef ltoCSProfileFile;
+ llvm::StringRef ltoNewPmPasses;
+ llvm::StringRef ltoObjPath;
+ llvm::StringRef ltoSampleProfile;
+ llvm::StringRef mapFile;
+ llvm::StringRef outputFile;
+ llvm::StringRef optRemarksFilename;
+ llvm::StringRef optRemarksPasses;
+ llvm::StringRef optRemarksFormat;
+ llvm::StringRef progName;
+ llvm::StringRef printSymbolOrder;
+ llvm::StringRef soName;
+ llvm::StringRef sysroot;
+ llvm::StringRef thinLTOCacheDir;
+ llvm::StringRef thinLTOIndexOnlyArg;
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
+ std::string rpath;
+ std::vector<VersionDefinition> versionDefinitions;
+ std::vector<llvm::StringRef> auxiliaryList;
+ std::vector<llvm::StringRef> filterList;
+ std::vector<llvm::StringRef> searchPaths;
+ std::vector<llvm::StringRef> symbolOrderingFile;
+ std::vector<llvm::StringRef> undefined;
+ std::vector<SymbolVersion> dynamicList;
+ std::vector<SymbolVersion> versionScriptGlobals;
+ std::vector<SymbolVersion> versionScriptLocals;
+ std::vector<uint8_t> buildIdVector;
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
uint64_t>
- CallGraphProfile;
- bool AllowMultipleDefinition;
- bool AndroidPackDynRelocs;
- bool ARMHasBlx = false;
- bool ARMHasMovtMovw = false;
- bool ARMJ1J2BranchEncoding = false;
- bool AsNeeded = false;
- bool Bsymbolic;
- bool BsymbolicFunctions;
- bool CallGraphProfileSort;
- bool CheckSections;
- bool CompressDebugSections;
- bool Cref;
- bool DefineCommon;
- bool Demangle = true;
- bool DisableVerify;
- bool EhFrameHdr;
- bool EmitLLVM;
- bool EmitRelocs;
- bool EnableNewDtags;
- bool ExecuteOnly;
- bool ExportDynamic;
- bool FixCortexA53Errata843419;
- bool FormatBinary = false;
- bool GcSections;
- bool GdbIndex;
- bool GnuHash = false;
- bool GnuUnique;
- bool HasDynamicList = false;
- bool HasDynSymTab;
- bool IgnoreDataAddressEquality;
- bool IgnoreFunctionAddressEquality;
- bool LTODebugPassManager;
- bool LTONewPassManager;
- bool MergeArmExidx;
- bool MipsN32Abi = false;
- bool NoinhibitExec;
- bool Nostdlib;
- bool OFormatBinary;
- bool Omagic;
- bool OptRemarksWithHotness;
- bool Pie;
- bool PrintGcSections;
- bool PrintIcfSections;
- bool Relocatable;
- bool RelrPackDynRelocs;
- bool SaveTemps;
- bool SingleRoRx;
- bool Shared;
- bool Static = false;
- bool SysvHash = false;
- bool Target1Rel;
- bool Trace;
- bool ThinLTOEmitImportsFiles;
- bool ThinLTOIndexOnly;
- bool TocOptimize;
- bool UndefinedVersion;
- bool UseAndroidRelrTags = false;
- bool WarnBackrefs;
- bool WarnCommon;
- bool WarnIfuncTextrel;
- bool WarnMissingEntry;
- bool WarnSymbolOrdering;
- bool WriteAddends;
- bool ZCombreloc;
- bool ZCopyreloc;
- bool ZExecstack;
- bool ZGlobal;
- bool ZHazardplt;
- bool ZInitfirst;
- bool ZInterpose;
- bool ZKeepTextSectionPrefix;
- bool ZNodefaultlib;
- bool ZNodelete;
- bool ZNodlopen;
- bool ZNow;
- bool ZOrigin;
- bool ZRelro;
- bool ZRodynamic;
- bool ZText;
- bool ZRetpolineplt;
- bool ZWxneeded;
- DiscardPolicy Discard;
- ICFLevel ICF;
- OrphanHandlingPolicy OrphanHandling;
- SortSectionPolicy SortSection;
- StripPolicy Strip;
- UnresolvedPolicy UnresolvedSymbols;
- Target2Policy Target2;
- ARMVFPArgKind ARMVFPArgs = ARMVFPArgKind::Default;
- BuildIdKind BuildId = BuildIdKind::None;
- ELFKind EKind = ELFNoneKind;
- uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
- uint16_t EMachine = llvm::ELF::EM_NONE;
- llvm::Optional<uint64_t> ImageBase;
- uint64_t MaxPageSize;
- uint64_t MipsGotSize;
- uint64_t ZStackSize;
- unsigned LTOPartitions;
- unsigned LTOO;
- unsigned Optimize;
- unsigned ThinLTOJobs;
- int32_t SplitStackAdjustSize;
+ callGraphProfile;
+ bool allowMultipleDefinition;
+ bool allowShlibUndefined;
+ bool androidPackDynRelocs;
+ bool armHasBlx = false;
+ bool armHasMovtMovw = false;
+ bool armJ1J2BranchEncoding = false;
+ bool asNeeded = false;
+ bool bsymbolic;
+ bool bsymbolicFunctions;
+ bool callGraphProfileSort;
+ bool checkSections;
+ bool compressDebugSections;
+ bool cref;
+ bool defineCommon;
+ bool demangle = true;
+ bool dependentLibraries;
+ bool disableVerify;
+ bool ehFrameHdr;
+ bool emitLLVM;
+ bool emitRelocs;
+ bool enableNewDtags;
+ bool executeOnly;
+ bool exportDynamic;
+ bool fixCortexA53Errata843419;
+ bool forceBTI;
+ bool formatBinary = false;
+ bool requireCET;
+ bool gcSections;
+ bool gdbIndex;
+ bool gnuHash = false;
+ bool gnuUnique;
+ bool hasDynamicList = false;
+ bool hasDynSymTab;
+ bool ignoreDataAddressEquality;
+ bool ignoreFunctionAddressEquality;
+ bool ltoCSProfileGenerate;
+ bool ltoDebugPassManager;
+ bool ltoNewPassManager;
+ bool mergeArmExidx;
+ bool mipsN32Abi = false;
+ bool nmagic;
+ bool noinhibitExec;
+ bool nostdlib;
+ bool oFormatBinary;
+ bool omagic;
+ bool optRemarksWithHotness;
+ bool pacPlt;
+ bool picThunk;
+ bool pie;
+ bool printGcSections;
+ bool printIcfSections;
+ bool relocatable;
+ bool relrPackDynRelocs;
+ bool saveTemps;
+ bool singleRoRx;
+ bool shared;
+ bool isStatic = false;
+ bool sysvHash = false;
+ bool target1Rel;
+ bool trace;
+ bool thinLTOEmitImportsFiles;
+ bool thinLTOIndexOnly;
+ bool tocOptimize;
+ bool undefinedVersion;
+ bool useAndroidRelrTags = false;
+ bool warnBackrefs;
+ bool warnCommon;
+ bool warnIfuncTextrel;
+ bool warnMissingEntry;
+ bool warnSymbolOrdering;
+ bool writeAddends;
+ bool zCombreloc;
+ bool zCopyreloc;
+ bool zExecstack;
+ bool zGlobal;
+ bool zHazardplt;
+ bool zIfuncNoplt;
+ bool zInitfirst;
+ bool zInterpose;
+ bool zKeepTextSectionPrefix;
+ bool zNodefaultlib;
+ bool zNodelete;
+ bool zNodlopen;
+ bool zNow;
+ bool zOrigin;
+ bool zRelro;
+ bool zRodynamic;
+ bool zText;
+ bool zRetpolineplt;
+ bool zWxneeded;
+ DiscardPolicy discard;
+ ICFLevel icf;
+ OrphanHandlingPolicy orphanHandling;
+ SortSectionPolicy sortSection;
+ StripPolicy strip;
+ UnresolvedPolicy unresolvedSymbols;
+ Target2Policy target2;
+ ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
+ BuildIdKind buildId = BuildIdKind::None;
+ ELFKind ekind = ELFNoneKind;
+ uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
+ uint16_t emachine = llvm::ELF::EM_NONE;
+ llvm::Optional<uint64_t> imageBase;
+ uint64_t commonPageSize;
+ uint64_t maxPageSize;
+ uint64_t mipsGotSize;
+ uint64_t zStackSize;
+ unsigned ltoPartitions;
+ unsigned ltoo;
+ unsigned optimize;
+ unsigned thinLTOJobs;
+ int32_t splitStackAdjustSize;
// The following config options do not directly correspond to any
// particualr command line options.
// True if we need to pass through relocations in input files to the
// output file. Usually false because we consume relocations.
- bool CopyRelocs;
+ bool copyRelocs;
// True if the target is ELF64. False if ELF32.
- bool Is64;
+ bool is64;
// True if the target is little-endian. False if big-endian.
- bool IsLE;
+ bool isLE;
- // endianness::little if IsLE is true. endianness::big otherwise.
- llvm::support::endianness Endianness;
+ // endianness::little if isLE is true. endianness::big otherwise.
+ llvm::support::endianness endianness;
// True if the target is the little-endian MIPS64.
//
@@ -250,10 +264,24 @@ struct Configuration {
// name whatever that means. A fun hypothesis is that "EL" is short for
// little-endian written in the little-endian order, but I don't know
// if that's true.)
- bool IsMips64EL;
+ bool isMips64EL;
+
+ // True if we need to set the DF_STATIC_TLS flag to an output file,
+ // which works as a hint to the dynamic loader that the file contains
+ // code compiled with the static TLS model. The thread-local variable
+ // compiled with the static TLS model is faster but less flexible, and
+ // it may not be loaded using dlopen().
+ //
+ // We set this flag to true when we see a relocation for the static TLS
+ // model. Once this becomes true, it will never become false.
+ //
+ // Since the flag is updated by multi-threaded code, we use std::atomic.
+ // (Writing to a variable is not considered thread-safe even if the
+ // variable is boolean and we always set the same value from all threads.)
+ std::atomic<bool> hasStaticTlsModel{false};
// Holds set of ELF header flags for the target.
- uint32_t EFlags = 0;
+ uint32_t eflags = 0;
// The ELF spec defines two types of relocation table entries, RELA and
// REL. RELA is a triplet of (offset, info, addend) while REL is a
@@ -269,23 +297,23 @@ struct Configuration {
// Each ABI defines its relocation type. IsRela is true if target
// uses RELA. As far as we know, all 64-bit ABIs are using RELA. A
// few 32-bit ABIs are using RELA too.
- bool IsRela;
+ bool isRela;
// True if we are creating position-independent code.
- bool Pic;
+ bool isPic;
// 4 for ELF32, 8 for ELF64.
- int Wordsize;
+ int wordsize;
};
// The only instance of Configuration struct.
-extern Configuration *Config;
+extern Configuration *config;
-static inline void errorOrWarn(const Twine &Msg) {
- if (!Config->NoinhibitExec)
- error(Msg);
+static inline void errorOrWarn(const Twine &msg) {
+ if (!config->noinhibitExec)
+ error(msg);
else
- warn(Msg);
+ warn(msg);
}
} // namespace elf
} // namespace lld
diff --git a/ELF/DWARF.cpp b/ELF/DWARF.cpp
index 17e1a4d600eb5..1e4b36f71b540 100644
--- a/ELF/DWARF.cpp
+++ b/ELF/DWARF.cpp
@@ -1,9 +1,8 @@
//===- DWARF.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -26,81 +25,102 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) {
- for (InputSectionBase *Sec : Obj->getSections()) {
- if (!Sec)
+template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
+ for (InputSectionBase *sec : obj->getSections()) {
+ if (!sec)
continue;
- if (LLDDWARFSection *M =
- StringSwitch<LLDDWARFSection *>(Sec->Name)
- .Case(".debug_addr", &AddrSection)
- .Case(".debug_gnu_pubnames", &GnuPubNamesSection)
- .Case(".debug_gnu_pubtypes", &GnuPubTypesSection)
- .Case(".debug_info", &InfoSection)
- .Case(".debug_ranges", &RangeSection)
- .Case(".debug_rnglists", &RngListsSection)
- .Case(".debug_line", &LineSection)
+ if (LLDDWARFSection *m =
+ StringSwitch<LLDDWARFSection *>(sec->name)
+ .Case(".debug_addr", &addrSection)
+ .Case(".debug_gnu_pubnames", &gnuPubNamesSection)
+ .Case(".debug_gnu_pubtypes", &gnuPubTypesSection)
+ .Case(".debug_info", &infoSection)
+ .Case(".debug_ranges", &rangeSection)
+ .Case(".debug_rnglists", &rngListsSection)
+ .Case(".debug_line", &lineSection)
.Default(nullptr)) {
- M->Data = toStringRef(Sec->data());
- M->Sec = Sec;
+ m->Data = toStringRef(sec->data());
+ m->sec = sec;
continue;
}
- if (Sec->Name == ".debug_abbrev")
- AbbrevSection = toStringRef(Sec->data());
- else if (Sec->Name == ".debug_str")
- StrSection = toStringRef(Sec->data());
- else if (Sec->Name == ".debug_line_str")
- LineStringSection = toStringRef(Sec->data());
+ if (sec->name == ".debug_abbrev")
+ abbrevSection = toStringRef(sec->data());
+ else if (sec->name == ".debug_str")
+ strSection = toStringRef(sec->data());
+ else if (sec->name == ".debug_line_str")
+ lineStringSection = toStringRef(sec->data());
}
}
+namespace {
+template <class RelTy> struct LLDRelocationResolver {
+ // In the ELF ABIs, S sepresents the value of the symbol in the relocation
+ // entry. For Rela, the addend is stored as part of the relocation entry.
+ static uint64_t resolve(object::RelocationRef ref, uint64_t s,
+ uint64_t /* A */) {
+ return s + ref.getRawDataRefImpl().p;
+ }
+};
+
+template <class ELFT> struct LLDRelocationResolver<Elf_Rel_Impl<ELFT, false>> {
+ // For Rel, the addend A is supplied by the caller.
+ static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s,
+ uint64_t a) {
+ return s + a;
+ }
+};
+} // namespace
+
// Find if there is a relocation at Pos in Sec. The code is a bit
// more complicated than usual because we need to pass a section index
// to llvm since it has no idea about InputSection.
template <class ELFT>
template <class RelTy>
Optional<RelocAddrEntry>
-LLDDwarfObj<ELFT>::findAux(const InputSectionBase &Sec, uint64_t Pos,
- ArrayRef<RelTy> Rels) const {
- auto It = std::lower_bound(
- Rels.begin(), Rels.end(), Pos,
- [](const RelTy &A, uint64_t B) { return A.r_offset < B; });
- if (It == Rels.end() || It->r_offset != Pos)
+LLDDwarfObj<ELFT>::findAux(const InputSectionBase &sec, uint64_t pos,
+ ArrayRef<RelTy> rels) const {
+ auto it =
+ partition_point(rels, [=](const RelTy &a) { return a.r_offset < pos; });
+ if (it == rels.end() || it->r_offset != pos)
return None;
- const RelTy &Rel = *It;
+ const RelTy &rel = *it;
- const ObjFile<ELFT> *File = Sec.getFile<ELFT>();
- uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
- const typename ELFT::Sym &Sym = File->getELFSyms()[SymIndex];
- uint32_t SecIndex = File->getSectionIndex(Sym);
+ const ObjFile<ELFT> *file = sec.getFile<ELFT>();
+ uint32_t symIndex = rel.getSymbol(config->isMips64EL);
+ const typename ELFT::Sym &sym = file->template getELFSyms<ELFT>()[symIndex];
+ uint32_t secIndex = file->getSectionIndex(sym);
- // Broken debug info can point to a non-Defined symbol.
- auto *DR = dyn_cast<Defined>(&File->getRelocTargetSym(Rel));
- if (!DR) {
- RelType Type = Rel.getType(Config->IsMips64EL);
- if (Type != Target->NoneRel)
- error(toString(File) + ": relocation " + lld::toString(Type) + " at 0x" +
- llvm::utohexstr(Rel.r_offset) + " has unsupported target");
- return None;
- }
- uint64_t Val = DR->Value + getAddend<ELFT>(Rel);
+ // An undefined symbol may be a symbol defined in a discarded section. We
+ // shall still resolve it. This is important for --gdb-index: the end address
+ // offset of an entry in .debug_ranges is relocated. If it is not resolved,
+ // its zero value will terminate the decoding of .debug_ranges prematurely.
+ Symbol &s = file->getRelocTargetSym(rel);
+ uint64_t val = 0;
+ if (auto *dr = dyn_cast<Defined>(&s)) {
+ val = dr->value;
- // FIXME: We should be consistent about always adding the file
- // offset or not.
- if (DR->Section->Flags & ELF::SHF_ALLOC)
- Val += cast<InputSection>(DR->Section)->getOffsetInFile();
+ // FIXME: We should be consistent about always adding the file
+ // offset or not.
+ if (dr->section->flags & ELF::SHF_ALLOC)
+ val += cast<InputSection>(dr->section)->getOffsetInFile();
+ }
- return RelocAddrEntry{SecIndex, Val};
+ DataRefImpl d;
+ d.p = getAddend<ELFT>(rel);
+ return RelocAddrEntry{secIndex, RelocationRef(d, nullptr),
+ val, Optional<object::RelocationRef>(),
+ 0, LLDRelocationResolver<RelTy>::resolve};
}
template <class ELFT>
-Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &S,
- uint64_t Pos) const {
- auto &Sec = static_cast<const LLDDWARFSection &>(S);
- if (Sec.Sec->AreRelocsRela)
- return findAux(*Sec.Sec, Pos, Sec.Sec->template relas<ELFT>());
- return findAux(*Sec.Sec, Pos, Sec.Sec->template rels<ELFT>());
+Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s,
+ uint64_t pos) const {
+ auto &sec = static_cast<const LLDDWARFSection &>(s);
+ if (sec.sec->areRelocsRela)
+ return findAux(*sec.sec, pos, sec.sec->template relas<ELFT>());
+ return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>());
}
template class elf::LLDDwarfObj<ELF32LE>;
diff --git a/ELF/DWARF.h b/ELF/DWARF.h
index 8ecf02c77fb41..426022945007e 100644
--- a/ELF/DWARF.h
+++ b/ELF/DWARF.h
@@ -1,9 +1,8 @@
//===- DWARF.h -----------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===-------------------------------------------------------------------===//
@@ -21,70 +20,70 @@ namespace elf {
class InputSection;
struct LLDDWARFSection final : public llvm::DWARFSection {
- InputSectionBase *Sec = nullptr;
+ InputSectionBase *sec = nullptr;
};
template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
public:
- explicit LLDDwarfObj(ObjFile<ELFT> *Obj);
+ explicit LLDDwarfObj(ObjFile<ELFT> *obj);
void forEachInfoSections(
- llvm::function_ref<void(const llvm::DWARFSection &)> F) const override {
- F(InfoSection);
+ llvm::function_ref<void(const llvm::DWARFSection &)> f) const override {
+ f(infoSection);
}
const llvm::DWARFSection &getRangeSection() const override {
- return RangeSection;
+ return rangeSection;
}
const llvm::DWARFSection &getRnglistsSection() const override {
- return RngListsSection;
+ return rngListsSection;
}
const llvm::DWARFSection &getLineSection() const override {
- return LineSection;
+ return lineSection;
}
const llvm::DWARFSection &getAddrSection() const override {
- return AddrSection;
+ return addrSection;
}
const llvm::DWARFSection &getGnuPubNamesSection() const override {
- return GnuPubNamesSection;
+ return gnuPubNamesSection;
}
const llvm::DWARFSection &getGnuPubTypesSection() const override {
- return GnuPubTypesSection;
+ return gnuPubTypesSection;
}
StringRef getFileName() const override { return ""; }
- StringRef getAbbrevSection() const override { return AbbrevSection; }
- StringRef getStringSection() const override { return StrSection; }
- StringRef getLineStringSection() const override { return LineStringSection; }
+ StringRef getAbbrevSection() const override { return abbrevSection; }
+ StringRef getStringSection() const override { return strSection; }
+ StringRef getLineStringSection() const override { return lineStringSection; }
bool isLittleEndian() const override {
return ELFT::TargetEndianness == llvm::support::little;
}
- llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec,
- uint64_t Pos) const override;
+ llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &sec,
+ uint64_t pos) const override;
private:
template <class RelTy>
- llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec,
- uint64_t Pos,
- ArrayRef<RelTy> Rels) const;
-
- LLDDWARFSection GnuPubNamesSection;
- LLDDWARFSection GnuPubTypesSection;
- LLDDWARFSection InfoSection;
- LLDDWARFSection RangeSection;
- LLDDWARFSection RngListsSection;
- LLDDWARFSection LineSection;
- LLDDWARFSection AddrSection;
- StringRef AbbrevSection;
- StringRef StrSection;
- StringRef LineStringSection;
+ llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &sec,
+ uint64_t pos,
+ ArrayRef<RelTy> rels) const;
+
+ LLDDWARFSection gnuPubNamesSection;
+ LLDDWARFSection gnuPubTypesSection;
+ LLDDWARFSection infoSection;
+ LLDDWARFSection rangeSection;
+ LLDDWARFSection rngListsSection;
+ LLDDWARFSection lineSection;
+ LLDDWARFSection addrSection;
+ StringRef abbrevSection;
+ StringRef strSection;
+ StringRef lineStringSection;
};
} // namespace elf
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 13b6119e2dc91..fbfc71d22b7e5 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -1,9 +1,8 @@
//===- Driver.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -25,7 +24,6 @@
#include "Driver.h"
#include "Config.h"
-#include "Filesystem.h"
#include "ICF.h"
#include "InputFiles.h"
#include "InputSection.h"
@@ -41,6 +39,7 @@
#include "lld/Common/Args.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
@@ -51,6 +50,7 @@
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
+#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
@@ -68,44 +68,49 @@ using namespace llvm::support;
using namespace lld;
using namespace lld::elf;
-Configuration *elf::Config;
-LinkerDriver *elf::Driver;
+Configuration *elf::config;
+LinkerDriver *elf::driver;
-static void setConfigs(opt::InputArgList &Args);
+static void setConfigs(opt::InputArgList &args);
+static void readConfigs(opt::InputArgList &args);
-bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
- raw_ostream &Error) {
- errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
- errorHandler().ErrorLimitExceededMsg =
+bool elf::link(ArrayRef<const char *> args, bool canExitEarly,
+ raw_ostream &error) {
+ errorHandler().logName = args::getFilenameWithoutExe(args[0]);
+ errorHandler().errorLimitExceededMsg =
"too many errors emitted, stopping now (use "
"-error-limit=0 to see all errors)";
- errorHandler().ErrorOS = &Error;
- errorHandler().ExitEarly = CanExitEarly;
- errorHandler().ColorDiagnostics = Error.has_colors();
+ errorHandler().errorOS = &error;
+ errorHandler().exitEarly = canExitEarly;
+ errorHandler().colorDiagnostics = error.has_colors();
+
+ inputSections.clear();
+ outputSections.clear();
+ binaryFiles.clear();
+ bitcodeFiles.clear();
+ objectFiles.clear();
+ sharedFiles.clear();
+
+ config = make<Configuration>();
+ driver = make<LinkerDriver>();
+ script = make<LinkerScript>();
+ symtab = make<SymbolTable>();
- InputSections.clear();
- OutputSections.clear();
- BinaryFiles.clear();
- BitcodeFiles.clear();
- ObjectFiles.clear();
- SharedFiles.clear();
+ tar = nullptr;
+ memset(&in, 0, sizeof(in));
- Config = make<Configuration>();
- Driver = make<LinkerDriver>();
- Script = make<LinkerScript>();
- Symtab = make<SymbolTable>();
+ partitions = {Partition()};
- Tar = nullptr;
- memset(&In, 0, sizeof(In));
+ SharedFile::vernauxNum = 0;
- Config->ProgName = Args[0];
+ config->progName = args[0];
- Driver->main(Args);
+ driver->main(args);
// Exit immediately if we don't need to return to the caller.
// This saves time because the overhead of calling destructors
// for all globally-allocated objects is not negligible.
- if (CanExitEarly)
+ if (canExitEarly)
exitLld(errorCount() ? 1 : 0);
freeArena();
@@ -113,16 +118,16 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
}
// Parses a linker -m option.
-static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
- uint8_t OSABI = 0;
- StringRef S = Emul;
- if (S.endswith("_fbsd")) {
- S = S.drop_back(5);
- OSABI = ELFOSABI_FREEBSD;
+static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef emul) {
+ uint8_t osabi = 0;
+ StringRef s = emul;
+ if (s.endswith("_fbsd")) {
+ s = s.drop_back(5);
+ osabi = ELFOSABI_FREEBSD;
}
- std::pair<ELFKind, uint16_t> Ret =
- StringSwitch<std::pair<ELFKind, uint16_t>>(S)
+ std::pair<ELFKind, uint16_t> ret =
+ StringSwitch<std::pair<ELFKind, uint16_t>>(s)
.Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec",
{ELF64LEKind, EM_AARCH64})
.Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM})
@@ -130,7 +135,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS})
.Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
.Case("elf32lriscv", {ELF32LEKind, EM_RISCV})
- .Case("elf32ppc", {ELF32BEKind, EM_PPC})
+ .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC})
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
.Case("elf64lriscv", {ELF64LEKind, EM_RISCV})
@@ -141,91 +146,101 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
.Default({ELFNoneKind, EM_NONE});
- if (Ret.first == ELFNoneKind)
- error("unknown emulation: " + Emul);
- return std::make_tuple(Ret.first, Ret.second, OSABI);
+ if (ret.first == ELFNoneKind)
+ error("unknown emulation: " + emul);
+ return std::make_tuple(ret.first, ret.second, osabi);
}
// Returns slices of MB by parsing MB as an archive file.
// Each slice consists of a member file in the archive.
std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers(
- MemoryBufferRef MB) {
- std::unique_ptr<Archive> File =
- CHECK(Archive::create(MB),
- MB.getBufferIdentifier() + ": failed to parse archive");
-
- std::vector<std::pair<MemoryBufferRef, uint64_t>> V;
- Error Err = Error::success();
- bool AddToTar = File->isThin() && Tar;
- for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
- Archive::Child C =
- CHECK(COrErr, MB.getBufferIdentifier() +
+ MemoryBufferRef mb) {
+ std::unique_ptr<Archive> file =
+ CHECK(Archive::create(mb),
+ mb.getBufferIdentifier() + ": failed to parse archive");
+
+ std::vector<std::pair<MemoryBufferRef, uint64_t>> v;
+ Error err = Error::success();
+ bool addToTar = file->isThin() && tar;
+ for (const ErrorOr<Archive::Child> &cOrErr : file->children(err)) {
+ Archive::Child c =
+ CHECK(cOrErr, mb.getBufferIdentifier() +
": could not get the child of the archive");
- MemoryBufferRef MBRef =
- CHECK(C.getMemoryBufferRef(),
- MB.getBufferIdentifier() +
+ MemoryBufferRef mbref =
+ CHECK(c.getMemoryBufferRef(),
+ mb.getBufferIdentifier() +
": could not get the buffer for a child of the archive");
- if (AddToTar)
- Tar->append(relativeToRoot(check(C.getFullName())), MBRef.getBuffer());
- V.push_back(std::make_pair(MBRef, C.getChildOffset()));
+ if (addToTar)
+ tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer());
+ v.push_back(std::make_pair(mbref, c.getChildOffset()));
}
- if (Err)
- fatal(MB.getBufferIdentifier() + ": Archive::children failed: " +
- toString(std::move(Err)));
+ if (err)
+ fatal(mb.getBufferIdentifier() + ": Archive::children failed: " +
+ toString(std::move(err)));
// Take ownership of memory buffers created for members of thin archives.
- for (std::unique_ptr<MemoryBuffer> &MB : File->takeThinBuffers())
- make<std::unique_ptr<MemoryBuffer>>(std::move(MB));
+ for (std::unique_ptr<MemoryBuffer> &mb : file->takeThinBuffers())
+ make<std::unique_ptr<MemoryBuffer>>(std::move(mb));
- return V;
+ return v;
}
// Opens a file and create a file object. Path has to be resolved already.
-void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
+void LinkerDriver::addFile(StringRef path, bool withLOption) {
using namespace sys::fs;
- Optional<MemoryBufferRef> Buffer = readFile(Path);
- if (!Buffer.hasValue())
+ Optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer.hasValue())
return;
- MemoryBufferRef MBRef = *Buffer;
+ MemoryBufferRef mbref = *buffer;
- if (Config->FormatBinary) {
- Files.push_back(make<BinaryFile>(MBRef));
+ if (config->formatBinary) {
+ files.push_back(make<BinaryFile>(mbref));
return;
}
- switch (identify_magic(MBRef.getBuffer())) {
+ switch (identify_magic(mbref.getBuffer())) {
case file_magic::unknown:
- readLinkerScript(MBRef);
+ readLinkerScript(mbref);
return;
case file_magic::archive: {
// Handle -whole-archive.
- if (InWholeArchive) {
- for (const auto &P : getArchiveMembers(MBRef))
- Files.push_back(createObjectFile(P.first, Path, P.second));
+ if (inWholeArchive) {
+ for (const auto &p : getArchiveMembers(mbref))
+ files.push_back(createObjectFile(p.first, path, p.second));
return;
}
- std::unique_ptr<Archive> File =
- CHECK(Archive::create(MBRef), Path + ": failed to parse archive");
+ std::unique_ptr<Archive> file =
+ CHECK(Archive::create(mbref), path + ": failed to parse archive");
// If an archive file has no symbol table, it is likely that a user
// is attempting LTO and using a default ar command that doesn't
// understand the LLVM bitcode file. It is a pretty common error, so
// we'll handle it as if it had a symbol table.
- if (!File->isEmpty() && !File->hasSymbolTable()) {
- for (const auto &P : getArchiveMembers(MBRef))
- Files.push_back(make<LazyObjFile>(P.first, Path, P.second));
+ if (!file->isEmpty() && !file->hasSymbolTable()) {
+ // Check if all members are bitcode files. If not, ignore, which is the
+ // default action without the LTO hack described above.
+ for (const std::pair<MemoryBufferRef, uint64_t> &p :
+ getArchiveMembers(mbref))
+ if (identify_magic(p.first.getBuffer()) != file_magic::bitcode) {
+ error(path + ": archive has no index; run ranlib to add one");
+ return;
+ }
+
+ for (const std::pair<MemoryBufferRef, uint64_t> &p :
+ getArchiveMembers(mbref))
+ files.push_back(make<LazyObjFile>(p.first, path, p.second));
return;
}
// Handle the regular case.
- Files.push_back(make<ArchiveFile>(std::move(File)));
+ files.push_back(make<ArchiveFile>(std::move(file)));
return;
}
case file_magic::elf_shared_object:
- if (Config->Static || Config->Relocatable) {
- error("attempted static link of dynamic object " + Path);
+ if (config->isStatic || config->relocatable) {
+ error("attempted static link of dynamic object " + path);
return;
}
@@ -239,27 +254,27 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
// If a file was specified by -lfoo, the directory part is not
// significant, as a user did not specify it. This behavior is
// compatible with GNU.
- Files.push_back(
- createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path));
+ files.push_back(
+ make<SharedFile>(mbref, withLOption ? path::filename(path) : path));
return;
case file_magic::bitcode:
case file_magic::elf_relocatable:
- if (InLib)
- Files.push_back(make<LazyObjFile>(MBRef, "", 0));
+ if (inLib)
+ files.push_back(make<LazyObjFile>(mbref, "", 0));
else
- Files.push_back(createObjectFile(MBRef));
+ files.push_back(createObjectFile(mbref));
break;
default:
- error(Path + ": unknown file type");
+ error(path + ": unknown file type");
}
}
// Add a given library by searching it from input search paths.
-void LinkerDriver::addLibrary(StringRef Name) {
- if (Optional<std::string> Path = searchLibrary(Name))
- addFile(*Path, /*WithLOption=*/true);
+void LinkerDriver::addLibrary(StringRef name) {
+ if (Optional<std::string> path = searchLibrary(name))
+ addFile(*path, /*withLOption=*/true);
else
- error("unable to find library -l" + Name);
+ error("unable to find library -l" + name);
}
// This function is called on startup. We need this for LTO since
@@ -278,102 +293,117 @@ static void initLLVM() {
static void checkOptions() {
// The MIPS ABI as of 2016 does not support the GNU-style symbol lookup
// table which is a relatively new feature.
- if (Config->EMachine == EM_MIPS && Config->GnuHash)
+ if (config->emachine == EM_MIPS && config->gnuHash)
error("the .gnu.hash section is not compatible with the MIPS target");
- if (Config->FixCortexA53Errata843419 && Config->EMachine != EM_AARCH64)
+ if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64)
error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
- if (Config->TocOptimize && Config->EMachine != EM_PPC64)
+ if (config->tocOptimize && config->emachine != EM_PPC64)
error("--toc-optimize is only supported on the PowerPC64 target");
- if (Config->Pie && Config->Shared)
+ if (config->pie && config->shared)
error("-shared and -pie may not be used together");
- if (!Config->Shared && !Config->FilterList.empty())
+ if (!config->shared && !config->filterList.empty())
error("-F may not be used without -shared");
- if (!Config->Shared && !Config->AuxiliaryList.empty())
+ if (!config->shared && !config->auxiliaryList.empty())
error("-f may not be used without -shared");
- if (!Config->Relocatable && !Config->DefineCommon)
+ if (!config->relocatable && !config->defineCommon)
error("-no-define-common not supported in non relocatable output");
- if (Config->Relocatable) {
- if (Config->Shared)
+ if (config->zText && config->zIfuncNoplt)
+ error("-z text and -z ifunc-noplt may not be used together");
+
+ if (config->relocatable) {
+ if (config->shared)
error("-r and -shared may not be used together");
- if (Config->GcSections)
+ if (config->gcSections)
error("-r and --gc-sections may not be used together");
- if (Config->GdbIndex)
+ if (config->gdbIndex)
error("-r and --gdb-index may not be used together");
- if (Config->ICF != ICFLevel::None)
+ if (config->icf != ICFLevel::None)
error("-r and --icf may not be used together");
- if (Config->Pie)
+ if (config->pie)
error("-r and -pie may not be used together");
}
- if (Config->ExecuteOnly) {
- if (Config->EMachine != EM_AARCH64)
+ if (config->executeOnly) {
+ if (config->emachine != EM_AARCH64)
error("-execute-only is only supported on AArch64 targets");
- if (Config->SingleRoRx && !Script->HasSectionsCommand)
+ if (config->singleRoRx && !script->hasSectionsCommand)
error("-execute-only and -no-rosegment cannot be used together");
}
+
+ if (config->zRetpolineplt && config->requireCET)
+ error("--require-cet may not be used with -z retpolineplt");
+
+ if (config->emachine != EM_AARCH64) {
+ if (config->pacPlt)
+ error("--pac-plt only supported on AArch64");
+ if (config->forceBTI)
+ error("--force-bti only supported on AArch64");
+ }
}
-static const char *getReproduceOption(opt::InputArgList &Args) {
- if (auto *Arg = Args.getLastArg(OPT_reproduce))
- return Arg->getValue();
+static const char *getReproduceOption(opt::InputArgList &args) {
+ if (auto *arg = args.getLastArg(OPT_reproduce))
+ return arg->getValue();
return getenv("LLD_REPRODUCE");
}
-static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
- for (auto *Arg : Args.filtered(OPT_z))
- if (Key == Arg->getValue())
+static bool hasZOption(opt::InputArgList &args, StringRef key) {
+ for (auto *arg : args.filtered(OPT_z))
+ if (key == arg->getValue())
return true;
return false;
}
-static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2,
+static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2,
bool Default) {
- for (auto *Arg : Args.filtered_reverse(OPT_z)) {
- if (K1 == Arg->getValue())
+ for (auto *arg : args.filtered_reverse(OPT_z)) {
+ if (k1 == arg->getValue())
return true;
- if (K2 == Arg->getValue())
+ if (k2 == arg->getValue())
return false;
}
return Default;
}
-static bool isKnownZFlag(StringRef S) {
- return S == "combreloc" || S == "copyreloc" || S == "defs" ||
- S == "execstack" || S == "global" || S == "hazardplt" ||
- S == "initfirst" || S == "interpose" ||
- S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" ||
- S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" ||
- S == "nodelete" || S == "nodlopen" || S == "noexecstack" ||
- S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" ||
- S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" ||
- S == "rodynamic" || S == "text" || S == "wxneeded" ||
- S.startswith("max-page-size=") || S.startswith("stack-size=");
+static bool isKnownZFlag(StringRef s) {
+ return s == "combreloc" || s == "copyreloc" || s == "defs" ||
+ s == "execstack" || s == "global" || s == "hazardplt" ||
+ s == "ifunc-noplt" || s == "initfirst" || s == "interpose" ||
+ s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
+ s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
+ s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
+ s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" ||
+ s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" ||
+ s == "rodynamic" || s == "text" || s == "wxneeded" ||
+ s.startswith("common-page-size") || s.startswith("max-page-size=") ||
+ s.startswith("stack-size=");
}
// Report an error for an unknown -z option.
-static void checkZOptions(opt::InputArgList &Args) {
- for (auto *Arg : Args.filtered(OPT_z))
- if (!isKnownZFlag(Arg->getValue()))
- error("unknown -z value: " + StringRef(Arg->getValue()));
+static void checkZOptions(opt::InputArgList &args) {
+ for (auto *arg : args.filtered(OPT_z))
+ if (!isKnownZFlag(arg->getValue()))
+ error("unknown -z value: " + StringRef(arg->getValue()));
}
-void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
- ELFOptTable Parser;
- opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
+void LinkerDriver::main(ArrayRef<const char *> argsArr) {
+ ELFOptTable parser;
+ opt::InputArgList args = parser.parse(argsArr.slice(1));
// Interpret this flag early because error() depends on them.
- errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
+ errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20);
+ checkZOptions(args);
// Handle -help
- if (Args.hasArg(OPT_help)) {
+ if (args.hasArg(OPT_help)) {
printHelp();
return;
}
@@ -393,213 +423,218 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
// lot of "configure" scripts out there that are generated by old version
// of Libtool. We cannot convince every software developer to migrate to
// the latest version and re-generate scripts. So we have this hack.
- if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version))
+ if (args.hasArg(OPT_v) || args.hasArg(OPT_version))
message(getLLDVersion() + " (compatible with GNU linkers)");
- if (const char *Path = getReproduceOption(Args)) {
+ if (const char *path = getReproduceOption(args)) {
// Note that --reproduce is a debug option so you can ignore it
// if you are trying to understand the whole picture of the code.
- Expected<std::unique_ptr<TarWriter>> ErrOrWriter =
- TarWriter::create(Path, path::stem(Path));
- if (ErrOrWriter) {
- Tar = std::move(*ErrOrWriter);
- Tar->append("response.txt", createResponseFile(Args));
- Tar->append("version.txt", getLLDVersion() + "\n");
+ Expected<std::unique_ptr<TarWriter>> errOrWriter =
+ TarWriter::create(path, path::stem(path));
+ if (errOrWriter) {
+ tar = std::move(*errOrWriter);
+ tar->append("response.txt", createResponseFile(args));
+ tar->append("version.txt", getLLDVersion() + "\n");
} else {
- error("--reproduce: " + toString(ErrOrWriter.takeError()));
+ error("--reproduce: " + toString(errOrWriter.takeError()));
}
}
- readConfigs(Args);
- checkZOptions(Args);
+ readConfigs(args);
// The behavior of -v or --version is a bit strange, but this is
// needed for compatibility with GNU linkers.
- if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT))
+ if (args.hasArg(OPT_v) && !args.hasArg(OPT_INPUT))
return;
- if (Args.hasArg(OPT_version))
+ if (args.hasArg(OPT_version))
return;
initLLVM();
- createFiles(Args);
+ createFiles(args);
if (errorCount())
return;
inferMachineType();
- setConfigs(Args);
+ setConfigs(args);
checkOptions();
if (errorCount())
return;
- switch (Config->EKind) {
+ // The Target instance handles target-specific stuff, such as applying
+ // relocations or writing a PLT section. It also contains target-dependent
+ // values such as a default image base address.
+ target = getTarget();
+
+ switch (config->ekind) {
case ELF32LEKind:
- link<ELF32LE>(Args);
+ link<ELF32LE>(args);
return;
case ELF32BEKind:
- link<ELF32BE>(Args);
+ link<ELF32BE>(args);
return;
case ELF64LEKind:
- link<ELF64LE>(Args);
+ link<ELF64LE>(args);
return;
case ELF64BEKind:
- link<ELF64BE>(Args);
+ link<ELF64BE>(args);
return;
default:
llvm_unreachable("unknown Config->EKind");
}
}
-static std::string getRpath(opt::InputArgList &Args) {
- std::vector<StringRef> V = args::getStrings(Args, OPT_rpath);
- return llvm::join(V.begin(), V.end(), ":");
+static std::string getRpath(opt::InputArgList &args) {
+ std::vector<StringRef> v = args::getStrings(args, OPT_rpath);
+ return llvm::join(v.begin(), v.end(), ":");
}
// Determines what we should do if there are remaining unresolved
// symbols after the name resolution.
-static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
- UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols,
+static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) {
+ UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols,
OPT_warn_unresolved_symbols, true)
? UnresolvedPolicy::ReportError
: UnresolvedPolicy::Warn;
// Process the last of -unresolved-symbols, -no-undefined or -z defs.
- for (auto *Arg : llvm::reverse(Args)) {
- switch (Arg->getOption().getID()) {
+ for (auto *arg : llvm::reverse(args)) {
+ switch (arg->getOption().getID()) {
case OPT_unresolved_symbols: {
- StringRef S = Arg->getValue();
- if (S == "ignore-all" || S == "ignore-in-object-files")
+ StringRef s = arg->getValue();
+ if (s == "ignore-all" || s == "ignore-in-object-files")
return UnresolvedPolicy::Ignore;
- if (S == "ignore-in-shared-libs" || S == "report-all")
- return ErrorOrWarn;
- error("unknown --unresolved-symbols value: " + S);
+ if (s == "ignore-in-shared-libs" || s == "report-all")
+ return errorOrWarn;
+ error("unknown --unresolved-symbols value: " + s);
continue;
}
case OPT_no_undefined:
- return ErrorOrWarn;
+ return errorOrWarn;
case OPT_z:
- if (StringRef(Arg->getValue()) == "defs")
- return ErrorOrWarn;
+ if (StringRef(arg->getValue()) == "defs")
+ return errorOrWarn;
continue;
}
}
// -shared implies -unresolved-symbols=ignore-all because missing
// symbols are likely to be resolved at runtime using other DSOs.
- if (Config->Shared)
+ if (config->shared)
return UnresolvedPolicy::Ignore;
- return ErrorOrWarn;
+ return errorOrWarn;
}
-static Target2Policy getTarget2(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_target2, "got-rel");
- if (S == "rel")
+static Target2Policy getTarget2(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_target2, "got-rel");
+ if (s == "rel")
return Target2Policy::Rel;
- if (S == "abs")
+ if (s == "abs")
return Target2Policy::Abs;
- if (S == "got-rel")
+ if (s == "got-rel")
return Target2Policy::GotRel;
- error("unknown --target2 option: " + S);
+ error("unknown --target2 option: " + s);
return Target2Policy::GotRel;
}
-static bool isOutputFormatBinary(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_oformat, "elf");
- if (S == "binary")
+static bool isOutputFormatBinary(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_oformat, "elf");
+ if (s == "binary")
return true;
- if (!S.startswith("elf"))
- error("unknown --oformat value: " + S);
+ if (!s.startswith("elf"))
+ error("unknown --oformat value: " + s);
return false;
}
-static DiscardPolicy getDiscard(opt::InputArgList &Args) {
- if (Args.hasArg(OPT_relocatable))
+static DiscardPolicy getDiscard(opt::InputArgList &args) {
+ if (args.hasArg(OPT_relocatable))
return DiscardPolicy::None;
- auto *Arg =
- Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none);
- if (!Arg)
+ auto *arg =
+ args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none);
+ if (!arg)
return DiscardPolicy::Default;
- if (Arg->getOption().getID() == OPT_discard_all)
+ if (arg->getOption().getID() == OPT_discard_all)
return DiscardPolicy::All;
- if (Arg->getOption().getID() == OPT_discard_locals)
+ if (arg->getOption().getID() == OPT_discard_locals)
return DiscardPolicy::Locals;
return DiscardPolicy::None;
}
-static StringRef getDynamicLinker(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker);
- if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker)
+static StringRef getDynamicLinker(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker);
+ if (!arg || arg->getOption().getID() == OPT_no_dynamic_linker)
return "";
- return Arg->getValue();
+ return arg->getValue();
}
-static ICFLevel getICF(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all);
- if (!Arg || Arg->getOption().getID() == OPT_icf_none)
+static ICFLevel getICF(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all);
+ if (!arg || arg->getOption().getID() == OPT_icf_none)
return ICFLevel::None;
- if (Arg->getOption().getID() == OPT_icf_safe)
+ if (arg->getOption().getID() == OPT_icf_safe)
return ICFLevel::Safe;
return ICFLevel::All;
}
-static StripPolicy getStrip(opt::InputArgList &Args) {
- if (Args.hasArg(OPT_relocatable))
+static StripPolicy getStrip(opt::InputArgList &args) {
+ if (args.hasArg(OPT_relocatable))
return StripPolicy::None;
- auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug);
- if (!Arg)
+ auto *arg = args.getLastArg(OPT_strip_all, OPT_strip_debug);
+ if (!arg)
return StripPolicy::None;
- if (Arg->getOption().getID() == OPT_strip_all)
+ if (arg->getOption().getID() == OPT_strip_all)
return StripPolicy::All;
return StripPolicy::Debug;
}
-static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) {
- uint64_t VA = 0;
- if (S.startswith("0x"))
- S = S.drop_front(2);
- if (!to_integer(S, VA, 16))
- error("invalid argument: " + toString(Arg));
- return VA;
+static uint64_t parseSectionAddress(StringRef s, opt::InputArgList &args,
+ const opt::Arg &arg) {
+ uint64_t va = 0;
+ if (s.startswith("0x"))
+ s = s.drop_front(2);
+ if (!to_integer(s, va, 16))
+ error("invalid argument: " + arg.getAsString(args));
+ return va;
}
-static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) {
- StringMap<uint64_t> Ret;
- for (auto *Arg : Args.filtered(OPT_section_start)) {
- StringRef Name;
- StringRef Addr;
- std::tie(Name, Addr) = StringRef(Arg->getValue()).split('=');
- Ret[Name] = parseSectionAddress(Addr, *Arg);
+static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &args) {
+ StringMap<uint64_t> ret;
+ for (auto *arg : args.filtered(OPT_section_start)) {
+ StringRef name;
+ StringRef addr;
+ std::tie(name, addr) = StringRef(arg->getValue()).split('=');
+ ret[name] = parseSectionAddress(addr, args, *arg);
}
- if (auto *Arg = Args.getLastArg(OPT_Ttext))
- Ret[".text"] = parseSectionAddress(Arg->getValue(), *Arg);
- if (auto *Arg = Args.getLastArg(OPT_Tdata))
- Ret[".data"] = parseSectionAddress(Arg->getValue(), *Arg);
- if (auto *Arg = Args.getLastArg(OPT_Tbss))
- Ret[".bss"] = parseSectionAddress(Arg->getValue(), *Arg);
- return Ret;
+ if (auto *arg = args.getLastArg(OPT_Ttext))
+ ret[".text"] = parseSectionAddress(arg->getValue(), args, *arg);
+ if (auto *arg = args.getLastArg(OPT_Tdata))
+ ret[".data"] = parseSectionAddress(arg->getValue(), args, *arg);
+ if (auto *arg = args.getLastArg(OPT_Tbss))
+ ret[".bss"] = parseSectionAddress(arg->getValue(), args, *arg);
+ return ret;
}
-static SortSectionPolicy getSortSection(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_sort_section);
- if (S == "alignment")
+static SortSectionPolicy getSortSection(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_sort_section);
+ if (s == "alignment")
return SortSectionPolicy::Alignment;
- if (S == "name")
+ if (s == "name")
return SortSectionPolicy::Name;
- if (!S.empty())
- error("unknown --sort-section rule: " + S);
+ if (!s.empty())
+ error("unknown --sort-section rule: " + s);
return SortSectionPolicy::Default;
}
-static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_orphan_handling, "place");
- if (S == "warn")
+static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_orphan_handling, "place");
+ if (s == "warn")
return OrphanHandlingPolicy::Warn;
- if (S == "error")
+ if (s == "error")
return OrphanHandlingPolicy::Error;
- if (S != "place")
- error("unknown --orphan-handling mode: " + S);
+ if (s != "place")
+ error("unknown --orphan-handling mode: " + s);
return OrphanHandlingPolicy::Place;
}
@@ -607,388 +642,412 @@ static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) {
// synonym for "sha1" because all our hash functions including
// -build-id=sha1 are actually tree hashes for performance reasons.
static std::pair<BuildIdKind, std::vector<uint8_t>>
-getBuildId(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_build_id, OPT_build_id_eq);
- if (!Arg)
+getBuildId(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_build_id, OPT_build_id_eq);
+ if (!arg)
return {BuildIdKind::None, {}};
- if (Arg->getOption().getID() == OPT_build_id)
+ if (arg->getOption().getID() == OPT_build_id)
return {BuildIdKind::Fast, {}};
- StringRef S = Arg->getValue();
- if (S == "fast")
+ StringRef s = arg->getValue();
+ if (s == "fast")
return {BuildIdKind::Fast, {}};
- if (S == "md5")
+ if (s == "md5")
return {BuildIdKind::Md5, {}};
- if (S == "sha1" || S == "tree")
+ if (s == "sha1" || s == "tree")
return {BuildIdKind::Sha1, {}};
- if (S == "uuid")
+ if (s == "uuid")
return {BuildIdKind::Uuid, {}};
- if (S.startswith("0x"))
- return {BuildIdKind::Hexstring, parseHex(S.substr(2))};
+ if (s.startswith("0x"))
+ return {BuildIdKind::Hexstring, parseHex(s.substr(2))};
- if (S != "none")
- error("unknown --build-id style: " + S);
+ if (s != "none")
+ error("unknown --build-id style: " + s);
return {BuildIdKind::None, {}};
}
-static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_pack_dyn_relocs, "none");
- if (S == "android")
+static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_pack_dyn_relocs, "none");
+ if (s == "android")
return {true, false};
- if (S == "relr")
+ if (s == "relr")
return {false, true};
- if (S == "android+relr")
+ if (s == "android+relr")
return {true, true};
- if (S != "none")
- error("unknown -pack-dyn-relocs format: " + S);
+ if (s != "none")
+ error("unknown -pack-dyn-relocs format: " + s);
return {false, false};
}
-static void readCallGraph(MemoryBufferRef MB) {
+static void readCallGraph(MemoryBufferRef mb) {
// Build a map from symbol name to section
- DenseMap<StringRef, Symbol *> Map;
- for (InputFile *File : ObjectFiles)
- for (Symbol *Sym : File->getSymbols())
- Map[Sym->getName()] = Sym;
-
- auto FindSection = [&](StringRef Name) -> InputSectionBase * {
- Symbol *Sym = Map.lookup(Name);
- if (!Sym) {
- if (Config->WarnSymbolOrdering)
- warn(MB.getBufferIdentifier() + ": no such symbol: " + Name);
+ DenseMap<StringRef, Symbol *> map;
+ for (InputFile *file : objectFiles)
+ for (Symbol *sym : file->getSymbols())
+ map[sym->getName()] = sym;
+
+ auto findSection = [&](StringRef name) -> InputSectionBase * {
+ Symbol *sym = map.lookup(name);
+ if (!sym) {
+ if (config->warnSymbolOrdering)
+ warn(mb.getBufferIdentifier() + ": no such symbol: " + name);
return nullptr;
}
- maybeWarnUnorderableSymbol(Sym);
+ maybeWarnUnorderableSymbol(sym);
- if (Defined *DR = dyn_cast_or_null<Defined>(Sym))
- return dyn_cast_or_null<InputSectionBase>(DR->Section);
+ if (Defined *dr = dyn_cast_or_null<Defined>(sym))
+ return dyn_cast_or_null<InputSectionBase>(dr->section);
return nullptr;
};
- for (StringRef Line : args::getLines(MB)) {
- SmallVector<StringRef, 3> Fields;
- Line.split(Fields, ' ');
- uint64_t Count;
+ for (StringRef line : args::getLines(mb)) {
+ SmallVector<StringRef, 3> fields;
+ line.split(fields, ' ');
+ uint64_t count;
- if (Fields.size() != 3 || !to_integer(Fields[2], Count)) {
- error(MB.getBufferIdentifier() + ": parse error");
+ if (fields.size() != 3 || !to_integer(fields[2], count)) {
+ error(mb.getBufferIdentifier() + ": parse error");
return;
}
- if (InputSectionBase *From = FindSection(Fields[0]))
- if (InputSectionBase *To = FindSection(Fields[1]))
- Config->CallGraphProfile[std::make_pair(From, To)] += Count;
+ if (InputSectionBase *from = findSection(fields[0]))
+ if (InputSectionBase *to = findSection(fields[1]))
+ config->callGraphProfile[std::make_pair(from, to)] += count;
}
}
template <class ELFT> static void readCallGraphsFromObjectFiles() {
- for (auto File : ObjectFiles) {
- auto *Obj = cast<ObjFile<ELFT>>(File);
+ for (auto file : objectFiles) {
+ auto *obj = cast<ObjFile<ELFT>>(file);
- for (const Elf_CGProfile_Impl<ELFT> &CGPE : Obj->CGProfile) {
- auto *FromSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_from));
- auto *ToSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_to));
- if (!FromSym || !ToSym)
+ for (const Elf_CGProfile_Impl<ELFT> &cgpe : obj->cgProfile) {
+ auto *fromSym = dyn_cast<Defined>(&obj->getSymbol(cgpe.cgp_from));
+ auto *toSym = dyn_cast<Defined>(&obj->getSymbol(cgpe.cgp_to));
+ if (!fromSym || !toSym)
continue;
- auto *From = dyn_cast_or_null<InputSectionBase>(FromSym->Section);
- auto *To = dyn_cast_or_null<InputSectionBase>(ToSym->Section);
- if (From && To)
- Config->CallGraphProfile[{From, To}] += CGPE.cgp_weight;
+ auto *from = dyn_cast_or_null<InputSectionBase>(fromSym->section);
+ auto *to = dyn_cast_or_null<InputSectionBase>(toSym->section);
+ if (from && to)
+ config->callGraphProfile[{from, to}] += cgpe.cgp_weight;
}
}
}
-static bool getCompressDebugSections(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none");
- if (S == "none")
+static bool getCompressDebugSections(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_compress_debug_sections, "none");
+ if (s == "none")
return false;
- if (S != "zlib")
- error("unknown --compress-debug-sections value: " + S);
+ if (s != "zlib")
+ error("unknown --compress-debug-sections value: " + s);
if (!zlib::isAvailable())
error("--compress-debug-sections: zlib is not available");
return true;
}
-static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &Args,
- unsigned Id) {
- auto *Arg = Args.getLastArg(Id);
- if (!Arg)
+static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
+ unsigned id) {
+ auto *arg = args.getLastArg(id);
+ if (!arg)
return {"", ""};
- StringRef S = Arg->getValue();
- std::pair<StringRef, StringRef> Ret = S.split(';');
- if (Ret.second.empty())
- error(Arg->getSpelling() + " expects 'old;new' format, but got " + S);
- return Ret;
+ StringRef s = arg->getValue();
+ std::pair<StringRef, StringRef> ret = s.split(';');
+ if (ret.second.empty())
+ error(arg->getSpelling() + " expects 'old;new' format, but got " + s);
+ return ret;
}
// Parse the symbol ordering file and warn for any duplicate entries.
-static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef MB) {
- SetVector<StringRef> Names;
- for (StringRef S : args::getLines(MB))
- if (!Names.insert(S) && Config->WarnSymbolOrdering)
- warn(MB.getBufferIdentifier() + ": duplicate ordered symbol: " + S);
+static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef mb) {
+ SetVector<StringRef> names;
+ for (StringRef s : args::getLines(mb))
+ if (!names.insert(s) && config->warnSymbolOrdering)
+ warn(mb.getBufferIdentifier() + ": duplicate ordered symbol: " + s);
- return Names.takeVector();
+ return names.takeVector();
}
-static void parseClangOption(StringRef Opt, const Twine &Msg) {
- std::string Err;
- raw_string_ostream OS(Err);
+static void parseClangOption(StringRef opt, const Twine &msg) {
+ std::string err;
+ raw_string_ostream os(err);
- const char *Argv[] = {Config->ProgName.data(), Opt.data()};
- if (cl::ParseCommandLineOptions(2, Argv, "", &OS))
+ const char *argv[] = {config->progName.data(), opt.data()};
+ if (cl::ParseCommandLineOptions(2, argv, "", &os))
return;
- OS.flush();
- error(Msg + ": " + StringRef(Err).trim());
+ os.flush();
+ error(msg + ": " + StringRef(err).trim());
}
// Initializes Config members by the command line options.
-void LinkerDriver::readConfigs(opt::InputArgList &Args) {
- errorHandler().Verbose = Args.hasArg(OPT_verbose);
- errorHandler().FatalWarnings =
- Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
- ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
-
- Config->AllowMultipleDefinition =
- Args.hasFlag(OPT_allow_multiple_definition,
+static void readConfigs(opt::InputArgList &args) {
+ errorHandler().verbose = args.hasArg(OPT_verbose);
+ errorHandler().fatalWarnings =
+ args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
+ errorHandler().vsDiagnostics =
+ args.hasArg(OPT_visual_studio_diagnostics_format, false);
+ threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true);
+
+ config->allowMultipleDefinition =
+ args.hasFlag(OPT_allow_multiple_definition,
OPT_no_allow_multiple_definition, false) ||
- hasZOption(Args, "muldefs");
- Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary);
- Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
- Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
- Config->CheckSections =
- Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true);
- Config->Chroot = Args.getLastArgValue(OPT_chroot);
- Config->CompressDebugSections = getCompressDebugSections(Args);
- Config->Cref = Args.hasFlag(OPT_cref, OPT_no_cref, false);
- Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common,
- !Args.hasArg(OPT_relocatable));
- Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
- Config->DisableVerify = Args.hasArg(OPT_disable_verify);
- Config->Discard = getDiscard(Args);
- Config->DwoDir = Args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq);
- Config->DynamicLinker = getDynamicLinker(Args);
- Config->EhFrameHdr =
- Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
- Config->EmitLLVM = Args.hasArg(OPT_plugin_opt_emit_llvm, false);
- Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
- Config->CallGraphProfileSort = Args.hasFlag(
+ hasZOption(args, "muldefs");
+ config->allowShlibUndefined =
+ args.hasFlag(OPT_allow_shlib_undefined, OPT_no_allow_shlib_undefined,
+ args.hasArg(OPT_shared));
+ config->auxiliaryList = args::getStrings(args, OPT_auxiliary);
+ config->bsymbolic = args.hasArg(OPT_Bsymbolic);
+ config->bsymbolicFunctions = args.hasArg(OPT_Bsymbolic_functions);
+ config->checkSections =
+ args.hasFlag(OPT_check_sections, OPT_no_check_sections, true);
+ config->chroot = args.getLastArgValue(OPT_chroot);
+ config->compressDebugSections = getCompressDebugSections(args);
+ config->cref = args.hasFlag(OPT_cref, OPT_no_cref, false);
+ config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common,
+ !args.hasArg(OPT_relocatable));
+ config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true);
+ config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true);
+ config->disableVerify = args.hasArg(OPT_disable_verify);
+ config->discard = getDiscard(args);
+ config->dwoDir = args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq);
+ config->dynamicLinker = getDynamicLinker(args);
+ config->ehFrameHdr =
+ args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
+ config->emitLLVM = args.hasArg(OPT_plugin_opt_emit_llvm, false);
+ config->emitRelocs = args.hasArg(OPT_emit_relocs);
+ config->callGraphProfileSort = args.hasFlag(
OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true);
- Config->EnableNewDtags =
- Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
- Config->Entry = Args.getLastArgValue(OPT_entry);
- Config->ExecuteOnly =
- Args.hasFlag(OPT_execute_only, OPT_no_execute_only, false);
- Config->ExportDynamic =
- Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
- Config->FilterList = args::getStrings(Args, OPT_filter);
- Config->Fini = Args.getLastArgValue(OPT_fini, "_fini");
- Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419);
- Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
- Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true);
- Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false);
- Config->ICF = getICF(Args);
- Config->IgnoreDataAddressEquality =
- Args.hasArg(OPT_ignore_data_address_equality);
- Config->IgnoreFunctionAddressEquality =
- Args.hasArg(OPT_ignore_function_address_equality);
- Config->Init = Args.getLastArgValue(OPT_init, "_init");
- Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline);
- Config->LTODebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager);
- Config->LTONewPassManager = Args.hasArg(OPT_lto_new_pass_manager);
- Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes);
- Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
- Config->LTOObjPath = Args.getLastArgValue(OPT_plugin_opt_obj_path_eq);
- Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
- Config->LTOSampleProfile = Args.getLastArgValue(OPT_lto_sample_profile);
- Config->MapFile = Args.getLastArgValue(OPT_Map);
- Config->MipsGotSize = args::getInteger(Args, OPT_mips_got_size, 0xfff0);
- Config->MergeArmExidx =
- Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true);
- Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec);
- Config->Nostdlib = Args.hasArg(OPT_nostdlib);
- Config->OFormatBinary = isOutputFormatBinary(Args);
- Config->Omagic = Args.hasFlag(OPT_omagic, OPT_no_omagic, false);
- Config->OptRemarksFilename = Args.getLastArgValue(OPT_opt_remarks_filename);
- Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness);
- Config->Optimize = args::getInteger(Args, OPT_O, 1);
- Config->OrphanHandling = getOrphanHandling(Args);
- Config->OutputFile = Args.getLastArgValue(OPT_o);
- Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false);
- Config->PrintIcfSections =
- Args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false);
- Config->PrintGcSections =
- Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
- Config->Rpath = getRpath(Args);
- Config->Relocatable = Args.hasArg(OPT_relocatable);
- Config->SaveTemps = Args.hasArg(OPT_save_temps);
- Config->SearchPaths = args::getStrings(Args, OPT_library_path);
- Config->SectionStartMap = getSectionStartMap(Args);
- Config->Shared = Args.hasArg(OPT_shared);
- Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
- Config->SoName = Args.getLastArgValue(OPT_soname);
- Config->SortSection = getSortSection(Args);
- Config->SplitStackAdjustSize = args::getInteger(Args, OPT_split_stack_adjust_size, 16384);
- Config->Strip = getStrip(Args);
- Config->Sysroot = Args.getLastArgValue(OPT_sysroot);
- Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false);
- Config->Target2 = getTarget2(Args);
- Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir);
- Config->ThinLTOCachePolicy = CHECK(
- parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)),
+ config->enableNewDtags =
+ args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
+ config->entry = args.getLastArgValue(OPT_entry);
+ config->executeOnly =
+ args.hasFlag(OPT_execute_only, OPT_no_execute_only, false);
+ config->exportDynamic =
+ args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
+ config->filterList = args::getStrings(args, OPT_filter);
+ config->fini = args.getLastArgValue(OPT_fini, "_fini");
+ config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419);
+ config->forceBTI = args.hasArg(OPT_force_bti);
+ config->requireCET = args.hasArg(OPT_require_cet);
+ config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
+ config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true);
+ config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false);
+ config->icf = getICF(args);
+ config->ignoreDataAddressEquality =
+ args.hasArg(OPT_ignore_data_address_equality);
+ config->ignoreFunctionAddressEquality =
+ args.hasArg(OPT_ignore_function_address_equality);
+ config->init = args.getLastArgValue(OPT_init, "_init");
+ config->ltoAAPipeline = args.getLastArgValue(OPT_lto_aa_pipeline);
+ config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate);
+ config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file);
+ config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager);
+ config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager);
+ config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
+ config->ltoo = args::getInteger(args, OPT_lto_O, 2);
+ config->ltoObjPath = args.getLastArgValue(OPT_plugin_opt_obj_path_eq);
+ config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
+ config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
+ config->mapFile = args.getLastArgValue(OPT_Map);
+ config->mipsGotSize = args::getInteger(args, OPT_mips_got_size, 0xfff0);
+ config->mergeArmExidx =
+ args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true);
+ config->nmagic = args.hasFlag(OPT_nmagic, OPT_no_nmagic, false);
+ config->noinhibitExec = args.hasArg(OPT_noinhibit_exec);
+ config->nostdlib = args.hasArg(OPT_nostdlib);
+ config->oFormatBinary = isOutputFormatBinary(args);
+ config->omagic = args.hasFlag(OPT_omagic, OPT_no_omagic, false);
+ config->optRemarksFilename = args.getLastArgValue(OPT_opt_remarks_filename);
+ config->optRemarksPasses = args.getLastArgValue(OPT_opt_remarks_passes);
+ config->optRemarksWithHotness = args.hasArg(OPT_opt_remarks_with_hotness);
+ config->optRemarksFormat = args.getLastArgValue(OPT_opt_remarks_format);
+ config->optimize = args::getInteger(args, OPT_O, 1);
+ config->orphanHandling = getOrphanHandling(args);
+ config->outputFile = args.getLastArgValue(OPT_o);
+ config->pacPlt = args.hasArg(OPT_pac_plt);
+ config->pie = args.hasFlag(OPT_pie, OPT_no_pie, false);
+ config->printIcfSections =
+ args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false);
+ config->printGcSections =
+ args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
+ config->printSymbolOrder =
+ args.getLastArgValue(OPT_print_symbol_order);
+ config->rpath = getRpath(args);
+ config->relocatable = args.hasArg(OPT_relocatable);
+ config->saveTemps = args.hasArg(OPT_save_temps);
+ config->searchPaths = args::getStrings(args, OPT_library_path);
+ config->sectionStartMap = getSectionStartMap(args);
+ config->shared = args.hasArg(OPT_shared);
+ config->singleRoRx = args.hasArg(OPT_no_rosegment);
+ config->soName = args.getLastArgValue(OPT_soname);
+ config->sortSection = getSortSection(args);
+ config->splitStackAdjustSize = args::getInteger(args, OPT_split_stack_adjust_size, 16384);
+ config->strip = getStrip(args);
+ config->sysroot = args.getLastArgValue(OPT_sysroot);
+ config->target1Rel = args.hasFlag(OPT_target1_rel, OPT_target1_abs, false);
+ config->target2 = getTarget2(args);
+ config->thinLTOCacheDir = args.getLastArgValue(OPT_thinlto_cache_dir);
+ config->thinLTOCachePolicy = CHECK(
+ parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
- Config->ThinLTOEmitImportsFiles =
- Args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files);
- Config->ThinLTOIndexOnly = Args.hasArg(OPT_plugin_opt_thinlto_index_only) ||
- Args.hasArg(OPT_plugin_opt_thinlto_index_only_eq);
- Config->ThinLTOIndexOnlyArg =
- Args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq);
- Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u);
- Config->ThinLTOObjectSuffixReplace =
- getOldNewOptions(Args, OPT_plugin_opt_thinlto_object_suffix_replace_eq);
- Config->ThinLTOPrefixReplace =
- getOldNewOptions(Args, OPT_plugin_opt_thinlto_prefix_replace_eq);
- Config->Trace = Args.hasArg(OPT_trace);
- Config->Undefined = args::getStrings(Args, OPT_undefined);
- Config->UndefinedVersion =
- Args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true);
- Config->UseAndroidRelrTags = Args.hasFlag(
+ config->thinLTOEmitImportsFiles =
+ args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files);
+ config->thinLTOIndexOnly = args.hasArg(OPT_plugin_opt_thinlto_index_only) ||
+ args.hasArg(OPT_plugin_opt_thinlto_index_only_eq);
+ config->thinLTOIndexOnlyArg =
+ args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq);
+ config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
+ config->thinLTOObjectSuffixReplace =
+ getOldNewOptions(args, OPT_plugin_opt_thinlto_object_suffix_replace_eq);
+ config->thinLTOPrefixReplace =
+ getOldNewOptions(args, OPT_plugin_opt_thinlto_prefix_replace_eq);
+ config->trace = args.hasArg(OPT_trace);
+ config->undefined = args::getStrings(args, OPT_undefined);
+ config->undefinedVersion =
+ args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true);
+ config->useAndroidRelrTags = args.hasFlag(
OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false);
- Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args);
- Config->WarnBackrefs =
- Args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false);
- Config->WarnCommon = Args.hasFlag(OPT_warn_common, OPT_no_warn_common, false);
- Config->WarnIfuncTextrel =
- Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false);
- Config->WarnSymbolOrdering =
- Args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
- Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true);
- Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true);
- Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false);
- Config->ZGlobal = hasZOption(Args, "global");
- Config->ZHazardplt = hasZOption(Args, "hazardplt");
- Config->ZInitfirst = hasZOption(Args, "initfirst");
- Config->ZInterpose = hasZOption(Args, "interpose");
- Config->ZKeepTextSectionPrefix = getZFlag(
- Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
- Config->ZNodefaultlib = hasZOption(Args, "nodefaultlib");
- Config->ZNodelete = hasZOption(Args, "nodelete");
- Config->ZNodlopen = hasZOption(Args, "nodlopen");
- Config->ZNow = getZFlag(Args, "now", "lazy", false);
- Config->ZOrigin = hasZOption(Args, "origin");
- Config->ZRelro = getZFlag(Args, "relro", "norelro", true);
- Config->ZRetpolineplt = hasZOption(Args, "retpolineplt");
- Config->ZRodynamic = hasZOption(Args, "rodynamic");
- Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", 0);
- Config->ZText = getZFlag(Args, "text", "notext", true);
- Config->ZWxneeded = hasZOption(Args, "wxneeded");
+ config->unresolvedSymbols = getUnresolvedSymbolPolicy(args);
+ config->warnBackrefs =
+ args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false);
+ config->warnCommon = args.hasFlag(OPT_warn_common, OPT_no_warn_common, false);
+ config->warnIfuncTextrel =
+ args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false);
+ config->warnSymbolOrdering =
+ args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
+ config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true);
+ config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
+ config->zExecstack = getZFlag(args, "execstack", "noexecstack", false);
+ config->zGlobal = hasZOption(args, "global");
+ config->zHazardplt = hasZOption(args, "hazardplt");
+ config->zIfuncNoplt = hasZOption(args, "ifunc-noplt");
+ config->zInitfirst = hasZOption(args, "initfirst");
+ config->zInterpose = hasZOption(args, "interpose");
+ config->zKeepTextSectionPrefix = getZFlag(
+ args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
+ config->zNodefaultlib = hasZOption(args, "nodefaultlib");
+ config->zNodelete = hasZOption(args, "nodelete");
+ config->zNodlopen = hasZOption(args, "nodlopen");
+ config->zNow = getZFlag(args, "now", "lazy", false);
+ config->zOrigin = hasZOption(args, "origin");
+ config->zRelro = getZFlag(args, "relro", "norelro", true);
+ config->zRetpolineplt = hasZOption(args, "retpolineplt");
+ config->zRodynamic = hasZOption(args, "rodynamic");
+ config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
+ config->zText = getZFlag(args, "text", "notext", true);
+ config->zWxneeded = hasZOption(args, "wxneeded");
// Parse LTO options.
- if (auto *Arg = Args.getLastArg(OPT_plugin_opt_mcpu_eq))
- parseClangOption(Saver.save("-mcpu=" + StringRef(Arg->getValue())),
- Arg->getSpelling());
+ if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
+ parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
+ arg->getSpelling());
- for (auto *Arg : Args.filtered(OPT_plugin_opt))
- parseClangOption(Arg->getValue(), Arg->getSpelling());
+ for (auto *arg : args.filtered(OPT_plugin_opt))
+ parseClangOption(arg->getValue(), arg->getSpelling());
// Parse -mllvm options.
- for (auto *Arg : Args.filtered(OPT_mllvm))
- parseClangOption(Arg->getValue(), Arg->getSpelling());
+ for (auto *arg : args.filtered(OPT_mllvm))
+ parseClangOption(arg->getValue(), arg->getSpelling());
- if (Config->LTOO > 3)
- error("invalid optimization level for LTO: " + Twine(Config->LTOO));
- if (Config->LTOPartitions == 0)
+ if (config->ltoo > 3)
+ error("invalid optimization level for LTO: " + Twine(config->ltoo));
+ if (config->ltoPartitions == 0)
error("--lto-partitions: number of threads must be > 0");
- if (Config->ThinLTOJobs == 0)
+ if (config->thinLTOJobs == 0)
error("--thinlto-jobs: number of threads must be > 0");
- if (Config->SplitStackAdjustSize < 0)
+ if (config->splitStackAdjustSize < 0)
error("--split-stack-adjust-size: size must be >= 0");
// Parse ELF{32,64}{LE,BE} and CPU type.
- if (auto *Arg = Args.getLastArg(OPT_m)) {
- StringRef S = Arg->getValue();
- std::tie(Config->EKind, Config->EMachine, Config->OSABI) =
- parseEmulation(S);
- Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32");
- Config->Emulation = S;
+ if (auto *arg = args.getLastArg(OPT_m)) {
+ StringRef s = arg->getValue();
+ std::tie(config->ekind, config->emachine, config->osabi) =
+ parseEmulation(s);
+ config->mipsN32Abi = (s == "elf32btsmipn32" || s == "elf32ltsmipn32");
+ config->emulation = s;
}
// Parse -hash-style={sysv,gnu,both}.
- if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
- StringRef S = Arg->getValue();
- if (S == "sysv")
- Config->SysvHash = true;
- else if (S == "gnu")
- Config->GnuHash = true;
- else if (S == "both")
- Config->SysvHash = Config->GnuHash = true;
+ if (auto *arg = args.getLastArg(OPT_hash_style)) {
+ StringRef s = arg->getValue();
+ if (s == "sysv")
+ config->sysvHash = true;
+ else if (s == "gnu")
+ config->gnuHash = true;
+ else if (s == "both")
+ config->sysvHash = config->gnuHash = true;
else
- error("unknown -hash-style: " + S);
+ error("unknown -hash-style: " + s);
}
- if (Args.hasArg(OPT_print_map))
- Config->MapFile = "-";
-
- // --omagic is an option to create old-fashioned executables in which
- // .text segments are writable. Today, the option is still in use to
- // create special-purpose programs such as boot loaders. It doesn't
- // make sense to create PT_GNU_RELRO for such executables.
- if (Config->Omagic)
- Config->ZRelro = false;
-
- std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args);
-
- std::tie(Config->AndroidPackDynRelocs, Config->RelrPackDynRelocs) =
- getPackDynRelocs(Args);
-
- if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- Config->SymbolOrderingFile = getSymbolOrderingFile(*Buffer);
+ if (args.hasArg(OPT_print_map))
+ config->mapFile = "-";
+
+ // Page alignment can be disabled by the -n (--nmagic) and -N (--omagic).
+ // As PT_GNU_RELRO relies on Paging, do not create it when we have disabled
+ // it.
+ if (config->nmagic || config->omagic)
+ config->zRelro = false;
+
+ std::tie(config->buildId, config->buildIdVector) = getBuildId(args);
+
+ std::tie(config->androidPackDynRelocs, config->relrPackDynRelocs) =
+ getPackDynRelocs(args);
+
+ if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){
+ if (args.hasArg(OPT_call_graph_ordering_file))
+ error("--symbol-ordering-file and --call-graph-order-file "
+ "may not be used together");
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())){
+ config->symbolOrderingFile = getSymbolOrderingFile(*buffer);
+ // Also need to disable CallGraphProfileSort to prevent
+ // LLD order symbols with CGProfile
+ config->callGraphProfileSort = false;
+ }
+ }
// If --retain-symbol-file is used, we'll keep only the symbols listed in
// the file and discard all others.
- if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) {
- Config->DefaultSymbolVersion = VER_NDX_LOCAL;
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- for (StringRef S : args::getLines(*Buffer))
- Config->VersionScriptGlobals.push_back(
- {S, /*IsExternCpp*/ false, /*HasWildcard*/ false});
+ if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) {
+ config->defaultSymbolVersion = VER_NDX_LOCAL;
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ for (StringRef s : args::getLines(*buffer))
+ config->versionScriptGlobals.push_back(
+ {s, /*IsExternCpp*/ false, /*HasWildcard*/ false});
}
- bool HasExportDynamic =
- Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
+ bool hasExportDynamic =
+ args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
// Parses -dynamic-list and -export-dynamic-symbol. They make some
// symbols private. Note that -export-dynamic takes precedence over them
// as it says all symbols should be exported.
- if (!HasExportDynamic) {
- for (auto *Arg : Args.filtered(OPT_dynamic_list))
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- readDynamicList(*Buffer);
-
- for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
- Config->DynamicList.push_back(
- {Arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false});
+ if (!hasExportDynamic) {
+ for (auto *arg : args.filtered(OPT_dynamic_list))
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ readDynamicList(*buffer);
+
+ for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
+ config->dynamicList.push_back(
+ {arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false});
}
// If --export-dynamic-symbol=foo is given and symbol foo is defined in
// an object file in an archive file, that object file should be pulled
// out and linked. (It doesn't have to behave like that from technical
// point of view, but this is needed for compatibility with GNU.)
- for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
- Config->Undefined.push_back(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
+ config->undefined.push_back(arg->getValue());
- for (auto *Arg : Args.filtered(OPT_version_script))
- if (Optional<std::string> Path = searchScript(Arg->getValue())) {
- if (Optional<MemoryBufferRef> Buffer = readFile(*Path))
- readVersionScript(*Buffer);
+ for (auto *arg : args.filtered(OPT_version_script))
+ if (Optional<std::string> path = searchScript(arg->getValue())) {
+ if (Optional<MemoryBufferRef> buffer = readFile(*path))
+ readVersionScript(*buffer);
} else {
- error(Twine("cannot find version script ") + Arg->getValue());
+ error(Twine("cannot find version script ") + arg->getValue());
}
}
@@ -996,17 +1055,18 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
// command line options, but computed based on other Config values.
// This function initialize such members. See Config.h for the details
// of these values.
-static void setConfigs(opt::InputArgList &Args) {
- ELFKind K = Config->EKind;
- uint16_t M = Config->EMachine;
-
- Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs);
- Config->Is64 = (K == ELF64LEKind || K == ELF64BEKind);
- Config->IsLE = (K == ELF32LEKind || K == ELF64LEKind);
- Config->Endianness = Config->IsLE ? endianness::little : endianness::big;
- Config->IsMips64EL = (K == ELF64LEKind && M == EM_MIPS);
- Config->Pic = Config->Pie || Config->Shared;
- Config->Wordsize = Config->Is64 ? 8 : 4;
+static void setConfigs(opt::InputArgList &args) {
+ ELFKind k = config->ekind;
+ uint16_t m = config->emachine;
+
+ config->copyRelocs = (config->relocatable || config->emitRelocs);
+ config->is64 = (k == ELF64LEKind || k == ELF64BEKind);
+ config->isLE = (k == ELF32LEKind || k == ELF64LEKind);
+ config->endianness = config->isLE ? endianness::little : endianness::big;
+ config->isMips64EL = (k == ELF64LEKind && m == EM_MIPS);
+ config->isPic = config->pie || config->shared;
+ config->picThunk = args.hasArg(OPT_pic_veneer, config->isPic);
+ config->wordsize = config->is64 ? 8 : 4;
// ELF defines two different ways to store relocation addends as shown below:
//
@@ -1021,148 +1081,150 @@ static void setConfigs(opt::InputArgList &Args) {
// You cannot choose which one, Rel or Rela, you want to use. Instead each
// ABI defines which one you need to use. The following expression expresses
// that.
- Config->IsRela = M == EM_AARCH64 || M == EM_AMDGPU || M == EM_HEXAGON ||
- M == EM_PPC || M == EM_PPC64 || M == EM_RISCV ||
- M == EM_X86_64;
+ config->isRela = m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON ||
+ m == EM_PPC || m == EM_PPC64 || m == EM_RISCV ||
+ m == EM_X86_64;
// If the output uses REL relocations we must store the dynamic relocation
// addends to the output sections. We also store addends for RELA relocations
// if --apply-dynamic-relocs is used.
// We default to not writing the addends when using RELA relocations since
// any standard conforming tool can find it in r_addend.
- Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs,
+ config->writeAddends = args.hasFlag(OPT_apply_dynamic_relocs,
OPT_no_apply_dynamic_relocs, false) ||
- !Config->IsRela;
+ !config->isRela;
- Config->TocOptimize =
- Args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, M == EM_PPC64);
+ config->tocOptimize =
+ args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, m == EM_PPC64);
}
// Returns a value of "-format" option.
-static bool isFormatBinary(StringRef S) {
- if (S == "binary")
+static bool isFormatBinary(StringRef s) {
+ if (s == "binary")
return true;
- if (S == "elf" || S == "default")
+ if (s == "elf" || s == "default")
return false;
- error("unknown -format value: " + S +
+ error("unknown -format value: " + s +
" (supported formats: elf, default, binary)");
return false;
}
-void LinkerDriver::createFiles(opt::InputArgList &Args) {
+void LinkerDriver::createFiles(opt::InputArgList &args) {
// For --{push,pop}-state.
- std::vector<std::tuple<bool, bool, bool>> Stack;
+ std::vector<std::tuple<bool, bool, bool>> stack;
// Iterate over argv to process input files and positional arguments.
- for (auto *Arg : Args) {
- switch (Arg->getOption().getUnaliasedOption().getID()) {
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
case OPT_library:
- addLibrary(Arg->getValue());
+ addLibrary(arg->getValue());
break;
case OPT_INPUT:
- addFile(Arg->getValue(), /*WithLOption=*/false);
+ addFile(arg->getValue(), /*withLOption=*/false);
break;
case OPT_defsym: {
- StringRef From;
- StringRef To;
- std::tie(From, To) = StringRef(Arg->getValue()).split('=');
- if (From.empty() || To.empty())
- error("-defsym: syntax error: " + StringRef(Arg->getValue()));
+ StringRef from;
+ StringRef to;
+ std::tie(from, to) = StringRef(arg->getValue()).split('=');
+ if (from.empty() || to.empty())
+ error("-defsym: syntax error: " + StringRef(arg->getValue()));
else
- readDefsym(From, MemoryBufferRef(To, "-defsym"));
+ readDefsym(from, MemoryBufferRef(to, "-defsym"));
break;
}
case OPT_script:
- if (Optional<std::string> Path = searchScript(Arg->getValue())) {
- if (Optional<MemoryBufferRef> MB = readFile(*Path))
- readLinkerScript(*MB);
+ if (Optional<std::string> path = searchScript(arg->getValue())) {
+ if (Optional<MemoryBufferRef> mb = readFile(*path))
+ readLinkerScript(*mb);
break;
}
- error(Twine("cannot find linker script ") + Arg->getValue());
+ error(Twine("cannot find linker script ") + arg->getValue());
break;
case OPT_as_needed:
- Config->AsNeeded = true;
+ config->asNeeded = true;
break;
case OPT_format:
- Config->FormatBinary = isFormatBinary(Arg->getValue());
+ config->formatBinary = isFormatBinary(arg->getValue());
break;
case OPT_no_as_needed:
- Config->AsNeeded = false;
+ config->asNeeded = false;
break;
case OPT_Bstatic:
- Config->Static = true;
+ case OPT_omagic:
+ case OPT_nmagic:
+ config->isStatic = true;
break;
case OPT_Bdynamic:
- Config->Static = false;
+ config->isStatic = false;
break;
case OPT_whole_archive:
- InWholeArchive = true;
+ inWholeArchive = true;
break;
case OPT_no_whole_archive:
- InWholeArchive = false;
+ inWholeArchive = false;
break;
case OPT_just_symbols:
- if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue())) {
- Files.push_back(createObjectFile(*MB));
- Files.back()->JustSymbols = true;
+ if (Optional<MemoryBufferRef> mb = readFile(arg->getValue())) {
+ files.push_back(createObjectFile(*mb));
+ files.back()->justSymbols = true;
}
break;
case OPT_start_group:
- if (InputFile::IsInGroup)
+ if (InputFile::isInGroup)
error("nested --start-group");
- InputFile::IsInGroup = true;
+ InputFile::isInGroup = true;
break;
case OPT_end_group:
- if (!InputFile::IsInGroup)
+ if (!InputFile::isInGroup)
error("stray --end-group");
- InputFile::IsInGroup = false;
- ++InputFile::NextGroupId;
+ InputFile::isInGroup = false;
+ ++InputFile::nextGroupId;
break;
case OPT_start_lib:
- if (InLib)
+ if (inLib)
error("nested --start-lib");
- if (InputFile::IsInGroup)
+ if (InputFile::isInGroup)
error("may not nest --start-lib in --start-group");
- InLib = true;
- InputFile::IsInGroup = true;
+ inLib = true;
+ InputFile::isInGroup = true;
break;
case OPT_end_lib:
- if (!InLib)
+ if (!inLib)
error("stray --end-lib");
- InLib = false;
- InputFile::IsInGroup = false;
- ++InputFile::NextGroupId;
+ inLib = false;
+ InputFile::isInGroup = false;
+ ++InputFile::nextGroupId;
break;
case OPT_push_state:
- Stack.emplace_back(Config->AsNeeded, Config->Static, InWholeArchive);
+ stack.emplace_back(config->asNeeded, config->isStatic, inWholeArchive);
break;
case OPT_pop_state:
- if (Stack.empty()) {
+ if (stack.empty()) {
error("unbalanced --push-state/--pop-state");
break;
}
- std::tie(Config->AsNeeded, Config->Static, InWholeArchive) = Stack.back();
- Stack.pop_back();
+ std::tie(config->asNeeded, config->isStatic, inWholeArchive) = stack.back();
+ stack.pop_back();
break;
}
}
- if (Files.empty() && errorCount() == 0)
+ if (files.empty() && errorCount() == 0)
error("no input files");
}
// If -m <machine_type> was not given, infer it from object files.
void LinkerDriver::inferMachineType() {
- if (Config->EKind != ELFNoneKind)
+ if (config->ekind != ELFNoneKind)
return;
- for (InputFile *F : Files) {
- if (F->EKind == ELFNoneKind)
+ for (InputFile *f : files) {
+ if (f->ekind == ELFNoneKind)
continue;
- Config->EKind = F->EKind;
- Config->EMachine = F->EMachine;
- Config->OSABI = F->OSABI;
- Config->MipsN32Abi = Config->EMachine == EM_MIPS && isMipsN32Abi(F);
+ config->ekind = f->ekind;
+ config->emachine = f->emachine;
+ config->osabi = f->osabi;
+ config->mipsN32Abi = config->emachine == EM_MIPS && isMipsN32Abi(f);
return;
}
error("target emulation unknown: -m or at least one .o file required");
@@ -1170,49 +1232,72 @@ void LinkerDriver::inferMachineType() {
// Parse -z max-page-size=<value>. The default value is defined by
// each target.
-static uint64_t getMaxPageSize(opt::InputArgList &Args) {
- uint64_t Val = args::getZOptionValue(Args, OPT_z, "max-page-size",
- Target->DefaultMaxPageSize);
- if (!isPowerOf2_64(Val))
+static uint64_t getMaxPageSize(opt::InputArgList &args) {
+ uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size",
+ target->defaultMaxPageSize);
+ if (!isPowerOf2_64(val))
error("max-page-size: value isn't a power of 2");
- return Val;
+ if (config->nmagic || config->omagic) {
+ if (val != target->defaultMaxPageSize)
+ warn("-z max-page-size set, but paging disabled by omagic or nmagic");
+ return 1;
+ }
+ return val;
+}
+
+// Parse -z common-page-size=<value>. The default value is defined by
+// each target.
+static uint64_t getCommonPageSize(opt::InputArgList &args) {
+ uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size",
+ target->defaultCommonPageSize);
+ if (!isPowerOf2_64(val))
+ error("common-page-size: value isn't a power of 2");
+ if (config->nmagic || config->omagic) {
+ if (val != target->defaultCommonPageSize)
+ warn("-z common-page-size set, but paging disabled by omagic or nmagic");
+ return 1;
+ }
+ // commonPageSize can't be larger than maxPageSize.
+ if (val > config->maxPageSize)
+ val = config->maxPageSize;
+ return val;
}
// Parses -image-base option.
-static Optional<uint64_t> getImageBase(opt::InputArgList &Args) {
- // Because we are using "Config->MaxPageSize" here, this function has to be
+static Optional<uint64_t> getImageBase(opt::InputArgList &args) {
+ // Because we are using "Config->maxPageSize" here, this function has to be
// called after the variable is initialized.
- auto *Arg = Args.getLastArg(OPT_image_base);
- if (!Arg)
+ auto *arg = args.getLastArg(OPT_image_base);
+ if (!arg)
return None;
- StringRef S = Arg->getValue();
- uint64_t V;
- if (!to_integer(S, V)) {
- error("-image-base: number expected, but got " + S);
+ StringRef s = arg->getValue();
+ uint64_t v;
+ if (!to_integer(s, v)) {
+ error("-image-base: number expected, but got " + s);
return 0;
}
- if ((V % Config->MaxPageSize) != 0)
- warn("-image-base: address isn't multiple of page size: " + S);
- return V;
+ if ((v % config->maxPageSize) != 0)
+ warn("-image-base: address isn't multiple of page size: " + s);
+ return v;
}
// Parses `--exclude-libs=lib,lib,...`.
// The library names may be delimited by commas or colons.
-static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
- DenseSet<StringRef> Ret;
- for (auto *Arg : Args.filtered(OPT_exclude_libs)) {
- StringRef S = Arg->getValue();
+static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &args) {
+ DenseSet<StringRef> ret;
+ for (auto *arg : args.filtered(OPT_exclude_libs)) {
+ StringRef s = arg->getValue();
for (;;) {
- size_t Pos = S.find_first_of(",:");
- if (Pos == StringRef::npos)
+ size_t pos = s.find_first_of(",:");
+ if (pos == StringRef::npos)
break;
- Ret.insert(S.substr(0, Pos));
- S = S.substr(Pos + 1);
+ ret.insert(s.substr(0, pos));
+ s = s.substr(pos + 1);
}
- Ret.insert(S);
+ ret.insert(s);
}
- return Ret;
+ return ret;
}
// Handles the -exclude-libs option. If a static library file is specified
@@ -1221,139 +1306,247 @@ static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
// A special library name "ALL" means all archive files.
//
// This is not a popular option, but some programs such as bionic libc use it.
-template <class ELFT>
-static void excludeLibs(opt::InputArgList &Args) {
- DenseSet<StringRef> Libs = getExcludeLibs(Args);
- bool All = Libs.count("ALL");
-
- auto Visit = [&](InputFile *File) {
- if (!File->ArchiveName.empty())
- if (All || Libs.count(path::filename(File->ArchiveName)))
- for (Symbol *Sym : File->getSymbols())
- if (!Sym->isLocal() && Sym->File == File)
- Sym->VersionId = VER_NDX_LOCAL;
+static void excludeLibs(opt::InputArgList &args) {
+ DenseSet<StringRef> libs = getExcludeLibs(args);
+ bool all = libs.count("ALL");
+
+ auto visit = [&](InputFile *file) {
+ if (!file->archiveName.empty())
+ if (all || libs.count(path::filename(file->archiveName)))
+ for (Symbol *sym : file->getSymbols())
+ if (!sym->isLocal() && sym->file == file)
+ sym->versionId = VER_NDX_LOCAL;
};
- for (InputFile *File : ObjectFiles)
- Visit(File);
+ for (InputFile *file : objectFiles)
+ visit(file);
- for (BitcodeFile *File : BitcodeFiles)
- Visit(File);
+ for (BitcodeFile *file : bitcodeFiles)
+ visit(file);
}
// Force Sym to be entered in the output. Used for -u or equivalent.
-template <class ELFT> static void handleUndefined(StringRef Name) {
- Symbol *Sym = Symtab->find(Name);
- if (!Sym)
+static void handleUndefined(Symbol *sym) {
+ // Since a symbol may not be used inside the program, LTO may
+ // eliminate it. Mark the symbol as "used" to prevent it.
+ sym->isUsedInRegularObj = true;
+
+ if (sym->isLazy())
+ sym->fetch();
+}
+
+// As an extention to GNU linkers, lld supports a variant of `-u`
+// which accepts wildcard patterns. All symbols that match a given
+// pattern are handled as if they were given by `-u`.
+static void handleUndefinedGlob(StringRef arg) {
+ Expected<GlobPattern> pat = GlobPattern::create(arg);
+ if (!pat) {
+ error("--undefined-glob: " + toString(pat.takeError()));
return;
+ }
- // Since symbol S may not be used inside the program, LTO may
- // eliminate it. Mark the symbol as "used" to prevent it.
- Sym->IsUsedInRegularObj = true;
+ std::vector<Symbol *> syms;
+ symtab->forEachSymbol([&](Symbol *sym) {
+ // Calling Sym->fetch() from here is not safe because it may
+ // add new symbols to the symbol table, invalidating the
+ // current iterator. So we just keep a note.
+ if (pat->match(sym->getName()))
+ syms.push_back(sym);
+ });
- if (Sym->isLazy())
- Symtab->fetchLazy<ELFT>(Sym);
+ for (Symbol *sym : syms)
+ handleUndefined(sym);
}
-template <class ELFT> static void handleLibcall(StringRef Name) {
- Symbol *Sym = Symtab->find(Name);
- if (!Sym || !Sym->isLazy())
+static void handleLibcall(StringRef name) {
+ Symbol *sym = symtab->find(name);
+ if (!sym || !sym->isLazy())
return;
- MemoryBufferRef MB;
- if (auto *LO = dyn_cast<LazyObject>(Sym))
- MB = LO->File->MB;
+ MemoryBufferRef mb;
+ if (auto *lo = dyn_cast<LazyObject>(sym))
+ mb = lo->file->mb;
else
- MB = cast<LazyArchive>(Sym)->getMemberBuffer();
+ mb = cast<LazyArchive>(sym)->getMemberBuffer();
- if (isBitcode(MB))
- Symtab->fetchLazy<ELFT>(Sym);
+ if (isBitcode(mb))
+ sym->fetch();
+}
+
+// Replaces common symbols with defined symbols reside in .bss sections.
+// This function is called after all symbol names are resolved. As a
+// result, the passes after the symbol resolution won't see any
+// symbols of type CommonSymbol.
+static void replaceCommonSymbols() {
+ symtab->forEachSymbol([](Symbol *sym) {
+ auto *s = dyn_cast<CommonSymbol>(sym);
+ if (!s)
+ return;
+
+ auto *bss = make<BssSection>("COMMON", s->size, s->alignment);
+ bss->file = s->file;
+ bss->markDead();
+ inputSections.push_back(bss);
+ s->replace(Defined{s->file, s->getName(), s->binding, s->stOther, s->type,
+ /*value=*/0, s->size, bss});
+ });
}
// If all references to a DSO happen to be weak, the DSO is not added
// to DT_NEEDED. If that happens, we need to eliminate shared symbols
// created from the DSO. Otherwise, they become dangling references
// that point to a non-existent DSO.
-template <class ELFT> static void demoteSharedSymbols() {
- for (Symbol *Sym : Symtab->getSymbols()) {
- if (auto *S = dyn_cast<SharedSymbol>(Sym)) {
- if (!S->getFile<ELFT>().IsNeeded) {
- bool Used = S->Used;
- replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther,
- S->Type);
- S->Used = Used;
- }
- }
- }
+static void demoteSharedSymbols() {
+ symtab->forEachSymbol([](Symbol *sym) {
+ auto *s = dyn_cast<SharedSymbol>(sym);
+ if (!s || s->getFile().isNeeded)
+ return;
+
+ bool used = s->used;
+ s->replace(Undefined{nullptr, s->getName(), STB_WEAK, s->stOther, s->type});
+ s->used = used;
+ });
}
-// The section referred to by S is considered address-significant. Set the
-// KeepUnique flag on the section if appropriate.
-static void markAddrsig(Symbol *S) {
- if (auto *D = dyn_cast_or_null<Defined>(S))
- if (D->Section)
+// The section referred to by `s` is considered address-significant. Set the
+// keepUnique flag on the section if appropriate.
+static void markAddrsig(Symbol *s) {
+ if (auto *d = dyn_cast_or_null<Defined>(s))
+ if (d->section)
// We don't need to keep text sections unique under --icf=all even if they
// are address-significant.
- if (Config->ICF == ICFLevel::Safe || !(D->Section->Flags & SHF_EXECINSTR))
- D->Section->KeepUnique = true;
+ if (config->icf == ICFLevel::Safe || !(d->section->flags & SHF_EXECINSTR))
+ d->section->keepUnique = true;
}
// Record sections that define symbols mentioned in --keep-unique <symbol>
// and symbols referred to by address-significance tables. These sections are
// ineligible for ICF.
template <class ELFT>
-static void findKeepUniqueSections(opt::InputArgList &Args) {
- for (auto *Arg : Args.filtered(OPT_keep_unique)) {
- StringRef Name = Arg->getValue();
- auto *D = dyn_cast_or_null<Defined>(Symtab->find(Name));
- if (!D || !D->Section) {
- warn("could not find symbol " + Name + " to keep unique");
+static void findKeepUniqueSections(opt::InputArgList &args) {
+ for (auto *arg : args.filtered(OPT_keep_unique)) {
+ StringRef name = arg->getValue();
+ auto *d = dyn_cast_or_null<Defined>(symtab->find(name));
+ if (!d || !d->section) {
+ warn("could not find symbol " + name + " to keep unique");
continue;
}
- D->Section->KeepUnique = true;
+ d->section->keepUnique = true;
}
// --icf=all --ignore-data-address-equality means that we can ignore
// the dynsym and address-significance tables entirely.
- if (Config->ICF == ICFLevel::All && Config->IgnoreDataAddressEquality)
+ if (config->icf == ICFLevel::All && config->ignoreDataAddressEquality)
return;
// Symbols in the dynsym could be address-significant in other executables
// or DSOs, so we conservatively mark them as address-significant.
- for (Symbol *S : Symtab->getSymbols())
- if (S->includeInDynsym())
- markAddrsig(S);
+ symtab->forEachSymbol([&](Symbol *sym) {
+ if (sym->includeInDynsym())
+ markAddrsig(sym);
+ });
// Visit the address-significance table in each object file and mark each
// referenced symbol as address-significant.
- for (InputFile *F : ObjectFiles) {
- auto *Obj = cast<ObjFile<ELFT>>(F);
- ArrayRef<Symbol *> Syms = Obj->getSymbols();
- if (Obj->AddrsigSec) {
- ArrayRef<uint8_t> Contents =
- check(Obj->getObj().getSectionContents(Obj->AddrsigSec));
- const uint8_t *Cur = Contents.begin();
- while (Cur != Contents.end()) {
- unsigned Size;
- const char *Err;
- uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err);
- if (Err)
- fatal(toString(F) + ": could not decode addrsig section: " + Err);
- markAddrsig(Syms[SymIndex]);
- Cur += Size;
+ for (InputFile *f : objectFiles) {
+ auto *obj = cast<ObjFile<ELFT>>(f);
+ ArrayRef<Symbol *> syms = obj->getSymbols();
+ if (obj->addrsigSec) {
+ ArrayRef<uint8_t> contents =
+ check(obj->getObj().getSectionContents(obj->addrsigSec));
+ const uint8_t *cur = contents.begin();
+ while (cur != contents.end()) {
+ unsigned size;
+ const char *err;
+ uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err);
+ if (err)
+ fatal(toString(f) + ": could not decode addrsig section: " + err);
+ markAddrsig(syms[symIndex]);
+ cur += size;
}
} else {
// If an object file does not have an address-significance table,
// conservatively mark all of its symbols as address-significant.
- for (Symbol *S : Syms)
- markAddrsig(S);
+ for (Symbol *s : syms)
+ markAddrsig(s);
+ }
+ }
+}
+
+// This function reads a symbol partition specification section. These sections
+// are used to control which partition a symbol is allocated to. See
+// https://lld.llvm.org/Partitions.html for more details on partitions.
+template <typename ELFT>
+static void readSymbolPartitionSection(InputSectionBase *s) {
+ // Read the relocation that refers to the partition's entry point symbol.
+ Symbol *sym;
+ if (s->areRelocsRela)
+ sym = &s->getFile<ELFT>()->getRelocTargetSym(s->template relas<ELFT>()[0]);
+ else
+ sym = &s->getFile<ELFT>()->getRelocTargetSym(s->template rels<ELFT>()[0]);
+ if (!isa<Defined>(sym) || !sym->includeInDynsym())
+ return;
+
+ StringRef partName = reinterpret_cast<const char *>(s->data().data());
+ for (Partition &part : partitions) {
+ if (part.name == partName) {
+ sym->partition = part.getNumber();
+ return;
}
}
+
+ // Forbid partitions from being used on incompatible targets, and forbid them
+ // from being used together with various linker features that assume a single
+ // set of output sections.
+ if (script->hasSectionsCommand)
+ error(toString(s->file) +
+ ": partitions cannot be used with the SECTIONS command");
+ if (script->hasPhdrsCommands())
+ error(toString(s->file) +
+ ": partitions cannot be used with the PHDRS command");
+ if (!config->sectionStartMap.empty())
+ error(toString(s->file) + ": partitions cannot be used with "
+ "--section-start, -Ttext, -Tdata or -Tbss");
+ if (config->emachine == EM_MIPS)
+ error(toString(s->file) + ": partitions cannot be used on this target");
+
+ // Impose a limit of no more than 254 partitions. This limit comes from the
+ // sizes of the Partition fields in InputSectionBase and Symbol, as well as
+ // the amount of space devoted to the partition number in RankFlags.
+ if (partitions.size() == 254)
+ fatal("may not have more than 254 partitions");
+
+ partitions.emplace_back();
+ Partition &newPart = partitions.back();
+ newPart.name = partName;
+ sym->partition = newPart.getNumber();
}
-template <class ELFT> static Symbol *addUndefined(StringRef Name) {
- return Symtab->addUndefined<ELFT>(Name, STB_GLOBAL, STV_DEFAULT, 0, false,
- nullptr);
+static Symbol *addUndefined(StringRef name) {
+ return symtab->addSymbol(
+ Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0});
+}
+
+// This function is where all the optimizations of link-time
+// optimization takes place. When LTO is in use, some input files are
+// not in native object file format but in the LLVM bitcode format.
+// This function compiles bitcode files into a few big native files
+// using LLVM functions and replaces bitcode symbols with the results.
+// Because all bitcode files that the program consists of are passed to
+// the compiler at once, it can do a whole-program optimization.
+template <class ELFT> void LinkerDriver::compileBitcodeFiles() {
+ // Compile bitcode files and replace bitcode symbols.
+ lto.reset(new BitcodeCompiler);
+ for (BitcodeFile *file : bitcodeFiles)
+ lto->add(*file);
+
+ for (InputFile *file : lto->compile()) {
+ auto *obj = cast<ObjFile<ELFT>>(file);
+ obj->parse(/*ignoreComdats=*/true);
+ for (Symbol *sym : obj->getGlobalSymbols())
+ sym->parseSymbolVersion();
+ objectFiles.push_back(file);
+ }
}
// The --wrap option is a feature to rename symbols so that you can write
@@ -1365,9 +1558,9 @@ template <class ELFT> static Symbol *addUndefined(StringRef Name) {
//
// This data structure is instantiated for each -wrap option.
struct WrappedSymbol {
- Symbol *Sym;
- Symbol *Real;
- Symbol *Wrap;
+ Symbol *sym;
+ Symbol *real;
+ Symbol *wrap;
};
// Handles -wrap option.
@@ -1375,34 +1568,33 @@ struct WrappedSymbol {
// This function instantiates wrapper symbols. At this point, they seem
// like they are not being used at all, so we explicitly set some flags so
// that LTO won't eliminate them.
-template <class ELFT>
-static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) {
- std::vector<WrappedSymbol> V;
- DenseSet<StringRef> Seen;
+static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &args) {
+ std::vector<WrappedSymbol> v;
+ DenseSet<StringRef> seen;
- for (auto *Arg : Args.filtered(OPT_wrap)) {
- StringRef Name = Arg->getValue();
- if (!Seen.insert(Name).second)
+ for (auto *arg : args.filtered(OPT_wrap)) {
+ StringRef name = arg->getValue();
+ if (!seen.insert(name).second)
continue;
- Symbol *Sym = Symtab->find(Name);
- if (!Sym)
+ Symbol *sym = symtab->find(name);
+ if (!sym)
continue;
- Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
- Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
- V.push_back({Sym, Real, Wrap});
+ Symbol *real = addUndefined(saver.save("__real_" + name));
+ Symbol *wrap = addUndefined(saver.save("__wrap_" + name));
+ v.push_back({sym, real, wrap});
// We want to tell LTO not to inline symbols to be overwritten
// because LTO doesn't know the final symbol contents after renaming.
- Real->CanInline = false;
- Sym->CanInline = false;
+ real->canInline = false;
+ sym->canInline = false;
// Tell LTO not to eliminate these symbols.
- Sym->IsUsedInRegularObj = true;
- Wrap->IsUsedInRegularObj = true;
+ sym->isUsedInRegularObj = true;
+ wrap->isUsedInRegularObj = true;
}
- return V;
+ return v;
}
// Do renaming for -wrap by updating pointers to symbols.
@@ -1410,27 +1602,63 @@ static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) {
// When this function is executed, only InputFiles and symbol table
// contain pointers to symbol objects. We visit them to replace pointers,
// so that wrapped symbols are swapped as instructed by the command line.
-template <class ELFT> static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) {
- DenseMap<Symbol *, Symbol *> Map;
- for (const WrappedSymbol &W : Wrapped) {
- Map[W.Sym] = W.Wrap;
- Map[W.Real] = W.Sym;
+static void wrapSymbols(ArrayRef<WrappedSymbol> wrapped) {
+ DenseMap<Symbol *, Symbol *> map;
+ for (const WrappedSymbol &w : wrapped) {
+ map[w.sym] = w.wrap;
+ map[w.real] = w.sym;
}
// Update pointers in input files.
- parallelForEach(ObjectFiles, [&](InputFile *File) {
- std::vector<Symbol *> &Syms = File->getMutableSymbols();
- for (size_t I = 0, E = Syms.size(); I != E; ++I)
- if (Symbol *S = Map.lookup(Syms[I]))
- Syms[I] = S;
+ parallelForEach(objectFiles, [&](InputFile *file) {
+ MutableArrayRef<Symbol *> syms = file->getMutableSymbols();
+ for (size_t i = 0, e = syms.size(); i != e; ++i)
+ if (Symbol *s = map.lookup(syms[i]))
+ syms[i] = s;
});
// Update pointers in the symbol table.
- for (const WrappedSymbol &W : Wrapped)
- Symtab->wrap(W.Sym, W.Real, W.Wrap);
+ for (const WrappedSymbol &w : wrapped)
+ symtab->wrap(w.sym, w.real, w.wrap);
}
-static const char *LibcallRoutineNames[] = {
+// To enable CET (x86's hardware-assited control flow enforcement), each
+// source file must be compiled with -fcf-protection. Object files compiled
+// with the flag contain feature flags indicating that they are compatible
+// with CET. We enable the feature only when all object files are compatible
+// with CET.
+//
+// This function returns the merged feature flags. If 0, we cannot enable CET.
+// This is also the case with AARCH64's BTI and PAC which use the similar
+// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism.
+//
+// Note that the CET-aware PLT is not implemented yet. We do error
+// check only.
+template <class ELFT> static uint32_t getAndFeatures() {
+ if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
+ config->emachine != EM_AARCH64)
+ return 0;
+
+ uint32_t ret = -1;
+ for (InputFile *f : objectFiles) {
+ uint32_t features = cast<ObjFile<ELFT>>(f)->andFeatures;
+ if (config->forceBTI && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
+ warn(toString(f) + ": --force-bti: file does not have BTI property");
+ features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ } else if (!features && config->requireCET)
+ error(toString(f) + ": --require-cet: file is not compatible with CET");
+ ret &= features;
+ }
+
+ // Force enable pointer authentication Plt, we don't warn in this case as
+ // this does not require support in the object for correctness.
+ if (config->pacPlt)
+ ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
+
+ return ret;
+}
+
+static const char *libcallRoutineNames[] = {
#define HANDLE_LIBCALL(code, name) name,
#include "llvm/IR/RuntimeLibcalls.def"
#undef HANDLE_LIBCALL
@@ -1438,53 +1666,48 @@ static const char *LibcallRoutineNames[] = {
// Do actual linking. Note that when this function is called,
// all linker scripts have already been parsed.
-template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
- Target = getTarget();
- InX<ELFT>::VerSym = nullptr;
- InX<ELFT>::VerNeed = nullptr;
-
- Config->MaxPageSize = getMaxPageSize(Args);
- Config->ImageBase = getImageBase(Args);
-
+template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// If a -hash-style option was not given, set to a default value,
// which varies depending on the target.
- if (!Args.hasArg(OPT_hash_style)) {
- if (Config->EMachine == EM_MIPS)
- Config->SysvHash = true;
+ if (!args.hasArg(OPT_hash_style)) {
+ if (config->emachine == EM_MIPS)
+ config->sysvHash = true;
else
- Config->SysvHash = Config->GnuHash = true;
+ config->sysvHash = config->gnuHash = true;
}
// Default output filename is "a.out" by the Unix tradition.
- if (Config->OutputFile.empty())
- Config->OutputFile = "a.out";
+ if (config->outputFile.empty())
+ config->outputFile = "a.out";
// Fail early if the output file or map file is not writable. If a user has a
// long link, e.g. due to a large LTO link, they do not wish to run it and
// find that it failed because there was a mistake in their command-line.
- if (auto E = tryCreateFile(Config->OutputFile))
- error("cannot open output file " + Config->OutputFile + ": " + E.message());
- if (auto E = tryCreateFile(Config->MapFile))
- error("cannot open map file " + Config->MapFile + ": " + E.message());
+ if (auto e = tryCreateFile(config->outputFile))
+ error("cannot open output file " + config->outputFile + ": " + e.message());
+ if (auto e = tryCreateFile(config->mapFile))
+ error("cannot open map file " + config->mapFile + ": " + e.message());
if (errorCount())
return;
// Use default entry point name if no name was given via the command
// line nor linker scripts. For some reason, MIPS entry point name is
// different from others.
- Config->WarnMissingEntry =
- (!Config->Entry.empty() || (!Config->Shared && !Config->Relocatable));
- if (Config->Entry.empty() && !Config->Relocatable)
- Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start";
+ config->warnMissingEntry =
+ (!config->entry.empty() || (!config->shared && !config->relocatable));
+ if (config->entry.empty() && !config->relocatable)
+ config->entry = (config->emachine == EM_MIPS) ? "__start" : "_start";
// Handle --trace-symbol.
- for (auto *Arg : Args.filtered(OPT_trace_symbol))
- Symtab->trace(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_trace_symbol))
+ symtab->insert(arg->getValue())->traced = true;
// Add all files to the symbol table. This will add almost all
- // symbols that we need to the symbol table.
- for (InputFile *F : Files)
- Symtab->addFile<ELFT>(F);
+ // symbols that we need to the symbol table. This process might
+ // add files to the link, via autolinking, these files are always
+ // appended to the Files vector.
+ for (size_t i = 0; i < files.size(); ++i)
+ parseFile(files[i]);
// Now that we have every file, we can decide if we will need a
// dynamic symbol table.
@@ -1492,20 +1715,26 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// producing a shared library.
// We also need one if any shared libraries are used and for pie executables
// (probably because the dynamic linker needs it).
- Config->HasDynSymTab =
- !SharedFiles.empty() || Config->Pic || Config->ExportDynamic;
+ config->hasDynSymTab =
+ !sharedFiles.empty() || config->isPic || config->exportDynamic;
// Some symbols (such as __ehdr_start) are defined lazily only when there
// are undefined symbols for them, so we add these to trigger that logic.
- for (StringRef Name : Script->ReferencedSymbols)
- addUndefined<ELFT>(Name);
+ for (StringRef name : script->referencedSymbols)
+ addUndefined(name);
// Handle the `--undefined <sym>` options.
- for (StringRef S : Config->Undefined)
- handleUndefined<ELFT>(S);
+ for (StringRef arg : config->undefined)
+ if (Symbol *sym = symtab->find(arg))
+ handleUndefined(sym);
// If an entry symbol is in a static archive, pull out that file now.
- handleUndefined<ELFT>(Config->Entry);
+ if (Symbol *sym = symtab->find(config->entry))
+ handleUndefined(sym);
+
+ // Handle the `--undefined-glob <pattern>` options.
+ for (StringRef pat : args::getStrings(args, OPT_undefined_glob))
+ handleUndefinedGlob(pat);
// If any of our inputs are bitcode files, the LTO code generator may create
// references to certain library functions that might not be explicit in the
@@ -1524,9 +1753,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// to, i.e. if the symbol's definition is in bitcode. Any other required
// libcall symbols will be added to the link after LTO when we add the LTO
// object file to the link.
- if (!BitcodeFiles.empty())
- for (const char *S : LibcallRoutineNames)
- handleLibcall<ELFT>(S);
+ if (!bitcodeFiles.empty())
+ for (const char *s : libcallRoutineNames)
+ handleLibcall(s);
// Return if there were name resolution errors.
if (errorCount())
@@ -1534,27 +1763,27 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Now when we read all script files, we want to finalize order of linker
// script commands, which can be not yet final because of INSERT commands.
- Script->processInsertCommands();
+ script->processInsertCommands();
// We want to declare linker script's symbols early,
// so that we can version them.
// They also might be exported if referenced by DSOs.
- Script->declareSymbols();
+ script->declareSymbols();
// Handle the -exclude-libs option.
- if (Args.hasArg(OPT_exclude_libs))
- excludeLibs<ELFT>(Args);
+ if (args.hasArg(OPT_exclude_libs))
+ excludeLibs(args);
- // Create ElfHeader early. We need a dummy section in
+ // Create elfHeader early. We need a dummy section in
// addReservedSymbols to mark the created symbols as not absolute.
- Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC);
- Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr);
+ Out::elfHeader = make<OutputSection>("", 0, SHF_ALLOC);
+ Out::elfHeader->size = sizeof(typename ELFT::Ehdr);
// Create wrapped symbols for -wrap option.
- std::vector<WrappedSymbol> Wrapped = addWrappedSymbols<ELFT>(Args);
+ std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args);
// We need to create some reserved symbols such as _end. Create them.
- if (!Config->Relocatable)
+ if (!config->relocatable)
addReservedSymbols();
// Apply version scripts.
@@ -1562,84 +1791,118 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// For a relocatable output, version scripts don't make sense, and
// parsing a symbol version string (e.g. dropping "@ver1" from a symbol
// name "foo@ver1") rather do harm, so we don't call this if -r is given.
- if (!Config->Relocatable)
- Symtab->scanVersionScript();
+ if (!config->relocatable)
+ symtab->scanVersionScript();
// Do link-time optimization if given files are LLVM bitcode files.
// This compiles bitcode files into real object files.
//
// With this the symbol table should be complete. After this, no new names
// except a few linker-synthesized ones will be added to the symbol table.
- Symtab->addCombinedLTOObject<ELFT>();
+ compileBitcodeFiles<ELFT>();
if (errorCount())
return;
// If -thinlto-index-only is given, we should create only "index
// files" and not object files. Index file creation is already done
// in addCombinedLTOObject, so we are done if that's the case.
- if (Config->ThinLTOIndexOnly)
+ if (config->thinLTOIndexOnly)
return;
// Likewise, --plugin-opt=emit-llvm is an option to make LTO create
// an output file in bitcode and exit, so that you can just get a
// combined bitcode file.
- if (Config->EmitLLVM)
+ if (config->emitLLVM)
return;
// Apply symbol renames for -wrap.
- if (!Wrapped.empty())
- wrapSymbols<ELFT>(Wrapped);
+ if (!wrapped.empty())
+ wrapSymbols(wrapped);
// Now that we have a complete list of input files.
// Beyond this point, no new files are added.
// Aggregate all input sections into one place.
- for (InputFile *F : ObjectFiles)
- for (InputSectionBase *S : F->getSections())
- if (S && S != &InputSection::Discarded)
- InputSections.push_back(S);
- for (BinaryFile *F : BinaryFiles)
- for (InputSectionBase *S : F->getSections())
- InputSections.push_back(cast<InputSection>(S));
-
- // We do not want to emit debug sections if --strip-all
- // or -strip-debug are given.
- if (Config->Strip != StripPolicy::None)
- llvm::erase_if(InputSections, [](InputSectionBase *S) {
- return S->Name.startswith(".debug") || S->Name.startswith(".zdebug");
- });
-
- Config->EFlags = Target->calcEFlags();
-
- if (Config->EMachine == EM_ARM) {
+ for (InputFile *f : objectFiles)
+ for (InputSectionBase *s : f->getSections())
+ if (s && s != &InputSection::discarded)
+ inputSections.push_back(s);
+ for (BinaryFile *f : binaryFiles)
+ for (InputSectionBase *s : f->getSections())
+ inputSections.push_back(cast<InputSection>(s));
+
+ llvm::erase_if(inputSections, [](InputSectionBase *s) {
+ if (s->type == SHT_LLVM_SYMPART) {
+ readSymbolPartitionSection<ELFT>(s);
+ return true;
+ }
+
+ // We do not want to emit debug sections if --strip-all
+ // or -strip-debug are given.
+ return config->strip != StripPolicy::None &&
+ (s->name.startswith(".debug") || s->name.startswith(".zdebug"));
+ });
+
+ // Now that the number of partitions is fixed, save a pointer to the main
+ // partition.
+ mainPart = &partitions[0];
+
+ // Read .note.gnu.property sections from input object files which
+ // contain a hint to tweak linker's and loader's behaviors.
+ config->andFeatures = getAndFeatures<ELFT>();
+
+ // The Target instance handles target-specific stuff, such as applying
+ // relocations or writing a PLT section. It also contains target-dependent
+ // values such as a default image base address.
+ target = getTarget();
+
+ config->eflags = target->calcEFlags();
+ // maxPageSize (sometimes called abi page size) is the maximum page size that
+ // the output can be run on. For example if the OS can use 4k or 64k page
+ // sizes then maxPageSize must be 64k for the output to be useable on both.
+ // All important alignment decisions must use this value.
+ config->maxPageSize = getMaxPageSize(args);
+ // commonPageSize is the most common page size that the output will be run on.
+ // For example if an OS can use 4k or 64k page sizes and 4k is more common
+ // than 64k then commonPageSize is set to 4k. commonPageSize can be used for
+ // optimizations such as DATA_SEGMENT_ALIGN in linker scripts. LLD's use of it
+ // is limited to writing trap instructions on the last executable segment.
+ config->commonPageSize = getCommonPageSize(args);
+
+ config->imageBase = getImageBase(args);
+
+ if (config->emachine == EM_ARM) {
// FIXME: These warnings can be removed when lld only uses these features
// when the input objects have been compiled with an architecture that
// supports them.
- if (Config->ARMHasBlx == false)
+ if (config->armHasBlx == false)
warn("lld uses blx instruction, no object with architecture supporting "
"feature detected");
}
// This adds a .comment section containing a version string. We have to add it
// before mergeSections because the .comment section is a mergeable section.
- if (!Config->Relocatable)
- InputSections.push_back(createCommentSection());
+ if (!config->relocatable)
+ inputSections.push_back(createCommentSection());
+
+ // Replace common symbols with regular symbols.
+ replaceCommonSymbols();
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
// and identical code folding.
splitSections<ELFT>();
markLive<ELFT>();
- demoteSharedSymbols<ELFT>();
+ demoteSharedSymbols();
mergeSections();
- if (Config->ICF != ICFLevel::None) {
- findKeepUniqueSections<ELFT>(Args);
+ if (config->icf != ICFLevel::None) {
+ findKeepUniqueSections<ELFT>(args);
doIcf<ELFT>();
}
// Read the callgraph now that we know what was gced or icfed
- if (Config->CallGraphProfileSort) {
- if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file))
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- readCallGraph(*Buffer);
+ if (config->callGraphProfileSort) {
+ if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file))
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ readCallGraph(*buffer);
readCallGraphsFromObjectFiles<ELFT>();
}
diff --git a/ELF/Driver.h b/ELF/Driver.h
index 81d7f608e588f..3115e28d16698 100644
--- a/ELF/Driver.h
+++ b/ELF/Driver.h
@@ -1,15 +1,15 @@
//===- Driver.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_DRIVER_H
#define LLD_ELF_DRIVER_H
+#include "LTO.h"
#include "SymbolTable.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Reproduce.h"
@@ -22,34 +22,37 @@
namespace lld {
namespace elf {
-extern class LinkerDriver *Driver;
+extern class LinkerDriver *driver;
class LinkerDriver {
public:
- void main(ArrayRef<const char *> Args);
- void addFile(StringRef Path, bool WithLOption);
- void addLibrary(StringRef Name);
+ void main(ArrayRef<const char *> args);
+ void addFile(StringRef path, bool withLOption);
+ void addLibrary(StringRef name);
private:
- void readConfigs(llvm::opt::InputArgList &Args);
- void createFiles(llvm::opt::InputArgList &Args);
+ void createFiles(llvm::opt::InputArgList &args);
void inferMachineType();
- template <class ELFT> void link(llvm::opt::InputArgList &Args);
+ template <class ELFT> void link(llvm::opt::InputArgList &args);
+ template <class ELFT> void compileBitcodeFiles();
// True if we are in --whole-archive and --no-whole-archive.
- bool InWholeArchive = false;
+ bool inWholeArchive = false;
// True if we are in --start-lib and --end-lib.
- bool InLib = false;
+ bool inLib = false;
+
+ // For LTO.
+ std::unique_ptr<BitcodeCompiler> lto;
- std::vector<InputFile *> Files;
+ std::vector<InputFile *> files;
};
// Parses command line options.
class ELFOptTable : public llvm::opt::OptTable {
public:
ELFOptTable();
- llvm::opt::InputArgList parse(ArrayRef<const char *> Argv);
+ llvm::opt::InputArgList parse(ArrayRef<const char *> argv);
};
// Create enum with OPT_xxx values for each option in Options.td
@@ -61,11 +64,12 @@ enum {
};
void printHelp();
-std::string createResponseFile(const llvm::opt::InputArgList &Args);
+std::string createResponseFile(const llvm::opt::InputArgList &args);
-llvm::Optional<std::string> findFromSearchPaths(StringRef Path);
-llvm::Optional<std::string> searchScript(StringRef Path);
-llvm::Optional<std::string> searchLibrary(StringRef Path);
+llvm::Optional<std::string> findFromSearchPaths(StringRef path);
+llvm::Optional<std::string> searchScript(StringRef path);
+llvm::Optional<std::string> searchLibraryBaseName(StringRef path);
+llvm::Optional<std::string> searchLibrary(StringRef path);
} // namespace elf
} // namespace lld
diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp
index e51d02e38da1e..87f0aa2a9827b 100644
--- a/ELF/DriverUtils.cpp
+++ b/ELF/DriverUtils.cpp
@@ -1,9 +1,8 @@
//===- DriverUtils.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -42,7 +41,7 @@ using namespace lld::elf;
#undef PREFIX
// Create table mapping all options defined in Options.td
-static const opt::OptTable::Info OptInfo[] = {
+static const opt::OptTable::Info optInfo[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
{X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
@@ -50,36 +49,36 @@ static const opt::OptTable::Info OptInfo[] = {
#undef OPTION
};
-ELFOptTable::ELFOptTable() : OptTable(OptInfo) {}
+ELFOptTable::ELFOptTable() : OptTable(optInfo) {}
// Set color diagnostics according to -color-diagnostics={auto,always,never}
// or -no-color-diagnostics flags.
-static void handleColorDiagnostics(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
+static void handleColorDiagnostics(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
OPT_no_color_diagnostics);
- if (!Arg)
+ if (!arg)
return;
- if (Arg->getOption().getID() == OPT_color_diagnostics) {
- errorHandler().ColorDiagnostics = true;
- } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
- errorHandler().ColorDiagnostics = false;
+ if (arg->getOption().getID() == OPT_color_diagnostics) {
+ errorHandler().colorDiagnostics = true;
+ } else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
+ errorHandler().colorDiagnostics = false;
} else {
- StringRef S = Arg->getValue();
- if (S == "always")
- errorHandler().ColorDiagnostics = true;
- else if (S == "never")
- errorHandler().ColorDiagnostics = false;
- else if (S != "auto")
- error("unknown option: --color-diagnostics=" + S);
+ StringRef s = arg->getValue();
+ if (s == "always")
+ errorHandler().colorDiagnostics = true;
+ else if (s == "never")
+ errorHandler().colorDiagnostics = false;
+ else if (s != "auto")
+ error("unknown option: --color-diagnostics=" + s);
}
}
-static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
- if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) {
- StringRef S = Arg->getValue();
- if (S != "windows" && S != "posix")
- error("invalid response file quoting: " + S);
- if (S == "windows")
+static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
+ if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
+ StringRef s = arg->getValue();
+ if (s != "windows" && s != "posix")
+ error("invalid response file quoting: " + s);
+ if (s == "windows")
return cl::TokenizeWindowsCommandLine;
return cl::TokenizeGNUCommandLine;
}
@@ -97,50 +96,56 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
// `--plugin-opt <foo>` is converted to `--plugin-opt=<foo>`. This is a
// bit hacky, but looks like it is still better than handling --plugin-opt
// options by hand.
-static void concatLTOPluginOptions(SmallVectorImpl<const char *> &Args) {
- SmallVector<const char *, 256> V;
- for (size_t I = 0, E = Args.size(); I != E; ++I) {
- StringRef S = Args[I];
- if ((S == "-plugin-opt" || S == "--plugin-opt") && I + 1 != E) {
- V.push_back(Saver.save(S + "=" + Args[I + 1]).data());
- ++I;
+static void concatLTOPluginOptions(SmallVectorImpl<const char *> &args) {
+ SmallVector<const char *, 256> v;
+ for (size_t i = 0, e = args.size(); i != e; ++i) {
+ StringRef s = args[i];
+ if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) {
+ v.push_back(saver.save(s + "=" + args[i + 1]).data());
+ ++i;
} else {
- V.push_back(Args[I]);
+ v.push_back(args[i]);
}
}
- Args = std::move(V);
+ args = std::move(v);
}
// Parses a given list of options.
-opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
+opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) {
// Make InputArgList from string vectors.
- unsigned MissingIndex;
- unsigned MissingCount;
- SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
+ unsigned missingIndex;
+ unsigned missingCount;
+ SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());
// We need to get the quoting style for response files before parsing all
// options so we parse here before and ignore all the options but
// --rsp-quoting.
- opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
+ opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount);
// Expand response files (arguments in the form of @<filename>)
// and then parse the argument again.
- cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
- concatLTOPluginOptions(Vec);
- Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
-
- handleColorDiagnostics(Args);
- if (MissingCount)
- error(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
-
- for (auto *Arg : Args.filtered(OPT_UNKNOWN))
- error("unknown argument: " + Arg->getSpelling());
- return Args;
+ cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec);
+ concatLTOPluginOptions(vec);
+ args = this->ParseArgs(vec, missingIndex, missingCount);
+
+ handleColorDiagnostics(args);
+ if (missingCount)
+ error(Twine(args.getArgString(missingIndex)) + ": missing argument");
+
+ for (auto *arg : args.filtered(OPT_UNKNOWN)) {
+ std::string nearest;
+ if (findNearest(arg->getAsString(args), nearest) > 1)
+ error("unknown argument '" + arg->getAsString(args) + "'");
+ else
+ error("unknown argument '" + arg->getAsString(args) +
+ "', did you mean '" + nearest + "'");
+ }
+ return args;
}
void elf::printHelp() {
ELFOptTable().PrintHelp(
- outs(), (Config->ProgName + " [options] file...").str().c_str(), "lld",
+ outs(), (config->progName + " [options] file...").str().c_str(), "lld",
false /*ShowHidden*/, true /*ShowAllAliases*/);
outs() << "\n";
@@ -149,30 +154,36 @@ void elf::printHelp() {
// in a message for the -help option. If it doesn't match, the scripts
// assume that the linker doesn't support very basic features such as
// shared libraries. Therefore, we need to print out at least "elf".
- outs() << Config->ProgName << ": supported targets: elf\n";
+ outs() << config->progName << ": supported targets: elf\n";
+}
+
+static std::string rewritePath(StringRef s) {
+ if (fs::exists(s))
+ return relativeToRoot(s);
+ return s;
}
// Reconstructs command line arguments so that so that you can re-run
// the same command with the same inputs. This is for --reproduce.
-std::string elf::createResponseFile(const opt::InputArgList &Args) {
- SmallString<0> Data;
- raw_svector_ostream OS(Data);
- OS << "--chroot .\n";
+std::string elf::createResponseFile(const opt::InputArgList &args) {
+ SmallString<0> data;
+ raw_svector_ostream os(data);
+ os << "--chroot .\n";
// Copy the command line to the output while rewriting paths.
- for (auto *Arg : Args) {
- switch (Arg->getOption().getUnaliasedOption().getID()) {
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
case OPT_reproduce:
break;
case OPT_INPUT:
- OS << quote(rewritePath(Arg->getValue())) << "\n";
+ os << quote(rewritePath(arg->getValue())) << "\n";
break;
case OPT_o:
// If -o path contains directories, "lld @response.txt" will likely
// fail because the archive we are creating doesn't contain empty
// directories for the output path (-o doesn't create directories).
// Strip directories to prevent the issue.
- OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n";
+ os << "-o " << quote(sys::path::filename(arg->getValue())) << "\n";
break;
case OPT_dynamic_list:
case OPT_library_path:
@@ -181,58 +192,62 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) {
case OPT_symbol_ordering_file:
case OPT_sysroot:
case OPT_version_script:
- OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue()))
+ os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue()))
<< "\n";
break;
default:
- OS << toString(*Arg) << "\n";
+ os << toString(*arg) << "\n";
}
}
- return Data.str();
+ return data.str();
}
// Find a file by concatenating given paths. If a resulting path
// starts with "=", the character is replaced with a --sysroot value.
-static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) {
- SmallString<128> S;
- if (Path1.startswith("="))
- path::append(S, Config->Sysroot, Path1.substr(1), Path2);
+static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
+ SmallString<128> s;
+ if (path1.startswith("="))
+ path::append(s, config->sysroot, path1.substr(1), path2);
else
- path::append(S, Path1, Path2);
+ path::append(s, path1, path2);
- if (fs::exists(S))
- return S.str().str();
+ if (fs::exists(s))
+ return s.str().str();
return None;
}
-Optional<std::string> elf::findFromSearchPaths(StringRef Path) {
- for (StringRef Dir : Config->SearchPaths)
- if (Optional<std::string> S = findFile(Dir, Path))
- return S;
+Optional<std::string> elf::findFromSearchPaths(StringRef path) {
+ for (StringRef dir : config->searchPaths)
+ if (Optional<std::string> s = findFile(dir, path))
+ return s;
return None;
}
-// This is for -lfoo. We'll look for libfoo.so or libfoo.a from
+// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
// search paths.
-Optional<std::string> elf::searchLibrary(StringRef Name) {
- if (Name.startswith(":"))
- return findFromSearchPaths(Name.substr(1));
-
- for (StringRef Dir : Config->SearchPaths) {
- if (!Config->Static)
- if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".so"))
- return S;
- if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a"))
- return S;
+Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
+ for (StringRef dir : config->searchPaths) {
+ if (!config->isStatic)
+ if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
+ return s;
+ if (Optional<std::string> s = findFile(dir, "lib" + name + ".a"))
+ return s;
}
return None;
}
+// This is for -l<namespec>.
+Optional<std::string> elf::searchLibrary(StringRef name) {
+ if (name.startswith(":"))
+ return findFromSearchPaths(name.substr(1));
+ return searchLibraryBaseName (name);
+}
+
// If a linker/version script doesn't exist in the current directory, we also
// look for the script in the '-L' search paths. This matches the behaviour of
// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
-Optional<std::string> elf::searchScript(StringRef Name) {
- if (fs::exists(Name))
- return Name.str();
- return findFromSearchPaths(Name);
+Optional<std::string> elf::searchScript(StringRef name) {
+ if (fs::exists(name))
+ return name.str();
+ return findFromSearchPaths(name);
}
diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp
index 95d444bdc2a1f..b3245dd01669e 100644
--- a/ELF/EhFrame.cpp
+++ b/ELF/EhFrame.cpp
@@ -1,9 +1,8 @@
//===- EhFrame.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -37,72 +36,72 @@ using namespace lld::elf;
namespace {
class EhReader {
public:
- EhReader(InputSectionBase *S, ArrayRef<uint8_t> D) : IS(S), D(D) {}
+ EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {}
size_t readEhRecordSize();
uint8_t getFdeEncoding();
private:
- template <class P> void failOn(const P *Loc, const Twine &Msg) {
- fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " +
- IS->getObjMsg((const uint8_t *)Loc - IS->data().data()));
+ template <class P> void failOn(const P *loc, const Twine &msg) {
+ fatal("corrupted .eh_frame: " + msg + "\n>>> defined in " +
+ isec->getObjMsg((const uint8_t *)loc - isec->data().data()));
}
uint8_t readByte();
- void skipBytes(size_t Count);
+ void skipBytes(size_t count);
StringRef readString();
void skipLeb128();
void skipAugP();
- InputSectionBase *IS;
- ArrayRef<uint8_t> D;
+ InputSectionBase *isec;
+ ArrayRef<uint8_t> d;
};
}
-size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) {
- return EhReader(S, S->data().slice(Off)).readEhRecordSize();
+size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) {
+ return EhReader(s, s->data().slice(off)).readEhRecordSize();
}
// .eh_frame section is a sequence of records. Each record starts with
// a 4 byte length field. This function reads the length.
size_t EhReader::readEhRecordSize() {
- if (D.size() < 4)
- failOn(D.data(), "CIE/FDE too small");
+ if (d.size() < 4)
+ failOn(d.data(), "CIE/FDE too small");
// First 4 bytes of CIE/FDE is the size of the record.
// If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
// but we do not support that format yet.
- uint64_t V = read32(D.data());
- if (V == UINT32_MAX)
- failOn(D.data(), "CIE/FDE too large");
- uint64_t Size = V + 4;
- if (Size > D.size())
- failOn(D.data(), "CIE/FDE ends past the end of the section");
- return Size;
+ uint64_t v = read32(d.data());
+ if (v == UINT32_MAX)
+ failOn(d.data(), "CIE/FDE too large");
+ uint64_t size = v + 4;
+ if (size > d.size())
+ failOn(d.data(), "CIE/FDE ends past the end of the section");
+ return size;
}
// Read a byte and advance D by one byte.
uint8_t EhReader::readByte() {
- if (D.empty())
- failOn(D.data(), "unexpected end of CIE");
- uint8_t B = D.front();
- D = D.slice(1);
- return B;
+ if (d.empty())
+ failOn(d.data(), "unexpected end of CIE");
+ uint8_t b = d.front();
+ d = d.slice(1);
+ return b;
}
-void EhReader::skipBytes(size_t Count) {
- if (D.size() < Count)
- failOn(D.data(), "CIE is too small");
- D = D.slice(Count);
+void EhReader::skipBytes(size_t count) {
+ if (d.size() < count)
+ failOn(d.data(), "CIE is too small");
+ d = d.slice(count);
}
// Read a null-terminated string.
StringRef EhReader::readString() {
- const uint8_t *End = std::find(D.begin(), D.end(), '\0');
- if (End == D.end())
- failOn(D.data(), "corrupted CIE (failed to read string)");
- StringRef S = toStringRef(D.slice(0, End - D.begin()));
- D = D.slice(S.size() + 1);
- return S;
+ const uint8_t *end = llvm::find(d, '\0');
+ if (end == d.end())
+ failOn(d.data(), "corrupted CIE (failed to read string)");
+ StringRef s = toStringRef(d.slice(0, end - d.begin()));
+ d = d.slice(s.size() + 1);
+ return s;
}
// Skip an integer encoded in the LEB128 format.
@@ -110,21 +109,21 @@ StringRef EhReader::readString() {
// But we need to be at least able to skip it so that we can read
// the field that follows a LEB128 number.
void EhReader::skipLeb128() {
- const uint8_t *ErrPos = D.data();
- while (!D.empty()) {
- uint8_t Val = D.front();
- D = D.slice(1);
- if ((Val & 0x80) == 0)
+ const uint8_t *errPos = d.data();
+ while (!d.empty()) {
+ uint8_t val = d.front();
+ d = d.slice(1);
+ if ((val & 0x80) == 0)
return;
}
- failOn(ErrPos, "corrupted CIE (failed to read LEB128)");
+ failOn(errPos, "corrupted CIE (failed to read LEB128)");
}
-static size_t getAugPSize(unsigned Enc) {
- switch (Enc & 0x0f) {
+static size_t getAugPSize(unsigned enc) {
+ switch (enc & 0x0f) {
case DW_EH_PE_absptr:
case DW_EH_PE_signed:
- return Config->Wordsize;
+ return config->wordsize;
case DW_EH_PE_udata2:
case DW_EH_PE_sdata2:
return 2;
@@ -139,29 +138,29 @@ static size_t getAugPSize(unsigned Enc) {
}
void EhReader::skipAugP() {
- uint8_t Enc = readByte();
- if ((Enc & 0xf0) == DW_EH_PE_aligned)
- failOn(D.data() - 1, "DW_EH_PE_aligned encoding is not supported");
- size_t Size = getAugPSize(Enc);
- if (Size == 0)
- failOn(D.data() - 1, "unknown FDE encoding");
- if (Size >= D.size())
- failOn(D.data() - 1, "corrupted CIE");
- D = D.slice(Size);
+ uint8_t enc = readByte();
+ if ((enc & 0xf0) == DW_EH_PE_aligned)
+ failOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported");
+ size_t size = getAugPSize(enc);
+ if (size == 0)
+ failOn(d.data() - 1, "unknown FDE encoding");
+ if (size >= d.size())
+ failOn(d.data() - 1, "corrupted CIE");
+ d = d.slice(size);
}
-uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
- return EhReader(P->Sec, P->data()).getFdeEncoding();
+uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
+ return EhReader(p->sec, p->data()).getFdeEncoding();
}
uint8_t EhReader::getFdeEncoding() {
skipBytes(8);
- int Version = readByte();
- if (Version != 1 && Version != 3)
- failOn(D.data() - 1,
- "FDE version 1 or 3 expected, but got " + Twine(Version));
+ int version = readByte();
+ if (version != 1 && version != 3)
+ failOn(d.data() - 1,
+ "FDE version 1 or 3 expected, but got " + Twine(version));
- StringRef Aug = readString();
+ StringRef aug = readString();
// Skip code and data alignment factors.
skipLeb128();
@@ -169,7 +168,7 @@ uint8_t EhReader::getFdeEncoding() {
// Skip the return address register. In CIE version 1 this is a single
// byte. In CIE version 3 this is an unsigned LEB128.
- if (Version == 1)
+ if (version == 1)
readByte();
else
skipLeb128();
@@ -177,22 +176,22 @@ uint8_t EhReader::getFdeEncoding() {
// We only care about an 'R' value, but other records may precede an 'R'
// record. Unfortunately records are not in TLV (type-length-value) format,
// so we need to teach the linker how to skip records for each type.
- for (char C : Aug) {
- if (C == 'R')
+ for (char c : aug) {
+ if (c == 'R')
return readByte();
- if (C == 'z') {
+ if (c == 'z') {
skipLeb128();
continue;
}
- if (C == 'P') {
+ if (c == 'P') {
skipAugP();
continue;
}
- if (C == 'L') {
+ if (c == 'L') {
readByte();
continue;
}
- failOn(Aug.data(), "unknown .eh_frame augmentation string: " + Aug);
+ failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
}
return DW_EH_PE_absptr;
}
diff --git a/ELF/EhFrame.h b/ELF/EhFrame.h
index 5112891a911ee..20dd6126ec8e3 100644
--- a/ELF/EhFrame.h
+++ b/ELF/EhFrame.h
@@ -1,9 +1,8 @@
//===- EhFrame.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -17,8 +16,8 @@ namespace elf {
class InputSectionBase;
struct EhSectionPiece;
-size_t readEhRecordSize(InputSectionBase *S, size_t Off);
-uint8_t getFdeEncoding(EhSectionPiece *P);
+size_t readEhRecordSize(InputSectionBase *s, size_t off);
+uint8_t getFdeEncoding(EhSectionPiece *p);
} // namespace elf
} // namespace lld
diff --git a/ELF/Filesystem.cpp b/ELF/Filesystem.cpp
deleted file mode 100644
index 5cf240eeca567..0000000000000
--- a/ELF/Filesystem.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===- Filesystem.cpp -----------------------------------------------------===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file contains a few utility functions to handle files.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Filesystem.h"
-#include "Config.h"
-#include "lld/Common/Threads.h"
-#include "llvm/Config/llvm-config.h"
-#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/FileSystem.h"
-#if LLVM_ON_UNIX
-#include <unistd.h>
-#endif
-#include <thread>
-
-using namespace llvm;
-
-using namespace lld;
-using namespace lld::elf;
-
-// Removes a given file asynchronously. This is a performance hack,
-// so remove this when operating systems are improved.
-//
-// On Linux (and probably on other Unix-like systems), unlink(2) is a
-// noticeably slow system call. As of 2016, unlink takes 250
-// milliseconds to remove a 1 GB file on ext4 filesystem on my machine.
-//
-// To create a new result file, we first remove existing file. So, if
-// you repeatedly link a 1 GB program in a regular compile-link-debug
-// cycle, every cycle wastes 250 milliseconds only to remove a file.
-// Since LLD can link a 1 GB binary in about 5 seconds, that waste
-// actually counts.
-//
-// This function spawns a background thread to remove the file.
-// The calling thread returns almost immediately.
-void elf::unlinkAsync(StringRef Path) {
-// Removing a file is async on windows.
-#if defined(_WIN32)
- sys::fs::remove(Path);
-#else
- if (!ThreadsEnabled || !sys::fs::exists(Path) ||
- !sys::fs::is_regular_file(Path))
- return;
-
- // We cannot just remove path from a different thread because we are now going
- // to create path as a new file.
- // Instead we open the file and unlink it on this thread. The unlink is fast
- // since the open fd guarantees that it is not removing the last reference.
- int FD;
- std::error_code EC = sys::fs::openFileForRead(Path, FD);
- sys::fs::remove(Path);
-
- // close and therefore remove TempPath in background.
- if (!EC)
- std::thread([=] { ::close(FD); }).detach();
-#endif
-}
-
-// Simulate file creation to see if Path is writable.
-//
-// Determining whether a file is writable or not is amazingly hard,
-// and after all the only reliable way of doing that is to actually
-// create a file. But we don't want to do that in this function
-// because LLD shouldn't update any file if it will end in a failure.
-// We also don't want to reimplement heuristics to determine if a
-// file is writable. So we'll let FileOutputBuffer do the work.
-//
-// FileOutputBuffer doesn't touch a desitnation file until commit()
-// is called. We use that class without calling commit() to predict
-// if the given file is writable.
-std::error_code elf::tryCreateFile(StringRef Path) {
- if (Path.empty())
- return std::error_code();
- if (Path == "-")
- return std::error_code();
- return errorToErrorCode(FileOutputBuffer::create(Path, 1).takeError());
-}
diff --git a/ELF/Filesystem.h b/ELF/Filesystem.h
deleted file mode 100644
index 987a74a6bcb6f..0000000000000
--- a/ELF/Filesystem.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//===- Filesystem.h ---------------------------------------------*- C++ -*-===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_ELF_FILESYSTEM_H
-#define LLD_ELF_FILESYSTEM_H
-
-#include "lld/Common/LLVM.h"
-#include <system_error>
-
-namespace lld {
-namespace elf {
-void unlinkAsync(StringRef Path);
-std::error_code tryCreateFile(StringRef Path);
-} // namespace elf
-} // namespace lld
-
-#endif
diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp
index e917ae76a689e..8b01d06b02480 100644
--- a/ELF/ICF.cpp
+++ b/ELF/ICF.cpp
@@ -1,9 +1,8 @@
//===- ICF.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -99,33 +98,33 @@ public:
void run();
private:
- void segregate(size_t Begin, size_t End, bool Constant);
+ void segregate(size_t begin, size_t end, bool constant);
template <class RelTy>
- bool constantEq(const InputSection *A, ArrayRef<RelTy> RelsA,
- const InputSection *B, ArrayRef<RelTy> RelsB);
+ bool constantEq(const InputSection *a, ArrayRef<RelTy> relsA,
+ const InputSection *b, ArrayRef<RelTy> relsB);
template <class RelTy>
- bool variableEq(const InputSection *A, ArrayRef<RelTy> RelsA,
- const InputSection *B, ArrayRef<RelTy> RelsB);
+ bool variableEq(const InputSection *a, ArrayRef<RelTy> relsA,
+ const InputSection *b, ArrayRef<RelTy> relsB);
- bool equalsConstant(const InputSection *A, const InputSection *B);
- bool equalsVariable(const InputSection *A, const InputSection *B);
+ bool equalsConstant(const InputSection *a, const InputSection *b);
+ bool equalsVariable(const InputSection *a, const InputSection *b);
- size_t findBoundary(size_t Begin, size_t End);
+ size_t findBoundary(size_t begin, size_t end);
- void forEachClassRange(size_t Begin, size_t End,
- llvm::function_ref<void(size_t, size_t)> Fn);
+ void forEachClassRange(size_t begin, size_t end,
+ llvm::function_ref<void(size_t, size_t)> fn);
- void forEachClass(llvm::function_ref<void(size_t, size_t)> Fn);
+ void forEachClass(llvm::function_ref<void(size_t, size_t)> fn);
- std::vector<InputSection *> Sections;
+ std::vector<InputSection *> sections;
// We repeat the main loop while `Repeat` is true.
- std::atomic<bool> Repeat;
+ std::atomic<bool> repeat;
// The main loop counter.
- int Cnt = 0;
+ int cnt = 0;
// We have two locations for equivalence classes. On the first iteration
// of the main loop, Class[0] has a valid value, and Class[1] contains
@@ -151,42 +150,42 @@ private:
// because we can safely read the next class without worrying about race
// conditions. Using the same location makes this algorithm converge
// faster because it uses results of the same iteration earlier.
- int Current = 0;
- int Next = 0;
+ int current = 0;
+ int next = 0;
};
}
// Returns true if section S is subject of ICF.
-static bool isEligible(InputSection *S) {
- if (!S->Live || S->KeepUnique || !(S->Flags & SHF_ALLOC))
+static bool isEligible(InputSection *s) {
+ if (!s->isLive() || s->keepUnique || !(s->flags & SHF_ALLOC))
return false;
// Don't merge writable sections. .data.rel.ro sections are marked as writable
// but are semantically read-only.
- if ((S->Flags & SHF_WRITE) && S->Name != ".data.rel.ro" &&
- !S->Name.startswith(".data.rel.ro."))
+ if ((s->flags & SHF_WRITE) && s->name != ".data.rel.ro" &&
+ !s->name.startswith(".data.rel.ro."))
return false;
// SHF_LINK_ORDER sections are ICF'd as a unit with their dependent sections,
// so we don't consider them for ICF individually.
- if (S->Flags & SHF_LINK_ORDER)
+ if (s->flags & SHF_LINK_ORDER)
return false;
// Don't merge synthetic sections as their Data member is not valid and empty.
// The Data member needs to be valid for ICF as it is used by ICF to determine
// the equality of section contents.
- if (isa<SyntheticSection>(S))
+ if (isa<SyntheticSection>(s))
return false;
// .init and .fini contains instructions that must be executed to initialize
// and finalize the process. They cannot and should not be merged.
- if (S->Name == ".init" || S->Name == ".fini")
+ if (s->name == ".init" || s->name == ".fini")
return false;
// A user program may enumerate sections named with a C identifier using
// __start_* and __stop_* symbols. We cannot ICF any such sections because
// that could change program semantics.
- if (isValidCIdentifier(S->Name))
+ if (isValidCIdentifier(s->name))
return false;
return true;
@@ -194,7 +193,7 @@ static bool isEligible(InputSection *S) {
// Split an equivalence class into smaller classes.
template <class ELFT>
-void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) {
+void ICF<ELFT>::segregate(size_t begin, size_t end, bool constant) {
// This loop rearranges sections in [Begin, End) so that all sections
// that are equal in terms of equals{Constant,Variable} are contiguous
// in [Begin, End).
@@ -203,93 +202,93 @@ void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) {
// issue in practice because the number of the distinct sections in
// each range is usually very small.
- while (Begin < End) {
+ while (begin < end) {
// Divide [Begin, End) into two. Let Mid be the start index of the
// second group.
- auto Bound =
- std::stable_partition(Sections.begin() + Begin + 1,
- Sections.begin() + End, [&](InputSection *S) {
- if (Constant)
- return equalsConstant(Sections[Begin], S);
- return equalsVariable(Sections[Begin], S);
+ auto bound =
+ std::stable_partition(sections.begin() + begin + 1,
+ sections.begin() + end, [&](InputSection *s) {
+ if (constant)
+ return equalsConstant(sections[begin], s);
+ return equalsVariable(sections[begin], s);
});
- size_t Mid = Bound - Sections.begin();
+ size_t mid = bound - sections.begin();
// Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by
// updating the sections in [Begin, Mid). We use Mid as an equivalence
// class ID because every group ends with a unique index.
- for (size_t I = Begin; I < Mid; ++I)
- Sections[I]->Class[Next] = Mid;
+ for (size_t i = begin; i < mid; ++i)
+ sections[i]->eqClass[next] = mid;
// If we created a group, we need to iterate the main loop again.
- if (Mid != End)
- Repeat = true;
+ if (mid != end)
+ repeat = true;
- Begin = Mid;
+ begin = mid;
}
}
// Compare two lists of relocations.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
- const InputSection *SecB, ArrayRef<RelTy> RB) {
- for (size_t I = 0; I < RA.size(); ++I) {
- if (RA[I].r_offset != RB[I].r_offset ||
- RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL))
+bool ICF<ELFT>::constantEq(const InputSection *secA, ArrayRef<RelTy> ra,
+ const InputSection *secB, ArrayRef<RelTy> rb) {
+ for (size_t i = 0; i < ra.size(); ++i) {
+ if (ra[i].r_offset != rb[i].r_offset ||
+ ra[i].getType(config->isMips64EL) != rb[i].getType(config->isMips64EL))
return false;
- uint64_t AddA = getAddend<ELFT>(RA[I]);
- uint64_t AddB = getAddend<ELFT>(RB[I]);
+ uint64_t addA = getAddend<ELFT>(ra[i]);
+ uint64_t addB = getAddend<ELFT>(rb[i]);
- Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
- Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
- if (&SA == &SB) {
- if (AddA == AddB)
+ Symbol &sa = secA->template getFile<ELFT>()->getRelocTargetSym(ra[i]);
+ Symbol &sb = secB->template getFile<ELFT>()->getRelocTargetSym(rb[i]);
+ if (&sa == &sb) {
+ if (addA == addB)
continue;
return false;
}
- auto *DA = dyn_cast<Defined>(&SA);
- auto *DB = dyn_cast<Defined>(&SB);
+ auto *da = dyn_cast<Defined>(&sa);
+ auto *db = dyn_cast<Defined>(&sb);
// Placeholder symbols generated by linker scripts look the same now but
// may have different values later.
- if (!DA || !DB || DA->ScriptDefined || DB->ScriptDefined)
+ if (!da || !db || da->scriptDefined || db->scriptDefined)
return false;
// Relocations referring to absolute symbols are constant-equal if their
// values are equal.
- if (!DA->Section && !DB->Section && DA->Value + AddA == DB->Value + AddB)
+ if (!da->section && !db->section && da->value + addA == db->value + addB)
continue;
- if (!DA->Section || !DB->Section)
+ if (!da->section || !db->section)
return false;
- if (DA->Section->kind() != DB->Section->kind())
+ if (da->section->kind() != db->section->kind())
return false;
// Relocations referring to InputSections are constant-equal if their
// section offsets are equal.
- if (isa<InputSection>(DA->Section)) {
- if (DA->Value + AddA == DB->Value + AddB)
+ if (isa<InputSection>(da->section)) {
+ if (da->value + addA == db->value + addB)
continue;
return false;
}
// Relocations referring to MergeInputSections are constant-equal if their
// offsets in the output section are equal.
- auto *X = dyn_cast<MergeInputSection>(DA->Section);
- if (!X)
+ auto *x = dyn_cast<MergeInputSection>(da->section);
+ if (!x)
return false;
- auto *Y = cast<MergeInputSection>(DB->Section);
- if (X->getParent() != Y->getParent())
+ auto *y = cast<MergeInputSection>(db->section);
+ if (x->getParent() != y->getParent())
return false;
- uint64_t OffsetA =
- SA.isSection() ? X->getOffset(AddA) : X->getOffset(DA->Value) + AddA;
- uint64_t OffsetB =
- SB.isSection() ? Y->getOffset(AddB) : Y->getOffset(DB->Value) + AddB;
- if (OffsetA != OffsetB)
+ uint64_t offsetA =
+ sa.isSection() ? x->getOffset(addA) : x->getOffset(da->value) + addA;
+ uint64_t offsetB =
+ sb.isSection() ? y->getOffset(addB) : y->getOffset(db->value) + addB;
+ if (offsetA != offsetB)
return false;
}
@@ -299,57 +298,57 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
// Compare "non-moving" part of two InputSections, namely everything
// except relocation targets.
template <class ELFT>
-bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
- if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags ||
- A->getSize() != B->getSize() || A->data() != B->data())
+bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
+ if (a->numRelocations != b->numRelocations || a->flags != b->flags ||
+ a->getSize() != b->getSize() || a->data() != b->data())
return false;
// If two sections have different output sections, we cannot merge them.
// FIXME: This doesn't do the right thing in the case where there is a linker
// script. We probably need to move output section assignment before ICF to
// get the correct behaviour here.
- if (getOutputSectionName(A) != getOutputSectionName(B))
+ if (getOutputSectionName(a) != getOutputSectionName(b))
return false;
- if (A->AreRelocsRela)
- return constantEq(A, A->template relas<ELFT>(), B,
- B->template relas<ELFT>());
- return constantEq(A, A->template rels<ELFT>(), B, B->template rels<ELFT>());
+ if (a->areRelocsRela)
+ return constantEq(a, a->template relas<ELFT>(), b,
+ b->template relas<ELFT>());
+ return constantEq(a, a->template rels<ELFT>(), b, b->template rels<ELFT>());
}
// Compare two lists of relocations. Returns true if all pairs of
// relocations point to the same section in terms of ICF.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::variableEq(const InputSection *SecA, ArrayRef<RelTy> RA,
- const InputSection *SecB, ArrayRef<RelTy> RB) {
- assert(RA.size() == RB.size());
+bool ICF<ELFT>::variableEq(const InputSection *secA, ArrayRef<RelTy> ra,
+ const InputSection *secB, ArrayRef<RelTy> rb) {
+ assert(ra.size() == rb.size());
- for (size_t I = 0; I < RA.size(); ++I) {
+ for (size_t i = 0; i < ra.size(); ++i) {
// The two sections must be identical.
- Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
- Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
- if (&SA == &SB)
+ Symbol &sa = secA->template getFile<ELFT>()->getRelocTargetSym(ra[i]);
+ Symbol &sb = secB->template getFile<ELFT>()->getRelocTargetSym(rb[i]);
+ if (&sa == &sb)
continue;
- auto *DA = cast<Defined>(&SA);
- auto *DB = cast<Defined>(&SB);
+ auto *da = cast<Defined>(&sa);
+ auto *db = cast<Defined>(&sb);
// We already dealt with absolute and non-InputSection symbols in
// constantEq, and for InputSections we have already checked everything
// except the equivalence class.
- if (!DA->Section)
+ if (!da->section)
continue;
- auto *X = dyn_cast<InputSection>(DA->Section);
- if (!X)
+ auto *x = dyn_cast<InputSection>(da->section);
+ if (!x)
continue;
- auto *Y = cast<InputSection>(DB->Section);
+ auto *y = cast<InputSection>(db->section);
// Ineligible sections are in the special equivalence class 0.
// They can never be the same in terms of the equivalence class.
- if (X->Class[Current] == 0)
+ if (x->eqClass[current] == 0)
return false;
- if (X->Class[Current] != Y->Class[Current])
+ if (x->eqClass[current] != y->eqClass[current])
return false;
};
@@ -358,19 +357,19 @@ bool ICF<ELFT>::variableEq(const InputSection *SecA, ArrayRef<RelTy> RA,
// Compare "moving" part of two InputSections, namely relocation targets.
template <class ELFT>
-bool ICF<ELFT>::equalsVariable(const InputSection *A, const InputSection *B) {
- if (A->AreRelocsRela)
- return variableEq(A, A->template relas<ELFT>(), B,
- B->template relas<ELFT>());
- return variableEq(A, A->template rels<ELFT>(), B, B->template rels<ELFT>());
+bool ICF<ELFT>::equalsVariable(const InputSection *a, const InputSection *b) {
+ if (a->areRelocsRela)
+ return variableEq(a, a->template relas<ELFT>(), b,
+ b->template relas<ELFT>());
+ return variableEq(a, a->template rels<ELFT>(), b, b->template rels<ELFT>());
}
-template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
- uint32_t Class = Sections[Begin]->Class[Current];
- for (size_t I = Begin + 1; I < End; ++I)
- if (Class != Sections[I]->Class[Current])
- return I;
- return End;
+template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t begin, size_t end) {
+ uint32_t eqClass = sections[begin]->eqClass[current];
+ for (size_t i = begin + 1; i < end; ++i)
+ if (eqClass != sections[i]->eqClass[current])
+ return i;
+ return end;
}
// Sections in the same equivalence class are contiguous in Sections
@@ -379,123 +378,125 @@ template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
//
// This function calls Fn on every group within [Begin, End).
template <class ELFT>
-void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
- llvm::function_ref<void(size_t, size_t)> Fn) {
- while (Begin < End) {
- size_t Mid = findBoundary(Begin, End);
- Fn(Begin, Mid);
- Begin = Mid;
+void ICF<ELFT>::forEachClassRange(size_t begin, size_t end,
+ llvm::function_ref<void(size_t, size_t)> fn) {
+ while (begin < end) {
+ size_t mid = findBoundary(begin, end);
+ fn(begin, mid);
+ begin = mid;
}
}
// Call Fn on each equivalence class.
template <class ELFT>
-void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> Fn) {
+void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> fn) {
// If threading is disabled or the number of sections are
// too small to use threading, call Fn sequentially.
- if (!ThreadsEnabled || Sections.size() < 1024) {
- forEachClassRange(0, Sections.size(), Fn);
- ++Cnt;
+ if (!threadsEnabled || sections.size() < 1024) {
+ forEachClassRange(0, sections.size(), fn);
+ ++cnt;
return;
}
- Current = Cnt % 2;
- Next = (Cnt + 1) % 2;
+ current = cnt % 2;
+ next = (cnt + 1) % 2;
// Shard into non-overlapping intervals, and call Fn in parallel.
// The sharding must be completed before any calls to Fn are made
// so that Fn can modify the Chunks in its shard without causing data
// races.
- const size_t NumShards = 256;
- size_t Step = Sections.size() / NumShards;
- size_t Boundaries[NumShards + 1];
- Boundaries[0] = 0;
- Boundaries[NumShards] = Sections.size();
-
- parallelForEachN(1, NumShards, [&](size_t I) {
- Boundaries[I] = findBoundary((I - 1) * Step, Sections.size());
+ const size_t numShards = 256;
+ size_t step = sections.size() / numShards;
+ size_t boundaries[numShards + 1];
+ boundaries[0] = 0;
+ boundaries[numShards] = sections.size();
+
+ parallelForEachN(1, numShards, [&](size_t i) {
+ boundaries[i] = findBoundary((i - 1) * step, sections.size());
});
- parallelForEachN(1, NumShards + 1, [&](size_t I) {
- if (Boundaries[I - 1] < Boundaries[I])
- forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
+ parallelForEachN(1, numShards + 1, [&](size_t i) {
+ if (boundaries[i - 1] < boundaries[i])
+ forEachClassRange(boundaries[i - 1], boundaries[i], fn);
});
- ++Cnt;
+ ++cnt;
}
// Combine the hashes of the sections referenced by the given section into its
// hash.
template <class ELFT, class RelTy>
-static void combineRelocHashes(InputSection *IS, ArrayRef<RelTy> Rels) {
- uint32_t Hash = IS->Class[1];
- for (RelTy Rel : Rels) {
- Symbol &S = IS->template getFile<ELFT>()->getRelocTargetSym(Rel);
- if (auto *D = dyn_cast<Defined>(&S))
- if (auto *RelSec = dyn_cast_or_null<InputSection>(D->Section))
- Hash ^= RelSec->Class[1];
+static void combineRelocHashes(unsigned cnt, InputSection *isec,
+ ArrayRef<RelTy> rels) {
+ uint32_t hash = isec->eqClass[cnt % 2];
+ for (RelTy rel : rels) {
+ Symbol &s = isec->template getFile<ELFT>()->getRelocTargetSym(rel);
+ if (auto *d = dyn_cast<Defined>(&s))
+ if (auto *relSec = dyn_cast_or_null<InputSection>(d->section))
+ hash += relSec->eqClass[cnt % 2];
}
// Set MSB to 1 to avoid collisions with non-hash IDs.
- IS->Class[0] = Hash | (1U << 31);
+ isec->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
}
-static void print(const Twine &S) {
- if (Config->PrintIcfSections)
- message(S);
+static void print(const Twine &s) {
+ if (config->printIcfSections)
+ message(s);
}
// The main function of ICF.
template <class ELFT> void ICF<ELFT>::run() {
// Collect sections to merge.
- for (InputSectionBase *Sec : InputSections)
- if (auto *S = dyn_cast<InputSection>(Sec))
- if (isEligible(S))
- Sections.push_back(S);
+ for (InputSectionBase *sec : inputSections)
+ if (auto *s = dyn_cast<InputSection>(sec))
+ if (isEligible(s))
+ sections.push_back(s);
// Initially, we use hash values to partition sections.
- parallelForEach(Sections, [&](InputSection *S) {
- S->Class[1] = xxHash64(S->data());
+ parallelForEach(sections, [&](InputSection *s) {
+ s->eqClass[0] = xxHash64(s->data());
});
- parallelForEach(Sections, [&](InputSection *S) {
- if (S->AreRelocsRela)
- combineRelocHashes<ELFT>(S, S->template relas<ELFT>());
- else
- combineRelocHashes<ELFT>(S, S->template rels<ELFT>());
- });
+ for (unsigned cnt = 0; cnt != 2; ++cnt) {
+ parallelForEach(sections, [&](InputSection *s) {
+ if (s->areRelocsRela)
+ combineRelocHashes<ELFT>(cnt, s, s->template relas<ELFT>());
+ else
+ combineRelocHashes<ELFT>(cnt, s, s->template rels<ELFT>());
+ });
+ }
// From now on, sections in Sections vector are ordered so that sections
// in the same equivalence class are consecutive in the vector.
- std::stable_sort(Sections.begin(), Sections.end(),
- [](InputSection *A, InputSection *B) {
- return A->Class[0] < B->Class[0];
- });
+ llvm::stable_sort(sections, [](const InputSection *a, const InputSection *b) {
+ return a->eqClass[0] < b->eqClass[0];
+ });
// Compare static contents and assign unique IDs for each static content.
- forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
+ forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });
// Split groups by comparing relocations until convergence is obtained.
do {
- Repeat = false;
+ repeat = false;
forEachClass(
- [&](size_t Begin, size_t End) { segregate(Begin, End, false); });
- } while (Repeat);
+ [&](size_t begin, size_t end) { segregate(begin, end, false); });
+ } while (repeat);
- log("ICF needed " + Twine(Cnt) + " iterations");
+ log("ICF needed " + Twine(cnt) + " iterations");
// Merge sections by the equivalence class.
- forEachClassRange(0, Sections.size(), [&](size_t Begin, size_t End) {
- if (End - Begin == 1)
+ forEachClassRange(0, sections.size(), [&](size_t begin, size_t end) {
+ if (end - begin == 1)
return;
- print("selected section " + toString(Sections[Begin]));
- for (size_t I = Begin + 1; I < End; ++I) {
- print(" removing identical section " + toString(Sections[I]));
- Sections[Begin]->replace(Sections[I]);
+ print("selected section " + toString(sections[begin]));
+ for (size_t i = begin + 1; i < end; ++i) {
+ print(" removing identical section " + toString(sections[i]));
+ sections[begin]->replace(sections[i]);
// At this point we know sections merged are fully identical and hence
// we want to remove duplicate implicit dependencies such as link order
// and relocation sections.
- for (InputSection *IS : Sections[I]->DependentSections)
- IS->Live = false;
+ for (InputSection *isec : sections[i]->dependentSections)
+ isec->markDead();
}
});
}
diff --git a/ELF/ICF.h b/ELF/ICF.h
index a6c8636ead6d5..ed828fc4a9499 100644
--- a/ELF/ICF.h
+++ b/ELF/ICF.h
@@ -1,9 +1,8 @@
//===- ICF.h --------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index e4d1dec7cbcbd..98b88283cf093 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -1,13 +1,13 @@
//===- InputFiles.cpp -----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
+#include "Driver.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "SymbolTable.h"
@@ -25,6 +25,7 @@
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/ARMAttributeParser.h"
#include "llvm/Support/ARMBuildAttributes.h"
+#include "llvm/Support/Endian.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/raw_ostream.h"
@@ -34,145 +35,272 @@ using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::sys;
using namespace llvm::sys::fs;
+using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-bool InputFile::IsInGroup;
-uint32_t InputFile::NextGroupId;
-std::vector<BinaryFile *> elf::BinaryFiles;
-std::vector<BitcodeFile *> elf::BitcodeFiles;
-std::vector<LazyObjFile *> elf::LazyObjFiles;
-std::vector<InputFile *> elf::ObjectFiles;
-std::vector<InputFile *> elf::SharedFiles;
-
-std::unique_ptr<TarWriter> elf::Tar;
+bool InputFile::isInGroup;
+uint32_t InputFile::nextGroupId;
+std::vector<BinaryFile *> elf::binaryFiles;
+std::vector<BitcodeFile *> elf::bitcodeFiles;
+std::vector<LazyObjFile *> elf::lazyObjFiles;
+std::vector<InputFile *> elf::objectFiles;
+std::vector<SharedFile *> elf::sharedFiles;
+
+std::unique_ptr<TarWriter> elf::tar;
+
+static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
+ unsigned char size;
+ unsigned char endian;
+ std::tie(size, endian) = getElfArchType(mb.getBuffer());
+
+ auto report = [&](StringRef msg) {
+ StringRef filename = mb.getBufferIdentifier();
+ if (archiveName.empty())
+ fatal(filename + ": " + msg);
+ else
+ fatal(archiveName + "(" + filename + "): " + msg);
+ };
+
+ if (!mb.getBuffer().startswith(ElfMagic))
+ report("not an ELF file");
+ if (endian != ELFDATA2LSB && endian != ELFDATA2MSB)
+ report("corrupted ELF file: invalid data encoding");
+ if (size != ELFCLASS32 && size != ELFCLASS64)
+ report("corrupted ELF file: invalid file class");
+
+ size_t bufSize = mb.getBuffer().size();
+ if ((size == ELFCLASS32 && bufSize < sizeof(Elf32_Ehdr)) ||
+ (size == ELFCLASS64 && bufSize < sizeof(Elf64_Ehdr)))
+ report("corrupted ELF file: file is too short");
+
+ if (size == ELFCLASS32)
+ return (endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind;
+ return (endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind;
+}
-InputFile::InputFile(Kind K, MemoryBufferRef M)
- : MB(M), GroupId(NextGroupId), FileKind(K) {
+InputFile::InputFile(Kind k, MemoryBufferRef m)
+ : mb(m), groupId(nextGroupId), fileKind(k) {
// All files within the same --{start,end}-group get the same group ID.
// Otherwise, a new file will get a new group ID.
- if (!IsInGroup)
- ++NextGroupId;
+ if (!isInGroup)
+ ++nextGroupId;
}
-Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
+Optional<MemoryBufferRef> elf::readFile(StringRef path) {
// The --chroot option changes our virtual root directory.
// This is useful when you are dealing with files created by --reproduce.
- if (!Config->Chroot.empty() && Path.startswith("/"))
- Path = Saver.save(Config->Chroot + Path);
+ if (!config->chroot.empty() && path.startswith("/"))
+ path = saver.save(config->chroot + path);
- log(Path);
+ log(path);
- auto MBOrErr = MemoryBuffer::getFile(Path, -1, false);
- if (auto EC = MBOrErr.getError()) {
- error("cannot open " + Path + ": " + EC.message());
+ auto mbOrErr = MemoryBuffer::getFile(path, -1, false);
+ if (auto ec = mbOrErr.getError()) {
+ error("cannot open " + path + ": " + ec.message());
return None;
}
- std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
- MemoryBufferRef MBRef = MB->getMemBufferRef();
- make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take MB ownership
+ std::unique_ptr<MemoryBuffer> &mb = *mbOrErr;
+ MemoryBufferRef mbref = mb->getMemBufferRef();
+ make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take MB ownership
+
+ if (tar)
+ tar->append(relativeToRoot(path), mbref.getBuffer());
+ return mbref;
+}
+
+// All input object files must be for the same architecture
+// (e.g. it does not make sense to link x86 object files with
+// MIPS object files.) This function checks for that error.
+static bool isCompatible(InputFile *file) {
+ if (!file->isElf() && !isa<BitcodeFile>(file))
+ return true;
+
+ if (file->ekind == config->ekind && file->emachine == config->emachine) {
+ if (config->emachine != EM_MIPS)
+ return true;
+ if (isMipsN32Abi(file) == config->mipsN32Abi)
+ return true;
+ }
+
+ if (!config->emulation.empty()) {
+ error(toString(file) + " is incompatible with " + config->emulation);
+ } else {
+ InputFile *existing;
+ if (!objectFiles.empty())
+ existing = objectFiles[0];
+ else if (!sharedFiles.empty())
+ existing = sharedFiles[0];
+ else
+ existing = bitcodeFiles[0];
+
+ error(toString(file) + " is incompatible with " + toString(existing));
+ }
+
+ return false;
+}
+
+template <class ELFT> static void doParseFile(InputFile *file) {
+ if (!isCompatible(file))
+ return;
+
+ // Binary file
+ if (auto *f = dyn_cast<BinaryFile>(file)) {
+ binaryFiles.push_back(f);
+ f->parse();
+ return;
+ }
+
+ // .a file
+ if (auto *f = dyn_cast<ArchiveFile>(file)) {
+ f->parse();
+ return;
+ }
+
+ // Lazy object file
+ if (auto *f = dyn_cast<LazyObjFile>(file)) {
+ lazyObjFiles.push_back(f);
+ f->parse<ELFT>();
+ return;
+ }
+
+ if (config->trace)
+ message(toString(file));
+
+ // .so file
+ if (auto *f = dyn_cast<SharedFile>(file)) {
+ f->parse<ELFT>();
+ return;
+ }
+
+ // LLVM bitcode file
+ if (auto *f = dyn_cast<BitcodeFile>(file)) {
+ bitcodeFiles.push_back(f);
+ f->parse<ELFT>();
+ return;
+ }
- if (Tar)
- Tar->append(relativeToRoot(Path), MBRef.getBuffer());
- return MBRef;
+ // Regular object file
+ objectFiles.push_back(file);
+ cast<ObjFile<ELFT>>(file)->parse();
+}
+
+// Add symbols in File to the symbol table.
+void elf::parseFile(InputFile *file) {
+ switch (config->ekind) {
+ case ELF32LEKind:
+ doParseFile<ELF32LE>(file);
+ return;
+ case ELF32BEKind:
+ doParseFile<ELF32BE>(file);
+ return;
+ case ELF64LEKind:
+ doParseFile<ELF64LE>(file);
+ return;
+ case ELF64BEKind:
+ doParseFile<ELF64BE>(file);
+ return;
+ default:
+ llvm_unreachable("unknown ELFT");
+ }
}
// Concatenates arguments to construct a string representing an error location.
-static std::string createFileLineMsg(StringRef Path, unsigned Line) {
- std::string Filename = path::filename(Path);
- std::string Lineno = ":" + std::to_string(Line);
- if (Filename == Path)
- return Filename + Lineno;
- return Filename + Lineno + " (" + Path.str() + Lineno + ")";
+static std::string createFileLineMsg(StringRef path, unsigned line) {
+ std::string filename = path::filename(path);
+ std::string lineno = ":" + std::to_string(line);
+ if (filename == path)
+ return filename + lineno;
+ return filename + lineno + " (" + path.str() + lineno + ")";
}
template <class ELFT>
-static std::string getSrcMsgAux(ObjFile<ELFT> &File, const Symbol &Sym,
- InputSectionBase &Sec, uint64_t Offset) {
+static std::string getSrcMsgAux(ObjFile<ELFT> &file, const Symbol &sym,
+ InputSectionBase &sec, uint64_t offset) {
// In DWARF, functions and variables are stored to different places.
// First, lookup a function for a given offset.
- if (Optional<DILineInfo> Info = File.getDILineInfo(&Sec, Offset))
- return createFileLineMsg(Info->FileName, Info->Line);
+ if (Optional<DILineInfo> info = file.getDILineInfo(&sec, offset))
+ return createFileLineMsg(info->FileName, info->Line);
// If it failed, lookup again as a variable.
- if (Optional<std::pair<std::string, unsigned>> FileLine =
- File.getVariableLoc(Sym.getName()))
- return createFileLineMsg(FileLine->first, FileLine->second);
+ if (Optional<std::pair<std::string, unsigned>> fileLine =
+ file.getVariableLoc(sym.getName()))
+ return createFileLineMsg(fileLine->first, fileLine->second);
- // File.SourceFile contains STT_FILE symbol, and that is a last resort.
- return File.SourceFile;
+ // File.sourceFile contains STT_FILE symbol, and that is a last resort.
+ return file.sourceFile;
}
-std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
- uint64_t Offset) {
+std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec,
+ uint64_t offset) {
if (kind() != ObjKind)
return "";
- switch (Config->EKind) {
+ switch (config->ekind) {
default:
llvm_unreachable("Invalid kind");
case ELF32LEKind:
- return getSrcMsgAux(cast<ObjFile<ELF32LE>>(*this), Sym, Sec, Offset);
+ return getSrcMsgAux(cast<ObjFile<ELF32LE>>(*this), sym, sec, offset);
case ELF32BEKind:
- return getSrcMsgAux(cast<ObjFile<ELF32BE>>(*this), Sym, Sec, Offset);
+ return getSrcMsgAux(cast<ObjFile<ELF32BE>>(*this), sym, sec, offset);
case ELF64LEKind:
- return getSrcMsgAux(cast<ObjFile<ELF64LE>>(*this), Sym, Sec, Offset);
+ return getSrcMsgAux(cast<ObjFile<ELF64LE>>(*this), sym, sec, offset);
case ELF64BEKind:
- return getSrcMsgAux(cast<ObjFile<ELF64BE>>(*this), Sym, Sec, Offset);
+ return getSrcMsgAux(cast<ObjFile<ELF64BE>>(*this), sym, sec, offset);
}
}
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
- Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
- for (std::unique_ptr<DWARFUnit> &CU : Dwarf->compile_units()) {
- auto Report = [](Error Err) {
- handleAllErrors(std::move(Err),
- [](ErrorInfoBase &Info) { warn(Info.message()); });
+ dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
+ for (std::unique_ptr<DWARFUnit> &cu : dwarf->compile_units()) {
+ auto report = [](Error err) {
+ handleAllErrors(std::move(err),
+ [](ErrorInfoBase &info) { warn(info.message()); });
};
- Expected<const DWARFDebugLine::LineTable *> ExpectedLT =
- Dwarf->getLineTableForUnit(CU.get(), Report);
- const DWARFDebugLine::LineTable *LT = nullptr;
- if (ExpectedLT)
- LT = *ExpectedLT;
+ Expected<const DWARFDebugLine::LineTable *> expectedLT =
+ dwarf->getLineTableForUnit(cu.get(), report);
+ const DWARFDebugLine::LineTable *lt = nullptr;
+ if (expectedLT)
+ lt = *expectedLT;
else
- Report(ExpectedLT.takeError());
- if (!LT)
+ report(expectedLT.takeError());
+ if (!lt)
continue;
- LineTables.push_back(LT);
+ lineTables.push_back(lt);
- // Loop over variable records and insert them to VariableLoc.
- for (const auto &Entry : CU->dies()) {
- DWARFDie Die(CU.get(), &Entry);
+ // Loop over variable records and insert them to variableLoc.
+ for (const auto &entry : cu->dies()) {
+ DWARFDie die(cu.get(), &entry);
// Skip all tags that are not variables.
- if (Die.getTag() != dwarf::DW_TAG_variable)
+ if (die.getTag() != dwarf::DW_TAG_variable)
continue;
// Skip if a local variable because we don't need them for generating
// error messages. In general, only non-local symbols can fail to be
// linked.
- if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
+ if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0))
continue;
// Get the source filename index for the variable.
- unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
- if (!LT->hasFileAtIndex(File))
+ unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0);
+ if (!lt->hasFileAtIndex(file))
continue;
// Get the line number on which the variable is declared.
- unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
+ unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0);
- // Here we want to take the variable name to add it into VariableLoc.
+ // Here we want to take the variable name to add it into variableLoc.
// Variable can have regular and linkage name associated. At first, we try
// to get linkage name as it can be different, for example when we have
// two variables in different namespaces of the same object. Use common
// name otherwise, but handle the case when it also absent in case if the
// input object file lacks some debug info.
- StringRef Name =
- dwarf::toString(Die.find(dwarf::DW_AT_linkage_name),
- dwarf::toString(Die.find(dwarf::DW_AT_name), ""));
- if (!Name.empty())
- VariableLoc.insert({Name, {LT, File, Line}});
+ StringRef name =
+ dwarf::toString(die.find(dwarf::DW_AT_linkage_name),
+ dwarf::toString(die.find(dwarf::DW_AT_name), ""));
+ if (!name.empty())
+ variableLoc.insert({name, {lt, file, line}});
}
}
}
@@ -181,112 +309,152 @@ template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
// object (variable, array, etc) definition.
template <class ELFT>
Optional<std::pair<std::string, unsigned>>
-ObjFile<ELFT>::getVariableLoc(StringRef Name) {
- llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
+ObjFile<ELFT>::getVariableLoc(StringRef name) {
+ llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
// Return if we have no debug information about data object.
- auto It = VariableLoc.find(Name);
- if (It == VariableLoc.end())
+ auto it = variableLoc.find(name);
+ if (it == variableLoc.end())
return None;
// Take file name string from line table.
- std::string FileName;
- if (!It->second.LT->getFileNameByIndex(
- It->second.File, nullptr,
- DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
+ std::string fileName;
+ if (!it->second.lt->getFileNameByIndex(
+ it->second.file, {},
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName))
return None;
- return std::make_pair(FileName, It->second.Line);
+ return std::make_pair(fileName, it->second.line);
}
// Returns source line information for a given offset
// using DWARF debug info.
template <class ELFT>
-Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S,
- uint64_t Offset) {
- llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
+Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s,
+ uint64_t offset) {
+ llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
+
+ // Detect SectionIndex for specified section.
+ uint64_t sectionIndex = object::SectionedAddress::UndefSection;
+ ArrayRef<InputSectionBase *> sections = s->file->getSections();
+ for (uint64_t curIndex = 0; curIndex < sections.size(); ++curIndex) {
+ if (s == sections[curIndex]) {
+ sectionIndex = curIndex;
+ break;
+ }
+ }
// Use fake address calcuated by adding section file offset and offset in
// section. See comments for ObjectInfo class.
- DILineInfo Info;
- for (const llvm::DWARFDebugLine::LineTable *LT : LineTables)
- if (LT->getFileLineInfoForAddress(
- S->getOffsetInFile() + Offset, nullptr,
- DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info))
- return Info;
+ DILineInfo info;
+ for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) {
+ if (lt->getFileLineInfoForAddress(
+ {s->getOffsetInFile() + offset, sectionIndex}, nullptr,
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info))
+ return info;
+ }
return None;
}
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
-std::string lld::toString(const InputFile *F) {
- if (!F)
+std::string lld::toString(const InputFile *f) {
+ if (!f)
return "<internal>";
- if (F->ToStringCache.empty()) {
- if (F->ArchiveName.empty())
- F->ToStringCache = F->getName();
+ if (f->toStringCache.empty()) {
+ if (f->archiveName.empty())
+ f->toStringCache = f->getName();
else
- F->ToStringCache = (F->ArchiveName + "(" + F->getName() + ")").str();
+ f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
}
- return F->ToStringCache;
+ return f->toStringCache;
}
-template <class ELFT>
-ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) {
- if (ELFT::TargetEndianness == support::little)
- EKind = ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind;
- else
- EKind = ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind;
+ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
+ ekind = getELFKind(mb, "");
- EMachine = getObj().getHeader()->e_machine;
- OSABI = getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI];
+ switch (ekind) {
+ case ELF32LEKind:
+ init<ELF32LE>();
+ break;
+ case ELF32BEKind:
+ init<ELF32BE>();
+ break;
+ case ELF64LEKind:
+ init<ELF64LE>();
+ break;
+ case ELF64BEKind:
+ init<ELF64BE>();
+ break;
+ default:
+ llvm_unreachable("getELFKind");
+ }
}
-template <class ELFT>
-typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() {
- return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end());
+template <typename Elf_Shdr>
+static const Elf_Shdr *findSection(ArrayRef<Elf_Shdr> sections, uint32_t type) {
+ for (const Elf_Shdr &sec : sections)
+ if (sec.sh_type == type)
+ return &sec;
+ return nullptr;
}
-template <class ELFT>
-uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
- return CHECK(getObj().getSectionIndex(&Sym, ELFSyms, SymtabSHNDX), this);
-}
+template <class ELFT> void ELFFileBase::init() {
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Sym = typename ELFT::Sym;
-template <class ELFT>
-void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
- const Elf_Shdr *Symtab) {
- FirstGlobal = Symtab->sh_info;
- ELFSyms = CHECK(getObj().symbols(Symtab), this);
- if (FirstGlobal == 0 || FirstGlobal > ELFSyms.size())
+ // Initialize trivial attributes.
+ const ELFFile<ELFT> &obj = getObj<ELFT>();
+ emachine = obj.getHeader()->e_machine;
+ osabi = obj.getHeader()->e_ident[llvm::ELF::EI_OSABI];
+ abiVersion = obj.getHeader()->e_ident[llvm::ELF::EI_ABIVERSION];
+
+ ArrayRef<Elf_Shdr> sections = CHECK(obj.sections(), this);
+
+ // Find a symbol table.
+ bool isDSO =
+ (identify_magic(mb.getBuffer()) == file_magic::elf_shared_object);
+ const Elf_Shdr *symtabSec =
+ findSection(sections, isDSO ? SHT_DYNSYM : SHT_SYMTAB);
+
+ if (!symtabSec)
+ return;
+
+ // Initialize members corresponding to a symbol table.
+ firstGlobal = symtabSec->sh_info;
+
+ ArrayRef<Elf_Sym> eSyms = CHECK(obj.symbols(symtabSec), this);
+ if (firstGlobal == 0 || firstGlobal > eSyms.size())
fatal(toString(this) + ": invalid sh_info in symbol table");
- StringTable =
- CHECK(getObj().getStringTableForSymtab(*Symtab, Sections), this);
+ elfSyms = reinterpret_cast<const void *>(eSyms.data());
+ numELFSyms = eSyms.size();
+ stringTable = CHECK(obj.getStringTableForSymtab(*symtabSec, sections), this);
}
template <class ELFT>
-ObjFile<ELFT>::ObjFile(MemoryBufferRef M, StringRef ArchiveName)
- : ELFFileBase<ELFT>(Base::ObjKind, M) {
- this->ArchiveName = ArchiveName;
+uint32_t ObjFile<ELFT>::getSectionIndex(const Elf_Sym &sym) const {
+ return CHECK(
+ this->getObj().getSectionIndex(&sym, getELFSyms<ELFT>(), shndxTable),
+ this);
}
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() {
- if (this->Symbols.empty())
+ if (this->symbols.empty())
return {};
- return makeArrayRef(this->Symbols).slice(1, this->FirstGlobal - 1);
+ return makeArrayRef(this->symbols).slice(1, this->firstGlobal - 1);
}
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getGlobalSymbols() {
- return makeArrayRef(this->Symbols).slice(this->FirstGlobal);
+ return makeArrayRef(this->symbols).slice(this->firstGlobal);
}
-template <class ELFT>
-void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
- // Read a section table. JustSymbols is usually false.
- if (this->JustSymbols)
+template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
+ // Read a section table. justSymbols is usually false.
+ if (this->justSymbols)
initializeJustSymbols();
else
- initializeSections(ComdatGroups);
+ initializeSections(ignoreComdats);
// Read a symbol table.
initializeSymbols();
@@ -296,17 +464,13 @@ void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
// They are identified and deduplicated by group name. This function
// returns a group name.
template <class ELFT>
-StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
- const Elf_Shdr &Sec) {
- // Group signatures are stored as symbol names in object files.
- // sh_info contains a symbol index, so we fetch a symbol and read its name.
- if (this->ELFSyms.empty())
- this->initSymtab(
- Sections, CHECK(object::getSection<ELFT>(Sections, Sec.sh_link), this));
-
- const Elf_Sym *Sym =
- CHECK(object::getSymbol<ELFT>(this->ELFSyms, Sec.sh_info), this);
- StringRef Signature = CHECK(Sym->getName(this->StringTable), this);
+StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
+ const Elf_Shdr &sec) {
+ typename ELFT::SymRange symbols = this->getELFSyms<ELFT>();
+ if (sec.sh_info >= symbols.size())
+ fatal(toString(this) + ": invalid symbol index");
+ const typename ELFT::Sym &sym = symbols[sec.sh_info];
+ StringRef signature = CHECK(sym.getName(this->stringTable), this);
// As a special case, if a symbol is a section symbol and has no name,
// we use a section name as a signature.
@@ -315,23 +479,12 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
// standard, but GNU gold 1.14 (the newest version as of July 2017) or
// older produce such sections as outputs for the -r option, so we need
// a bug-compatibility.
- if (Signature.empty() && Sym->getType() == STT_SECTION)
- return getSectionName(Sec);
- return Signature;
-}
-
-template <class ELFT>
-ArrayRef<typename ObjFile<ELFT>::Elf_Word>
-ObjFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
- const ELFFile<ELFT> &Obj = this->getObj();
- ArrayRef<Elf_Word> Entries =
- CHECK(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec), this);
- if (Entries.empty() || Entries[0] != GRP_COMDAT)
- fatal(toString(this) + ": unsupported SHT_GROUP format");
- return Entries.slice(1);
+ if (signature.empty() && sym.getType() == STT_SECTION)
+ return getSectionName(sec);
+ return signature;
}
-template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
+template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec) {
// On a regular link we don't merge sections if -O0 (default is -O1). This
// sometimes makes the linker significantly faster, although the output will
// be bigger.
@@ -344,14 +497,14 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// SHF_MERGE sections based both on their name and sh_entsize, but that seems
// to be more trouble than it is worth. Instead, we just use the regular (-O1)
// logic for -r.
- if (Config->Optimize == 0 && !Config->Relocatable)
+ if (config->optimize == 0 && !config->relocatable)
return false;
// A mergeable section with size 0 is useless because they don't have
// any data to merge. A mergeable string section with size 0 can be
// argued as invalid because it doesn't end with a null character.
// We'll avoid a mess by handling them as if they were non-mergeable.
- if (Sec.sh_size == 0)
+ if (sec.sh_size == 0)
return false;
// Check for sh_entsize. The ELF spec is not clear about the zero
@@ -359,17 +512,17 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// the section does not hold a table of fixed-size entries". We know
// that Rust 1.13 produces a string mergeable section with a zero
// sh_entsize. Here we just accept it rather than being picky about it.
- uint64_t EntSize = Sec.sh_entsize;
- if (EntSize == 0)
+ uint64_t entSize = sec.sh_entsize;
+ if (entSize == 0)
return false;
- if (Sec.sh_size % EntSize)
+ if (sec.sh_size % entSize)
fatal(toString(this) +
": SHF_MERGE section size must be a multiple of sh_entsize");
- uint64_t Flags = Sec.sh_flags;
- if (!(Flags & SHF_MERGE))
+ uint64_t flags = sec.sh_flags;
+ if (!(flags & SHF_MERGE))
return false;
- if (Flags & SHF_WRITE)
+ if (flags & SHF_WRITE)
fatal(toString(this) + ": writable SHF_MERGE section is not supported");
return true;
@@ -385,118 +538,138 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// When the option is given, we link "just symbols". The section table is
// initialized with null pointers.
template <class ELFT> void ObjFile<ELFT>::initializeJustSymbols() {
- ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this);
- this->Sections.resize(ObjSections.size());
+ ArrayRef<Elf_Shdr> sections = CHECK(this->getObj().sections(), this);
+ this->sections.resize(sections.size());
+}
- for (const Elf_Shdr &Sec : ObjSections) {
- if (Sec.sh_type != SHT_SYMTAB)
- continue;
- this->initSymtab(ObjSections, &Sec);
+// An ELF object file may contain a `.deplibs` section. If it exists, the
+// section contains a list of library specifiers such as `m` for libm. This
+// function resolves a given name by finding the first matching library checking
+// the various ways that a library can be specified to LLD. This ELF extension
+// is a form of autolinking and is called `dependent libraries`. It is currently
+// unique to LLVM and lld.
+static void addDependentLibrary(StringRef specifier, const InputFile *f) {
+ if (!config->dependentLibraries)
return;
- }
+ if (fs::exists(specifier))
+ driver->addFile(specifier, /*withLOption=*/false);
+ else if (Optional<std::string> s = findFromSearchPaths(specifier))
+ driver->addFile(*s, /*withLOption=*/true);
+ else if (Optional<std::string> s = searchLibraryBaseName(specifier))
+ driver->addFile(*s, /*withLOption=*/true);
+ else
+ error(toString(f) +
+ ": unable to find library from dependent library specifier: " +
+ specifier);
}
template <class ELFT>
-void ObjFile<ELFT>::initializeSections(
- DenseSet<CachedHashStringRef> &ComdatGroups) {
- const ELFFile<ELFT> &Obj = this->getObj();
-
- ArrayRef<Elf_Shdr> ObjSections = CHECK(Obj.sections(), this);
- uint64_t Size = ObjSections.size();
- this->Sections.resize(Size);
- this->SectionStringTable =
- CHECK(Obj.getSectionStringTable(ObjSections), this);
-
- for (size_t I = 0, E = ObjSections.size(); I < E; I++) {
- if (this->Sections[I] == &InputSection::Discarded)
+void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
+ const ELFFile<ELFT> &obj = this->getObj();
+
+ ArrayRef<Elf_Shdr> objSections = CHECK(obj.sections(), this);
+ uint64_t size = objSections.size();
+ this->sections.resize(size);
+ this->sectionStringTable =
+ CHECK(obj.getSectionStringTable(objSections), this);
+
+ for (size_t i = 0, e = objSections.size(); i < e; i++) {
+ if (this->sections[i] == &InputSection::discarded)
continue;
- const Elf_Shdr &Sec = ObjSections[I];
+ const Elf_Shdr &sec = objSections[i];
- if (Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE)
- CGProfile = check(
- this->getObj().template getSectionContentsAsArray<Elf_CGProfile>(
- &Sec));
+ if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE)
+ cgProfile =
+ check(obj.template getSectionContentsAsArray<Elf_CGProfile>(&sec));
// SHF_EXCLUDE'ed sections are discarded by the linker. However,
// if -r is given, we'll let the final link discard such sections.
// This is compatible with GNU.
- if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) {
- if (Sec.sh_type == SHT_LLVM_ADDRSIG) {
+ if ((sec.sh_flags & SHF_EXCLUDE) && !config->relocatable) {
+ if (sec.sh_type == SHT_LLVM_ADDRSIG) {
// We ignore the address-significance table if we know that the object
// file was created by objcopy or ld -r. This is because these tools
// will reorder the symbols in the symbol table, invalidating the data
// in the address-significance table, which refers to symbols by index.
- if (Sec.sh_link != 0)
- this->AddrsigSec = &Sec;
- else if (Config->ICF == ICFLevel::Safe)
+ if (sec.sh_link != 0)
+ this->addrsigSec = &sec;
+ else if (config->icf == ICFLevel::Safe)
warn(toString(this) + ": --icf=safe is incompatible with object "
"files created using objcopy or ld -r");
}
- this->Sections[I] = &InputSection::Discarded;
+ this->sections[i] = &InputSection::discarded;
continue;
}
- switch (Sec.sh_type) {
+ switch (sec.sh_type) {
case SHT_GROUP: {
// De-duplicate section groups by their signatures.
- StringRef Signature = getShtGroupSignature(ObjSections, Sec);
- bool IsNew = ComdatGroups.insert(CachedHashStringRef(Signature)).second;
- this->Sections[I] = &InputSection::Discarded;
-
- // We only support GRP_COMDAT type of group. Get the all entries of the
- // section here to let getShtGroupEntries to check the type early for us.
- ArrayRef<Elf_Word> Entries = getShtGroupEntries(Sec);
-
- // If it is a new section group, we want to keep group members.
- // Group leader sections, which contain indices of group members, are
- // discarded because they are useless beyond this point. The only
- // exception is the -r option because in order to produce re-linkable
- // object files, we want to pass through basically everything.
- if (IsNew) {
- if (Config->Relocatable)
- this->Sections[I] = createInputSection(Sec);
+ StringRef signature = getShtGroupSignature(objSections, sec);
+ this->sections[i] = &InputSection::discarded;
+
+
+ ArrayRef<Elf_Word> entries =
+ CHECK(obj.template getSectionContentsAsArray<Elf_Word>(&sec), this);
+ if (entries.empty())
+ fatal(toString(this) + ": empty SHT_GROUP");
+
+ // The first word of a SHT_GROUP section contains flags. Currently,
+ // the standard defines only "GRP_COMDAT" flag for the COMDAT group.
+ // An group with the empty flag doesn't define anything; such sections
+ // are just skipped.
+ if (entries[0] == 0)
+ continue;
+
+ if (entries[0] != GRP_COMDAT)
+ fatal(toString(this) + ": unsupported SHT_GROUP format");
+
+ bool isNew =
+ ignoreComdats ||
+ symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this)
+ .second;
+ if (isNew) {
+ if (config->relocatable)
+ this->sections[i] = createInputSection(sec);
continue;
}
// Otherwise, discard group members.
- for (uint32_t SecIndex : Entries) {
- if (SecIndex >= Size)
+ for (uint32_t secIndex : entries.slice(1)) {
+ if (secIndex >= size)
fatal(toString(this) +
- ": invalid section index in group: " + Twine(SecIndex));
- this->Sections[SecIndex] = &InputSection::Discarded;
+ ": invalid section index in group: " + Twine(secIndex));
+ this->sections[secIndex] = &InputSection::discarded;
}
break;
}
- case SHT_SYMTAB:
- this->initSymtab(ObjSections, &Sec);
- break;
case SHT_SYMTAB_SHNDX:
- this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, ObjSections), this);
+ shndxTable = CHECK(obj.getSHNDXTable(sec, objSections), this);
break;
+ case SHT_SYMTAB:
case SHT_STRTAB:
case SHT_NULL:
break;
default:
- this->Sections[I] = createInputSection(Sec);
+ this->sections[i] = createInputSection(sec);
}
// .ARM.exidx sections have a reverse dependency on the InputSection they
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
- if (Sec.sh_flags & SHF_LINK_ORDER) {
- InputSectionBase *LinkSec = nullptr;
- if (Sec.sh_link < this->Sections.size())
- LinkSec = this->Sections[Sec.sh_link];
- if (!LinkSec)
+ if (sec.sh_flags & SHF_LINK_ORDER) {
+ InputSectionBase *linkSec = nullptr;
+ if (sec.sh_link < this->sections.size())
+ linkSec = this->sections[sec.sh_link];
+ if (!linkSec)
fatal(toString(this) +
- ": invalid sh_link index: " + Twine(Sec.sh_link));
+ ": invalid sh_link index: " + Twine(sec.sh_link));
- InputSection *IS = cast<InputSection>(this->Sections[I]);
- LinkSec->DependentSections.push_back(IS);
- if (!isa<InputSection>(LinkSec))
- error("a section " + IS->Name +
+ InputSection *isec = cast<InputSection>(this->sections[i]);
+ linkSec->dependentSections.push_back(isec);
+ if (!isa<InputSection>(linkSec))
+ error("a section " + isec->name +
" with SHF_LINK_ORDER should not refer a non-regular "
"section: " +
- toString(LinkSec));
+ toString(linkSec));
}
}
}
@@ -504,9 +677,9 @@ void ObjFile<ELFT>::initializeSections(
// For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD
// flag in the ELF Header we need to look at Tag_ABI_VFP_args to find out how
// the input objects have been compiled.
-static void updateARMVFPArgs(const ARMAttributeParser &Attributes,
- const InputFile *F) {
- if (!Attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args))
+static void updateARMVFPArgs(const ARMAttributeParser &attributes,
+ const InputFile *f) {
+ if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args))
// If an ABI tag isn't present then it is implicitly given the value of 0
// which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files,
// including some in glibc that don't use FP args (and should have value 3)
@@ -514,31 +687,31 @@ static void updateARMVFPArgs(const ARMAttributeParser &Attributes,
// as a clash.
return;
- unsigned VFPArgs = Attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
- ARMVFPArgKind Arg;
- switch (VFPArgs) {
+ unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
+ ARMVFPArgKind arg;
+ switch (vfpArgs) {
case ARMBuildAttrs::BaseAAPCS:
- Arg = ARMVFPArgKind::Base;
+ arg = ARMVFPArgKind::Base;
break;
case ARMBuildAttrs::HardFPAAPCS:
- Arg = ARMVFPArgKind::VFP;
+ arg = ARMVFPArgKind::VFP;
break;
case ARMBuildAttrs::ToolChainFPPCS:
// Tool chain specific convention that conforms to neither AAPCS variant.
- Arg = ARMVFPArgKind::ToolChain;
+ arg = ARMVFPArgKind::ToolChain;
break;
case ARMBuildAttrs::CompatibleFPAAPCS:
// Object compatible with all conventions.
return;
default:
- error(toString(F) + ": unknown Tag_ABI_VFP_args value: " + Twine(VFPArgs));
+ error(toString(f) + ": unknown Tag_ABI_VFP_args value: " + Twine(vfpArgs));
return;
}
// Follow ld.bfd and error if there is a mix of calling conventions.
- if (Config->ARMVFPArgs != Arg && Config->ARMVFPArgs != ARMVFPArgKind::Default)
- error(toString(F) + ": incompatible Tag_ABI_VFP_args");
+ if (config->armVFPArgs != arg && config->armVFPArgs != ARMVFPArgKind::Default)
+ error(toString(f) + ": incompatible Tag_ABI_VFP_args");
else
- Config->ARMVFPArgs = Arg;
+ config->armVFPArgs = arg;
}
// The ARM support in lld makes some use of instructions that are not available
@@ -550,11 +723,11 @@ static void updateARMVFPArgs(const ARMAttributeParser &Attributes,
// at compile time. We follow the convention that if at least one input object
// is compiled with an architecture that supports these features then lld is
// permitted to use them.
-static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) {
- if (!Attributes.hasAttribute(ARMBuildAttrs::CPU_arch))
+static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) {
+ if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch))
return;
- auto Arch = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
- switch (Arch) {
+ auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
+ switch (arch) {
case ARMBuildAttrs::Pre_v4:
case ARMBuildAttrs::v4:
case ARMBuildAttrs::v4T:
@@ -566,70 +739,156 @@ static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) {
case ARMBuildAttrs::v6:
case ARMBuildAttrs::v6KZ:
case ARMBuildAttrs::v6K:
- Config->ARMHasBlx = true;
+ config->armHasBlx = true;
// Architectures used in pre-Cortex processors do not support
// The J1 = 1 J2 = 1 Thumb branch range extension, with the exception
// of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do.
break;
default:
// All other Architectures have BLX and extended branch encoding
- Config->ARMHasBlx = true;
- Config->ARMJ1J2BranchEncoding = true;
- if (Arch != ARMBuildAttrs::v6_M && Arch != ARMBuildAttrs::v6S_M)
+ config->armHasBlx = true;
+ config->armJ1J2BranchEncoding = true;
+ if (arch != ARMBuildAttrs::v6_M && arch != ARMBuildAttrs::v6S_M)
// All Architectures used in Cortex processors with the exception
// of v6-M and v6S-M have the MOVT and MOVW instructions.
- Config->ARMHasMovtMovw = true;
+ config->armHasMovtMovw = true;
break;
}
}
+// If a source file is compiled with x86 hardware-assisted call flow control
+// enabled, the generated object file contains feature flags indicating that
+// fact. This function reads the feature flags and returns it.
+//
+// Essentially we want to read a single 32-bit value in this function, but this
+// function is rather complicated because the value is buried deep inside a
+// .note.gnu.property section.
+//
+// The section consists of one or more NOTE records. Each NOTE record consists
+// of zero or more type-length-value fields. We want to find a field of a
+// certain type. It seems a bit too much to just store a 32-bit value, perhaps
+// the ABI is unnecessarily complicated.
+template <class ELFT>
+static uint32_t readAndFeatures(ObjFile<ELFT> *obj, ArrayRef<uint8_t> data) {
+ using Elf_Nhdr = typename ELFT::Nhdr;
+ using Elf_Note = typename ELFT::Note;
+
+ uint32_t featuresSet = 0;
+ while (!data.empty()) {
+ // Read one NOTE record.
+ if (data.size() < sizeof(Elf_Nhdr))
+ fatal(toString(obj) + ": .note.gnu.property: section too short");
+
+ auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data());
+ if (data.size() < nhdr->getSize())
+ fatal(toString(obj) + ": .note.gnu.property: section too short");
+
+ Elf_Note note(*nhdr);
+ if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") {
+ data = data.slice(nhdr->getSize());
+ continue;
+ }
+
+ uint32_t featureAndType = config->emachine == EM_AARCH64
+ ? GNU_PROPERTY_AARCH64_FEATURE_1_AND
+ : GNU_PROPERTY_X86_FEATURE_1_AND;
+
+ // Read a body of a NOTE record, which consists of type-length-value fields.
+ ArrayRef<uint8_t> desc = note.getDesc();
+ while (!desc.empty()) {
+ if (desc.size() < 8)
+ fatal(toString(obj) + ": .note.gnu.property: section too short");
+
+ uint32_t type = read32le(desc.data());
+ uint32_t size = read32le(desc.data() + 4);
+
+ if (type == featureAndType) {
+ // We found a FEATURE_1_AND field. There may be more than one of these
+ // in a .note.gnu.propery section, for a relocatable object we
+ // accumulate the bits set.
+ featuresSet |= read32le(desc.data() + 8);
+ }
+
+ // On 64-bit, a payload may be followed by a 4-byte padding to make its
+ // size a multiple of 8.
+ if (ELFT::Is64Bits)
+ size = alignTo(size, 8);
+
+ desc = desc.slice(size + 8); // +8 for Type and Size
+ }
+
+ // Go to next NOTE record to look for more FEATURE_1_AND descriptions.
+ data = data.slice(nhdr->getSize());
+ }
+
+ return featuresSet;
+}
+
template <class ELFT>
-InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
- uint32_t Idx = Sec.sh_info;
- if (Idx >= this->Sections.size())
- fatal(toString(this) + ": invalid relocated section index: " + Twine(Idx));
- InputSectionBase *Target = this->Sections[Idx];
+InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &sec) {
+ uint32_t idx = sec.sh_info;
+ if (idx >= this->sections.size())
+ fatal(toString(this) + ": invalid relocated section index: " + Twine(idx));
+ InputSectionBase *target = this->sections[idx];
// Strictly speaking, a relocation section must be included in the
// group of the section it relocates. However, LLVM 3.3 and earlier
// would fail to do so, so we gracefully handle that case.
- if (Target == &InputSection::Discarded)
+ if (target == &InputSection::discarded)
return nullptr;
- if (!Target)
+ if (!target)
fatal(toString(this) + ": unsupported relocation reference");
- return Target;
+ return target;
}
// Create a regular InputSection class that has the same contents
// as a given section.
-static InputSection *toRegularSection(MergeInputSection *Sec) {
- return make<InputSection>(Sec->File, Sec->Flags, Sec->Type, Sec->Alignment,
- Sec->data(), Sec->Name);
+static InputSection *toRegularSection(MergeInputSection *sec) {
+ return make<InputSection>(sec->file, sec->flags, sec->type, sec->alignment,
+ sec->data(), sec->name);
}
template <class ELFT>
-InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
- StringRef Name = getSectionName(Sec);
+InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &sec) {
+ StringRef name = getSectionName(sec);
- switch (Sec.sh_type) {
+ switch (sec.sh_type) {
case SHT_ARM_ATTRIBUTES: {
- if (Config->EMachine != EM_ARM)
+ if (config->emachine != EM_ARM)
break;
- ARMAttributeParser Attributes;
- ArrayRef<uint8_t> Contents = check(this->getObj().getSectionContents(&Sec));
- Attributes.Parse(Contents, /*isLittle*/ Config->EKind == ELF32LEKind);
- updateSupportedARMFeatures(Attributes);
- updateARMVFPArgs(Attributes, this);
+ ARMAttributeParser attributes;
+ ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(&sec));
+ attributes.Parse(contents, /*isLittle*/ config->ekind == ELF32LEKind);
+ updateSupportedARMFeatures(attributes);
+ updateARMVFPArgs(attributes, this);
// FIXME: Retain the first attribute section we see. The eglibc ARM
// dynamic loaders require the presence of an attribute section for dlopen
// to work. In a full implementation we would merge all attribute sections.
- if (In.ARMAttributes == nullptr) {
- In.ARMAttributes = make<InputSection>(*this, Sec, Name);
- return In.ARMAttributes;
+ if (in.armAttributes == nullptr) {
+ in.armAttributes = make<InputSection>(*this, sec, name);
+ return in.armAttributes;
}
- return &InputSection::Discarded;
+ return &InputSection::discarded;
+ }
+ case SHT_LLVM_DEPENDENT_LIBRARIES: {
+ if (config->relocatable)
+ break;
+ ArrayRef<char> data =
+ CHECK(this->getObj().template getSectionContentsAsArray<char>(&sec), this);
+ if (!data.empty() && data.back() != '\0') {
+ error(toString(this) +
+ ": corrupted dependent libraries section (unterminated string): " +
+ name);
+ return &InputSection::discarded;
+ }
+ for (const char *d = data.begin(), *e = data.end(); d < e;) {
+ StringRef s(d);
+ addDependentLibrary(s, this);
+ d += s.size() + 1;
+ }
+ return &InputSection::discarded;
}
case SHT_RELA:
case SHT_REL: {
@@ -638,25 +897,25 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// and the group is discarded, even though it's a violation of the
// spec. We handle that situation gracefully by discarding dangling
// relocation sections.
- InputSectionBase *Target = getRelocTarget(Sec);
- if (!Target)
+ InputSectionBase *target = getRelocTarget(sec);
+ if (!target)
return nullptr;
// This section contains relocation information.
// If -r is given, we do not interpret or apply relocation
// but just copy relocation sections to output.
- if (Config->Relocatable) {
- InputSection *RelocSec = make<InputSection>(*this, Sec, Name);
+ if (config->relocatable) {
+ InputSection *relocSec = make<InputSection>(*this, sec, name);
// We want to add a dependency to target, similar like we do for
// -emit-relocs below. This is useful for the case when linker script
// contains the "/DISCARD/". It is perhaps uncommon to use a script with
// -r, but we faced it in the Linux kernel and have to handle such case
// and not to crash.
- Target->DependentSections.push_back(RelocSec);
- return RelocSec;
+ target->dependentSections.push_back(relocSec);
+ return relocSec;
}
- if (Target->FirstRelocation)
+ if (target->firstRelocation)
fatal(toString(this) +
": multiple relocation sections to one section are not supported");
@@ -665,33 +924,33 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// because applying relocations at end of linking changes section
// contents. So, we simply handle such sections as non-mergeable ones.
// Degrading like this is acceptable because section merging is optional.
- if (auto *MS = dyn_cast<MergeInputSection>(Target)) {
- Target = toRegularSection(MS);
- this->Sections[Sec.sh_info] = Target;
+ if (auto *ms = dyn_cast<MergeInputSection>(target)) {
+ target = toRegularSection(ms);
+ this->sections[sec.sh_info] = target;
}
- if (Sec.sh_type == SHT_RELA) {
- ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this);
- Target->FirstRelocation = Rels.begin();
- Target->NumRelocations = Rels.size();
- Target->AreRelocsRela = true;
+ if (sec.sh_type == SHT_RELA) {
+ ArrayRef<Elf_Rela> rels = CHECK(getObj().relas(&sec), this);
+ target->firstRelocation = rels.begin();
+ target->numRelocations = rels.size();
+ target->areRelocsRela = true;
} else {
- ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this);
- Target->FirstRelocation = Rels.begin();
- Target->NumRelocations = Rels.size();
- Target->AreRelocsRela = false;
+ ArrayRef<Elf_Rel> rels = CHECK(getObj().rels(&sec), this);
+ target->firstRelocation = rels.begin();
+ target->numRelocations = rels.size();
+ target->areRelocsRela = false;
}
- assert(isUInt<31>(Target->NumRelocations));
+ assert(isUInt<31>(target->numRelocations));
// Relocation sections processed by the linker are usually removed
// from the output, so returning `nullptr` for the normal case.
// However, if -emit-relocs is given, we need to leave them in the output.
// (Some post link analysis tools need this information.)
- if (Config->EmitRelocs) {
- InputSection *RelocSec = make<InputSection>(*this, Sec, Name);
+ if (config->emitRelocs) {
+ InputSection *relocSec = make<InputSection>(*this, sec, name);
// We will not emit relocation section if target was discarded.
- Target->DependentSections.push_back(RelocSec);
- return RelocSec;
+ target->dependentSections.push_back(relocSec);
+ return relocSec;
}
return nullptr;
}
@@ -710,28 +969,42 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// explicitly told to do otherwise (by -z execstack). Because the stack
// executable-ness is controlled solely by command line options,
// .note.GNU-stack sections are simply ignored.
- if (Name == ".note.GNU-stack")
- return &InputSection::Discarded;
+ if (name == ".note.GNU-stack")
+ return &InputSection::discarded;
+
+ // Object files that use processor features such as Intel Control-Flow
+ // Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
+ // .note.gnu.property section containing a bitfield of feature bits like the
+ // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
+ //
+ // Since we merge bitmaps from multiple object files to create a new
+ // .note.gnu.property containing a single AND'ed bitmap, we discard an input
+ // file's .note.gnu.property section.
+ if (name == ".note.gnu.property") {
+ ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(&sec));
+ this->andFeatures = readAndFeatures(this, contents);
+ return &InputSection::discarded;
+ }
// Split stacks is a feature to support a discontiguous stack,
// commonly used in the programming language Go. For the details,
// see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled
// for split stack will include a .note.GNU-split-stack section.
- if (Name == ".note.GNU-split-stack") {
- if (Config->Relocatable) {
+ if (name == ".note.GNU-split-stack") {
+ if (config->relocatable) {
error("cannot mix split-stack and non-split-stack in a relocatable link");
- return &InputSection::Discarded;
+ return &InputSection::discarded;
}
- this->SplitStack = true;
- return &InputSection::Discarded;
+ this->splitStack = true;
+ return &InputSection::discarded;
}
// An object file cmpiled for split stack, but where some of the
// functions were compiled with the no_split_stack_attribute will
// include a .note.GNU-no-split-stack section.
- if (Name == ".note.GNU-no-split-stack") {
- this->SomeNoSplitStack = true;
- return &InputSection::Discarded;
+ if (name == ".note.GNU-no-split-stack") {
+ this->someNoSplitStack = true;
+ return &InputSection::discarded;
}
// The linkonce feature is a sort of proto-comdat. Some glibc i386 object
@@ -739,245 +1012,205 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// sections. Drop those sections to avoid duplicate symbol errors.
// FIXME: This is glibc PR20543, we should remove this hack once that has been
// fixed for a while.
- if (Name.startswith(".gnu.linkonce."))
- return &InputSection::Discarded;
+ if (name == ".gnu.linkonce.t.__x86.get_pc_thunk.bx" ||
+ name == ".gnu.linkonce.t.__i686.get_pc_thunk.bx")
+ return &InputSection::discarded;
// If we are creating a new .build-id section, strip existing .build-id
// sections so that the output won't have more than one .build-id.
// This is not usually a problem because input object files normally don't
// have .build-id sections, but you can create such files by
// "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it.
- if (Name == ".note.gnu.build-id" && Config->BuildId != BuildIdKind::None)
- return &InputSection::Discarded;
+ if (name == ".note.gnu.build-id" && config->buildId != BuildIdKind::None)
+ return &InputSection::discarded;
// The linker merges EH (exception handling) frames and creates a
// .eh_frame_hdr section for runtime. So we handle them with a special
// class. For relocatable outputs, they are just passed through.
- if (Name == ".eh_frame" && !Config->Relocatable)
- return make<EhInputSection>(*this, Sec, Name);
+ if (name == ".eh_frame" && !config->relocatable)
+ return make<EhInputSection>(*this, sec, name);
- if (shouldMerge(Sec))
- return make<MergeInputSection>(*this, Sec, Name);
- return make<InputSection>(*this, Sec, Name);
+ if (shouldMerge(sec))
+ return make<MergeInputSection>(*this, sec, name);
+ return make<InputSection>(*this, sec, name);
}
template <class ELFT>
-StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &Sec) {
- return CHECK(this->getObj().getSectionName(&Sec, SectionStringTable), this);
+StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &sec) {
+ return CHECK(getObj().getSectionName(&sec, sectionStringTable), this);
}
+// Initialize this->Symbols. this->Symbols is a parallel array as
+// its corresponding ELF symbol table.
template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
- this->Symbols.reserve(this->ELFSyms.size());
- for (const Elf_Sym &Sym : this->ELFSyms)
- this->Symbols.push_back(createSymbol(&Sym));
-}
-
-template <class ELFT> Symbol *ObjFile<ELFT>::createSymbol(const Elf_Sym *Sym) {
- int Binding = Sym->getBinding();
-
- uint32_t SecIdx = this->getSectionIndex(*Sym);
- if (SecIdx >= this->Sections.size())
- fatal(toString(this) + ": invalid section index: " + Twine(SecIdx));
-
- InputSectionBase *Sec = this->Sections[SecIdx];
- uint8_t StOther = Sym->st_other;
- uint8_t Type = Sym->getType();
- uint64_t Value = Sym->st_value;
- uint64_t Size = Sym->st_size;
-
- if (Binding == STB_LOCAL) {
- if (Sym->getType() == STT_FILE)
- SourceFile = CHECK(Sym->getName(this->StringTable), this);
+ ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
+ this->symbols.resize(eSyms.size());
+
+ // Our symbol table may have already been partially initialized
+ // because of LazyObjFile.
+ for (size_t i = 0, end = eSyms.size(); i != end; ++i)
+ if (!this->symbols[i] && eSyms[i].getBinding() != STB_LOCAL)
+ this->symbols[i] =
+ symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this));
+
+ // Fill this->Symbols. A symbol is either local or global.
+ for (size_t i = 0, end = eSyms.size(); i != end; ++i) {
+ const Elf_Sym &eSym = eSyms[i];
+
+ // Read symbol attributes.
+ uint32_t secIdx = getSectionIndex(eSym);
+ if (secIdx >= this->sections.size())
+ fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
+
+ InputSectionBase *sec = this->sections[secIdx];
+ uint8_t binding = eSym.getBinding();
+ uint8_t stOther = eSym.st_other;
+ uint8_t type = eSym.getType();
+ uint64_t value = eSym.st_value;
+ uint64_t size = eSym.st_size;
+ StringRefZ name = this->stringTable.data() + eSym.st_name;
+
+ // Handle local symbols. Local symbols are not added to the symbol
+ // table because they are not visible from other object files. We
+ // allocate symbol instances and add their pointers to Symbols.
+ if (binding == STB_LOCAL) {
+ if (eSym.getType() == STT_FILE)
+ sourceFile = CHECK(eSym.getName(this->stringTable), this);
+
+ if (this->stringTable.size() <= eSym.st_name)
+ fatal(toString(this) + ": invalid symbol name offset");
+
+ if (eSym.st_shndx == SHN_UNDEF)
+ this->symbols[i] = make<Undefined>(this, name, binding, stOther, type);
+ else if (sec == &InputSection::discarded)
+ this->symbols[i] = make<Undefined>(this, name, binding, stOther, type,
+ /*DiscardedSecIdx=*/secIdx);
+ else
+ this->symbols[i] =
+ make<Defined>(this, name, binding, stOther, type, value, size, sec);
+ continue;
+ }
- if (this->StringTable.size() <= Sym->st_name)
- fatal(toString(this) + ": invalid symbol name offset");
+ // Handle global undefined symbols.
+ if (eSym.st_shndx == SHN_UNDEF) {
+ this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
+ continue;
+ }
- StringRefZ Name = this->StringTable.data() + Sym->st_name;
- if (Sym->st_shndx == SHN_UNDEF)
- return make<Undefined>(this, Name, Binding, StOther, Type);
+ // Handle global common symbols.
+ if (eSym.st_shndx == SHN_COMMON) {
+ if (value == 0 || value >= UINT32_MAX)
+ fatal(toString(this) + ": common symbol '" + StringRef(name.data) +
+ "' has invalid alignment: " + Twine(value));
+ this->symbols[i]->resolve(
+ CommonSymbol{this, name, binding, stOther, type, value, size});
+ continue;
+ }
- return make<Defined>(this, Name, Binding, StOther, Type, Value, Size, Sec);
- }
+ // If a defined symbol is in a discarded section, handle it as if it
+ // were an undefined symbol. Such symbol doesn't comply with the
+ // standard, but in practice, a .eh_frame often directly refer
+ // COMDAT member sections, and if a comdat group is discarded, some
+ // defined symbol in a .eh_frame becomes dangling symbols.
+ if (sec == &InputSection::discarded) {
+ this->symbols[i]->resolve(
+ Undefined{this, name, binding, stOther, type, secIdx});
+ continue;
+ }
- StringRef Name = CHECK(Sym->getName(this->StringTable), this);
-
- switch (Sym->st_shndx) {
- case SHN_UNDEF:
- return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
- /*CanOmitFromDynSym=*/false, this);
- case SHN_COMMON:
- if (Value == 0 || Value >= UINT32_MAX)
- fatal(toString(this) + ": common symbol '" + Name +
- "' has invalid alignment: " + Twine(Value));
- return Symtab->addCommon(Name, Size, Value, Binding, StOther, Type, *this);
- }
+ // Handle global defined symbols.
+ if (binding == STB_GLOBAL || binding == STB_WEAK ||
+ binding == STB_GNU_UNIQUE) {
+ this->symbols[i]->resolve(
+ Defined{this, name, binding, stOther, type, value, size, sec});
+ continue;
+ }
- switch (Binding) {
- default:
- fatal(toString(this) + ": unexpected binding: " + Twine(Binding));
- case STB_GLOBAL:
- case STB_WEAK:
- case STB_GNU_UNIQUE:
- if (Sec == &InputSection::Discarded)
- return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
- /*CanOmitFromDynSym=*/false, this);
- return Symtab->addDefined(Name, StOther, Type, Value, Size, Binding, Sec,
- this);
+ fatal(toString(this) + ": unexpected binding: " + Twine((int)binding));
}
}
-ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
- : InputFile(ArchiveKind, File->getMemoryBufferRef()),
- File(std::move(File)) {}
+ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&file)
+ : InputFile(ArchiveKind, file->getMemoryBufferRef()),
+ file(std::move(file)) {}
-template <class ELFT> void ArchiveFile::parse() {
- for (const Archive::Symbol &Sym : File->symbols())
- Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym);
+void ArchiveFile::parse() {
+ for (const Archive::Symbol &sym : file->symbols())
+ symtab->addSymbol(LazyArchive{*this, sym});
}
// Returns a buffer pointing to a member file containing a given symbol.
-InputFile *ArchiveFile::fetch(const Archive::Symbol &Sym) {
- Archive::Child C =
- CHECK(Sym.getMember(), toString(this) +
+void ArchiveFile::fetch(const Archive::Symbol &sym) {
+ Archive::Child c =
+ CHECK(sym.getMember(), toString(this) +
": could not get the member for symbol " +
- Sym.getName());
+ sym.getName());
- if (!Seen.insert(C.getChildOffset()).second)
- return nullptr;
+ if (!seen.insert(c.getChildOffset()).second)
+ return;
- MemoryBufferRef MB =
- CHECK(C.getMemoryBufferRef(),
+ MemoryBufferRef mb =
+ CHECK(c.getMemoryBufferRef(),
toString(this) +
": could not get the buffer for the member defining symbol " +
- Sym.getName());
+ sym.getName());
- if (Tar && C.getParent()->isThin())
- Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), MB.getBuffer());
+ if (tar && c.getParent()->isThin())
+ tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer());
- InputFile *File = createObjectFile(
- MB, getName(), C.getParent()->isThin() ? 0 : C.getChildOffset());
- File->GroupId = GroupId;
- return File;
+ InputFile *file = createObjectFile(
+ mb, getName(), c.getParent()->isThin() ? 0 : c.getChildOffset());
+ file->groupId = groupId;
+ parseFile(file);
}
-template <class ELFT>
-SharedFile<ELFT>::SharedFile(MemoryBufferRef M, StringRef DefaultSoName)
- : ELFFileBase<ELFT>(Base::SharedKind, M), SoName(DefaultSoName),
- IsNeeded(!Config->AsNeeded) {}
-
-// Partially parse the shared object file so that we can call
-// getSoName on this object.
-template <class ELFT> void SharedFile<ELFT>::parseSoName() {
- const Elf_Shdr *DynamicSec = nullptr;
- const ELFFile<ELFT> Obj = this->getObj();
- ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
-
- // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
- for (const Elf_Shdr &Sec : Sections) {
- switch (Sec.sh_type) {
- default:
- continue;
- case SHT_DYNSYM:
- this->initSymtab(Sections, &Sec);
- break;
- case SHT_DYNAMIC:
- DynamicSec = &Sec;
- break;
- case SHT_SYMTAB_SHNDX:
- this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, Sections), this);
- break;
- case SHT_GNU_versym:
- this->VersymSec = &Sec;
- break;
- case SHT_GNU_verdef:
- this->VerdefSec = &Sec;
- break;
- }
- }
-
- if (this->VersymSec && this->ELFSyms.empty())
- error("SHT_GNU_versym should be associated with symbol table");
-
- // Search for a DT_SONAME tag to initialize this->SoName.
- if (!DynamicSec)
- return;
- ArrayRef<Elf_Dyn> Arr =
- CHECK(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec), this);
- for (const Elf_Dyn &Dyn : Arr) {
- if (Dyn.d_tag == DT_SONAME) {
- uint64_t Val = Dyn.getVal();
- if (Val >= this->StringTable.size())
- fatal(toString(this) + ": invalid DT_SONAME entry");
- SoName = this->StringTable.data() + Val;
- return;
- }
- }
-}
-
-// Parses ".gnu.version" section which is a parallel array for the symbol table.
-// If a given file doesn't have ".gnu.version" section, returns VER_NDX_GLOBAL.
-template <class ELFT> std::vector<uint32_t> SharedFile<ELFT>::parseVersyms() {
- size_t Size = this->ELFSyms.size() - this->FirstGlobal;
- if (!VersymSec)
- return std::vector<uint32_t>(Size, VER_NDX_GLOBAL);
-
- const char *Base = this->MB.getBuffer().data();
- const Elf_Versym *Versym =
- reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) +
- this->FirstGlobal;
-
- std::vector<uint32_t> Ret(Size);
- for (size_t I = 0; I < Size; ++I)
- Ret[I] = Versym[I].vs_index;
- return Ret;
-}
+unsigned SharedFile::vernauxNum;
-// Parse the version definitions in the object file if present. Returns a vector
-// whose nth element contains a pointer to the Elf_Verdef for version identifier
-// n. Version identifiers that are not definitions map to nullptr.
-template <class ELFT>
-std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() {
- if (!VerdefSec)
+// Parse the version definitions in the object file if present, and return a
+// vector whose nth element contains a pointer to the Elf_Verdef for version
+// identifier n. Version identifiers that are not definitions map to nullptr.
+template <typename ELFT>
+static std::vector<const void *> parseVerdefs(const uint8_t *base,
+ const typename ELFT::Shdr *sec) {
+ if (!sec)
return {};
// We cannot determine the largest verdef identifier without inspecting
// every Elf_Verdef, but both bfd and gold assign verdef identifiers
// sequentially starting from 1, so we predict that the largest identifier
- // will be VerdefCount.
- unsigned VerdefCount = VerdefSec->sh_info;
- std::vector<const Elf_Verdef *> Verdefs(VerdefCount + 1);
+ // will be verdefCount.
+ unsigned verdefCount = sec->sh_info;
+ std::vector<const void *> verdefs(verdefCount + 1);
// Build the Verdefs array by following the chain of Elf_Verdef objects
// from the start of the .gnu.version_d section.
- const char *Base = this->MB.getBuffer().data();
- const char *Verdef = Base + VerdefSec->sh_offset;
- for (unsigned I = 0; I != VerdefCount; ++I) {
- auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
- Verdef += CurVerdef->vd_next;
- unsigned VerdefIndex = CurVerdef->vd_ndx;
- Verdefs.resize(VerdefIndex + 1);
- Verdefs[VerdefIndex] = CurVerdef;
+ const uint8_t *verdef = base + sec->sh_offset;
+ for (unsigned i = 0; i != verdefCount; ++i) {
+ auto *curVerdef = reinterpret_cast<const typename ELFT::Verdef *>(verdef);
+ verdef += curVerdef->vd_next;
+ unsigned verdefIndex = curVerdef->vd_ndx;
+ verdefs.resize(verdefIndex + 1);
+ verdefs[verdefIndex] = curVerdef;
}
-
- return Verdefs;
+ return verdefs;
}
// We do not usually care about alignments of data in shared object
// files because the loader takes care of it. However, if we promote a
// DSO symbol to point to .bss due to copy relocation, we need to keep
// the original alignment requirements. We infer it in this function.
-template <class ELFT>
-uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections,
- const Elf_Sym &Sym) {
- uint64_t Ret = UINT64_MAX;
- if (Sym.st_value)
- Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
- if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size())
- Ret = std::min<uint64_t>(Ret, Sections[Sym.st_shndx].sh_addralign);
- return (Ret > UINT32_MAX) ? 0 : Ret;
+template <typename ELFT>
+static uint64_t getAlignment(ArrayRef<typename ELFT::Shdr> sections,
+ const typename ELFT::Sym &sym) {
+ uint64_t ret = UINT64_MAX;
+ if (sym.st_value)
+ ret = 1ULL << countTrailingZeros((uint64_t)sym.st_value);
+ if (0 < sym.st_shndx && sym.st_shndx < sections.size())
+ ret = std::min<uint64_t>(ret, sections[sym.st_shndx].sh_addralign);
+ return (ret > UINT32_MAX) ? 0 : ret;
}
-// Fully parse the shared object file. This must be called after parseSoName().
+// Fully parse the shared object file.
//
// This function parses symbol versions. If a DSO has version information,
// the file has a ".gnu.version_d" section which contains symbol version
@@ -992,80 +1225,163 @@ uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections,
// The file format for symbol versioning is perhaps a bit more complicated
// than necessary, but you can easily understand the code if you wrap your
// head around the data structure described above.
-template <class ELFT> void SharedFile<ELFT>::parseRest() {
- Verdefs = parseVerdefs(); // parse .gnu.version_d
- std::vector<uint32_t> Versyms = parseVersyms(); // parse .gnu.version
- ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this);
+template <class ELFT> void SharedFile::parse() {
+ using Elf_Dyn = typename ELFT::Dyn;
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Sym = typename ELFT::Sym;
+ using Elf_Verdef = typename ELFT::Verdef;
+ using Elf_Versym = typename ELFT::Versym;
+
+ ArrayRef<Elf_Dyn> dynamicTags;
+ const ELFFile<ELFT> obj = this->getObj<ELFT>();
+ ArrayRef<Elf_Shdr> sections = CHECK(obj.sections(), this);
+
+ const Elf_Shdr *versymSec = nullptr;
+ const Elf_Shdr *verdefSec = nullptr;
+
+ // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
+ for (const Elf_Shdr &sec : sections) {
+ switch (sec.sh_type) {
+ default:
+ continue;
+ case SHT_DYNAMIC:
+ dynamicTags =
+ CHECK(obj.template getSectionContentsAsArray<Elf_Dyn>(&sec), this);
+ break;
+ case SHT_GNU_versym:
+ versymSec = &sec;
+ break;
+ case SHT_GNU_verdef:
+ verdefSec = &sec;
+ break;
+ }
+ }
+
+ if (versymSec && numELFSyms == 0) {
+ error("SHT_GNU_versym should be associated with symbol table");
+ return;
+ }
+
+ // Search for a DT_SONAME tag to initialize this->soName.
+ for (const Elf_Dyn &dyn : dynamicTags) {
+ if (dyn.d_tag == DT_NEEDED) {
+ uint64_t val = dyn.getVal();
+ if (val >= this->stringTable.size())
+ fatal(toString(this) + ": invalid DT_NEEDED entry");
+ dtNeeded.push_back(this->stringTable.data() + val);
+ } else if (dyn.d_tag == DT_SONAME) {
+ uint64_t val = dyn.getVal();
+ if (val >= this->stringTable.size())
+ fatal(toString(this) + ": invalid DT_SONAME entry");
+ soName = this->stringTable.data() + val;
+ }
+ }
+
+ // DSOs are uniquified not by filename but by soname.
+ DenseMap<StringRef, SharedFile *>::iterator it;
+ bool wasInserted;
+ std::tie(it, wasInserted) = symtab->soNames.try_emplace(soName, this);
+
+ // If a DSO appears more than once on the command line with and without
+ // --as-needed, --no-as-needed takes precedence over --as-needed because a
+ // user can add an extra DSO with --no-as-needed to force it to be added to
+ // the dependency list.
+ it->second->isNeeded |= isNeeded;
+ if (!wasInserted)
+ return;
+
+ sharedFiles.push_back(this);
+
+ verdefs = parseVerdefs<ELFT>(obj.base(), verdefSec);
+
+ // Parse ".gnu.version" section which is a parallel array for the symbol
+ // table. If a given file doesn't have a ".gnu.version" section, we use
+ // VER_NDX_GLOBAL.
+ size_t size = numELFSyms - firstGlobal;
+ std::vector<uint32_t> versyms(size, VER_NDX_GLOBAL);
+ if (versymSec) {
+ ArrayRef<Elf_Versym> versym =
+ CHECK(obj.template getSectionContentsAsArray<Elf_Versym>(versymSec),
+ this)
+ .slice(firstGlobal);
+ for (size_t i = 0; i < size; ++i)
+ versyms[i] = versym[i].vs_index;
+ }
// System libraries can have a lot of symbols with versions. Using a
// fixed buffer for computing the versions name (foo@ver) can save a
// lot of allocations.
- SmallString<0> VersionedNameBuffer;
+ SmallString<0> versionedNameBuffer;
// Add symbols to the symbol table.
- ArrayRef<Elf_Sym> Syms = this->getGlobalELFSyms();
- for (size_t I = 0; I < Syms.size(); ++I) {
- const Elf_Sym &Sym = Syms[I];
+ ArrayRef<Elf_Sym> syms = this->getGlobalELFSyms<ELFT>();
+ for (size_t i = 0; i < syms.size(); ++i) {
+ const Elf_Sym &sym = syms[i];
// ELF spec requires that all local symbols precede weak or global
// symbols in each symbol table, and the index of first non-local symbol
// is stored to sh_info. If a local symbol appears after some non-local
// symbol, that's a violation of the spec.
- StringRef Name = CHECK(Sym.getName(this->StringTable), this);
- if (Sym.getBinding() == STB_LOCAL) {
- warn("found local symbol '" + Name +
+ StringRef name = CHECK(sym.getName(this->stringTable), this);
+ if (sym.getBinding() == STB_LOCAL) {
+ warn("found local symbol '" + name +
"' in global part of symbol table in file " + toString(this));
continue;
}
- if (Sym.isUndefined()) {
- Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(),
- Sym.st_other, Sym.getType(),
- /*CanOmitFromDynSym=*/false, this);
- S->ExportDynamic = true;
+ if (sym.isUndefined()) {
+ Symbol *s = symtab->addSymbol(
+ Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()});
+ s->exportDynamic = true;
continue;
}
// MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly
// assigns VER_NDX_LOCAL to this section global symbol. Here is a
// workaround for this bug.
- uint32_t Idx = Versyms[I] & ~VERSYM_HIDDEN;
- if (Config->EMachine == EM_MIPS && Idx == VER_NDX_LOCAL &&
- Name == "_gp_disp")
+ uint32_t idx = versyms[i] & ~VERSYM_HIDDEN;
+ if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL &&
+ name == "_gp_disp")
continue;
- uint64_t Alignment = getAlignment(Sections, Sym);
- if (!(Versyms[I] & VERSYM_HIDDEN))
- Symtab->addShared(Name, *this, Sym, Alignment, Idx);
+ uint32_t alignment = getAlignment<ELFT>(sections, sym);
+ if (!(versyms[i] & VERSYM_HIDDEN)) {
+ symtab->addSymbol(SharedSymbol{*this, name, sym.getBinding(),
+ sym.st_other, sym.getType(), sym.st_value,
+ sym.st_size, alignment, idx});
+ }
// Also add the symbol with the versioned name to handle undefined symbols
// with explicit versions.
- if (Idx == VER_NDX_GLOBAL)
+ if (idx == VER_NDX_GLOBAL)
continue;
- if (Idx >= Verdefs.size() || Idx == VER_NDX_LOCAL) {
- error("corrupt input file: version definition index " + Twine(Idx) +
- " for symbol " + Name + " is out of bounds\n>>> defined in " +
+ if (idx >= verdefs.size() || idx == VER_NDX_LOCAL) {
+ error("corrupt input file: version definition index " + Twine(idx) +
+ " for symbol " + name + " is out of bounds\n>>> defined in " +
toString(this));
continue;
}
- StringRef VerName =
- this->StringTable.data() + Verdefs[Idx]->getAux()->vda_name;
- VersionedNameBuffer.clear();
- Name = (Name + "@" + VerName).toStringRef(VersionedNameBuffer);
- Symtab->addShared(Saver.save(Name), *this, Sym, Alignment, Idx);
+ StringRef verName =
+ this->stringTable.data() +
+ reinterpret_cast<const Elf_Verdef *>(verdefs[idx])->getAux()->vda_name;
+ versionedNameBuffer.clear();
+ name = (name + "@" + verName).toStringRef(versionedNameBuffer);
+ symtab->addSymbol(SharedSymbol{*this, saver.save(name), sym.getBinding(),
+ sym.st_other, sym.getType(), sym.st_value,
+ sym.st_size, alignment, idx});
}
}
-static ELFKind getBitcodeELFKind(const Triple &T) {
- if (T.isLittleEndian())
- return T.isArch64Bit() ? ELF64LEKind : ELF32LEKind;
- return T.isArch64Bit() ? ELF64BEKind : ELF32BEKind;
+static ELFKind getBitcodeELFKind(const Triple &t) {
+ if (t.isLittleEndian())
+ return t.isArch64Bit() ? ELF64LEKind : ELF32LEKind;
+ return t.isArch64Bit() ? ELF64BEKind : ELF32BEKind;
}
-static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
- switch (T.getArch()) {
+static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) {
+ switch (t.getArch()) {
case Triple::aarch64:
return EM_AARCH64;
case Triple::amdgcn:
@@ -1088,25 +1404,28 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
case Triple::ppc64:
case Triple::ppc64le:
return EM_PPC64;
+ case Triple::riscv32:
+ case Triple::riscv64:
+ return EM_RISCV;
case Triple::x86:
- return T.isOSIAMCU() ? EM_IAMCU : EM_386;
+ return t.isOSIAMCU() ? EM_IAMCU : EM_386;
case Triple::x86_64:
return EM_X86_64;
default:
- error(Path + ": could not infer e_machine from bitcode target triple " +
- T.str());
+ error(path + ": could not infer e_machine from bitcode target triple " +
+ t.str());
return EM_NONE;
}
}
-BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName,
- uint64_t OffsetInArchive)
- : InputFile(BitcodeKind, MB) {
- this->ArchiveName = ArchiveName;
+BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive)
+ : InputFile(BitcodeKind, mb) {
+ this->archiveName = archiveName;
- std::string Path = MB.getBufferIdentifier().str();
- if (Config->ThinLTOIndexOnly)
- Path = replaceThinLTOSuffix(MB.getBufferIdentifier());
+ std::string path = mb.getBufferIdentifier().str();
+ if (config->thinLTOIndexOnly)
+ path = replaceThinLTOSuffix(mb.getBufferIdentifier());
// ThinLTO assumes that all MemoryBufferRefs given to it have a unique
// name. If two archives define two members with the same name, this
@@ -1114,20 +1433,21 @@ BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName,
// into consideration at LTO time (which very likely causes undefined
// symbols later in the link stage). So we append file offset to make
// filename unique.
- MemoryBufferRef MBRef(
- MB.getBuffer(),
- Saver.save(ArchiveName + Path +
- (ArchiveName.empty() ? "" : utostr(OffsetInArchive))));
+ StringRef name = archiveName.empty()
+ ? saver.save(path)
+ : saver.save(archiveName + "(" + path + " at " +
+ utostr(offsetInArchive) + ")");
+ MemoryBufferRef mbref(mb.getBuffer(), name);
- Obj = CHECK(lto::InputFile::create(MBRef), this);
+ obj = CHECK(lto::InputFile::create(mbref), this);
- Triple T(Obj->getTargetTriple());
- EKind = getBitcodeELFKind(T);
- EMachine = getBitcodeMachineKind(MB.getBufferIdentifier(), T);
+ Triple t(obj->getTargetTriple());
+ ekind = getBitcodeELFKind(t);
+ emachine = getBitcodeMachineKind(mb.getBufferIdentifier(), t);
}
-static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
- switch (GvVisibility) {
+static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) {
+ switch (gvVisibility) {
case GlobalValue::DefaultVisibility:
return STV_DEFAULT;
case GlobalValue::HiddenVisibility:
@@ -1139,209 +1459,187 @@ static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
}
template <class ELFT>
-static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
- const lto::InputFile::Symbol &ObjSym,
- BitcodeFile &F) {
- StringRef Name = Saver.save(ObjSym.getName());
- uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL;
-
- uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE;
- uint8_t Visibility = mapVisibility(ObjSym.getVisibility());
- bool CanOmitFromDynSym = ObjSym.canBeOmittedFromSymbolTable();
-
- int C = ObjSym.getComdatIndex();
- if (C != -1 && !KeptComdats[C])
- return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type,
- CanOmitFromDynSym, &F);
-
- if (ObjSym.isUndefined())
- return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type,
- CanOmitFromDynSym, &F);
-
- if (ObjSym.isCommon())
- return Symtab->addCommon(Name, ObjSym.getCommonSize(),
- ObjSym.getCommonAlignment(), Binding, Visibility,
- STT_OBJECT, F);
-
- return Symtab->addBitcode(Name, Binding, Visibility, Type, CanOmitFromDynSym,
- F);
-}
+static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
+ const lto::InputFile::Symbol &objSym,
+ BitcodeFile &f) {
+ StringRef name = saver.save(objSym.getName());
+ uint8_t binding = objSym.isWeak() ? STB_WEAK : STB_GLOBAL;
+ uint8_t type = objSym.isTLS() ? STT_TLS : STT_NOTYPE;
+ uint8_t visibility = mapVisibility(objSym.getVisibility());
+ bool canOmitFromDynSym = objSym.canBeOmittedFromSymbolTable();
+
+ int c = objSym.getComdatIndex();
+ if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) {
+ Undefined New(&f, name, binding, visibility, type);
+ if (canOmitFromDynSym)
+ New.exportDynamic = false;
+ return symtab->addSymbol(New);
+ }
-template <class ELFT>
-void BitcodeFile::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
- std::vector<bool> KeptComdats;
- for (StringRef S : Obj->getComdatTable())
- KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(S)).second);
+ if (objSym.isCommon())
+ return symtab->addSymbol(
+ CommonSymbol{&f, name, binding, visibility, STT_OBJECT,
+ objSym.getCommonAlignment(), objSym.getCommonSize()});
- for (const lto::InputFile::Symbol &ObjSym : Obj->symbols())
- Symbols.push_back(createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, *this));
+ Defined New(&f, name, binding, visibility, type, 0, 0, nullptr);
+ if (canOmitFromDynSym)
+ New.exportDynamic = false;
+ return symtab->addSymbol(New);
}
-static ELFKind getELFKind(MemoryBufferRef MB) {
- unsigned char Size;
- unsigned char Endian;
- std::tie(Size, Endian) = getElfArchType(MB.getBuffer());
-
- if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB)
- fatal(MB.getBufferIdentifier() + ": invalid data encoding");
- if (Size != ELFCLASS32 && Size != ELFCLASS64)
- fatal(MB.getBufferIdentifier() + ": invalid file class");
+template <class ELFT> void BitcodeFile::parse() {
+ std::vector<bool> keptComdats;
+ for (StringRef s : obj->getComdatTable())
+ keptComdats.push_back(
+ symtab->comdatGroups.try_emplace(CachedHashStringRef(s), this).second);
- size_t BufSize = MB.getBuffer().size();
- if ((Size == ELFCLASS32 && BufSize < sizeof(Elf32_Ehdr)) ||
- (Size == ELFCLASS64 && BufSize < sizeof(Elf64_Ehdr)))
- fatal(MB.getBufferIdentifier() + ": file is too short");
+ for (const lto::InputFile::Symbol &objSym : obj->symbols())
+ symbols.push_back(createBitcodeSymbol<ELFT>(keptComdats, objSym, *this));
- if (Size == ELFCLASS32)
- return (Endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind;
- return (Endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind;
+ for (auto l : obj->getDependentLibraries())
+ addDependentLibrary(l, this);
}
void BinaryFile::parse() {
- ArrayRef<uint8_t> Data = arrayRefFromStringRef(MB.getBuffer());
- auto *Section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- 8, Data, ".data");
- Sections.push_back(Section);
+ ArrayRef<uint8_t> data = arrayRefFromStringRef(mb.getBuffer());
+ auto *section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
+ 8, data, ".data");
+ sections.push_back(section);
// For each input file foo that is embedded to a result as a binary
// blob, we define _binary_foo_{start,end,size} symbols, so that
// user programs can access blobs by name. Non-alphanumeric
// characters in a filename are replaced with underscore.
- std::string S = "_binary_" + MB.getBufferIdentifier().str();
- for (size_t I = 0; I < S.size(); ++I)
- if (!isAlnum(S[I]))
- S[I] = '_';
-
- Symtab->addDefined(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0,
- STB_GLOBAL, Section, nullptr);
- Symtab->addDefined(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT,
- Data.size(), 0, STB_GLOBAL, Section, nullptr);
- Symtab->addDefined(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT,
- Data.size(), 0, STB_GLOBAL, nullptr, nullptr);
+ std::string s = "_binary_" + mb.getBufferIdentifier().str();
+ for (size_t i = 0; i < s.size(); ++i)
+ if (!isAlnum(s[i]))
+ s[i] = '_';
+
+ symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL,
+ STV_DEFAULT, STT_OBJECT, 0, 0, section});
+ symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL,
+ STV_DEFAULT, STT_OBJECT, data.size(), 0, section});
+ symtab->addSymbol(Defined{nullptr, saver.save(s + "_size"), STB_GLOBAL,
+ STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
}
-InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
- uint64_t OffsetInArchive) {
- if (isBitcode(MB))
- return make<BitcodeFile>(MB, ArchiveName, OffsetInArchive);
+InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive) {
+ if (isBitcode(mb))
+ return make<BitcodeFile>(mb, archiveName, offsetInArchive);
- switch (getELFKind(MB)) {
+ switch (getELFKind(mb, archiveName)) {
case ELF32LEKind:
- return make<ObjFile<ELF32LE>>(MB, ArchiveName);
+ return make<ObjFile<ELF32LE>>(mb, archiveName);
case ELF32BEKind:
- return make<ObjFile<ELF32BE>>(MB, ArchiveName);
+ return make<ObjFile<ELF32BE>>(mb, archiveName);
case ELF64LEKind:
- return make<ObjFile<ELF64LE>>(MB, ArchiveName);
+ return make<ObjFile<ELF64LE>>(mb, archiveName);
case ELF64BEKind:
- return make<ObjFile<ELF64BE>>(MB, ArchiveName);
+ return make<ObjFile<ELF64BE>>(mb, archiveName);
default:
llvm_unreachable("getELFKind");
}
}
-InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) {
- switch (getELFKind(MB)) {
- case ELF32LEKind:
- return make<SharedFile<ELF32LE>>(MB, DefaultSoName);
- case ELF32BEKind:
- return make<SharedFile<ELF32BE>>(MB, DefaultSoName);
- case ELF64LEKind:
- return make<SharedFile<ELF64LE>>(MB, DefaultSoName);
- case ELF64BEKind:
- return make<SharedFile<ELF64BE>>(MB, DefaultSoName);
- default:
- llvm_unreachable("getELFKind");
- }
-}
+void LazyObjFile::fetch() {
+ if (mb.getBuffer().empty())
+ return;
-MemoryBufferRef LazyObjFile::getBuffer() {
- if (AddedToLink)
- return MemoryBufferRef();
- AddedToLink = true;
- return MB;
-}
+ InputFile *file = createObjectFile(mb, archiveName, offsetInArchive);
+ file->groupId = groupId;
-InputFile *LazyObjFile::fetch() {
- MemoryBufferRef MBRef = getBuffer();
- if (MBRef.getBuffer().empty())
- return nullptr;
+ mb = {};
+
+ // Copy symbol vector so that the new InputFile doesn't have to
+ // insert the same defined symbols to the symbol table again.
+ file->symbols = std::move(symbols);
- InputFile *File = createObjectFile(MBRef, ArchiveName, OffsetInArchive);
- File->GroupId = GroupId;
- return File;
+ parseFile(file);
}
template <class ELFT> void LazyObjFile::parse() {
+ using Elf_Sym = typename ELFT::Sym;
+
// A lazy object file wraps either a bitcode file or an ELF file.
- if (isBitcode(this->MB)) {
- std::unique_ptr<lto::InputFile> Obj =
- CHECK(lto::InputFile::create(this->MB), this);
- for (const lto::InputFile::Symbol &Sym : Obj->symbols())
- if (!Sym.isUndefined())
- Symtab->addLazyObject<ELFT>(Saver.save(Sym.getName()), *this);
+ if (isBitcode(this->mb)) {
+ std::unique_ptr<lto::InputFile> obj =
+ CHECK(lto::InputFile::create(this->mb), this);
+ for (const lto::InputFile::Symbol &sym : obj->symbols()) {
+ if (sym.isUndefined())
+ continue;
+ symtab->addSymbol(LazyObject{*this, saver.save(sym.getName())});
+ }
return;
}
- if (getELFKind(this->MB) != Config->EKind) {
- error("incompatible file: " + this->MB.getBufferIdentifier());
+ if (getELFKind(this->mb, archiveName) != config->ekind) {
+ error("incompatible file: " + this->mb.getBufferIdentifier());
return;
}
- ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer()));
- ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this);
+ // Find a symbol table.
+ ELFFile<ELFT> obj = check(ELFFile<ELFT>::create(mb.getBuffer()));
+ ArrayRef<typename ELFT::Shdr> sections = CHECK(obj.sections(), this);
- for (const typename ELFT::Shdr &Sec : Sections) {
- if (Sec.sh_type != SHT_SYMTAB)
+ for (const typename ELFT::Shdr &sec : sections) {
+ if (sec.sh_type != SHT_SYMTAB)
continue;
- typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this);
- uint32_t FirstGlobal = Sec.sh_info;
- StringRef StringTable =
- CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
+ // A symbol table is found.
+ ArrayRef<Elf_Sym> eSyms = CHECK(obj.symbols(&sec), this);
+ uint32_t firstGlobal = sec.sh_info;
+ StringRef strtab = CHECK(obj.getStringTableForSymtab(sec, sections), this);
+ this->symbols.resize(eSyms.size());
+
+ // Get existing symbols or insert placeholder symbols.
+ for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i)
+ if (eSyms[i].st_shndx != SHN_UNDEF)
+ this->symbols[i] = symtab->insert(CHECK(eSyms[i].getName(strtab), this));
+
+ // Replace existing symbols with LazyObject symbols.
+ //
+ // resolve() may trigger this->fetch() if an existing symbol is an
+ // undefined symbol. If that happens, this LazyObjFile has served
+ // its purpose, and we can exit from the loop early.
+ for (Symbol *sym : this->symbols) {
+ if (!sym)
+ continue;
+ sym->resolve(LazyObject{*this, sym->getName()});
- for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal))
- if (Sym.st_shndx != SHN_UNDEF)
- Symtab->addLazyObject<ELFT>(CHECK(Sym.getName(StringTable), this),
- *this);
+ // MemoryBuffer is emptied if this file is instantiated as ObjFile.
+ if (mb.getBuffer().empty())
+ return;
+ }
return;
}
}
-std::string elf::replaceThinLTOSuffix(StringRef Path) {
- StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first;
- StringRef Repl = Config->ThinLTOObjectSuffixReplace.second;
+std::string elf::replaceThinLTOSuffix(StringRef path) {
+ StringRef suffix = config->thinLTOObjectSuffixReplace.first;
+ StringRef repl = config->thinLTOObjectSuffixReplace.second;
- if (Path.consume_back(Suffix))
- return (Path + Repl).str();
- return Path;
+ if (path.consume_back(suffix))
+ return (path + repl).str();
+ return path;
}
-template void ArchiveFile::parse<ELF32LE>();
-template void ArchiveFile::parse<ELF32BE>();
-template void ArchiveFile::parse<ELF64LE>();
-template void ArchiveFile::parse<ELF64BE>();
-
-template void BitcodeFile::parse<ELF32LE>(DenseSet<CachedHashStringRef> &);
-template void BitcodeFile::parse<ELF32BE>(DenseSet<CachedHashStringRef> &);
-template void BitcodeFile::parse<ELF64LE>(DenseSet<CachedHashStringRef> &);
-template void BitcodeFile::parse<ELF64BE>(DenseSet<CachedHashStringRef> &);
+template void BitcodeFile::parse<ELF32LE>();
+template void BitcodeFile::parse<ELF32BE>();
+template void BitcodeFile::parse<ELF64LE>();
+template void BitcodeFile::parse<ELF64BE>();
template void LazyObjFile::parse<ELF32LE>();
template void LazyObjFile::parse<ELF32BE>();
template void LazyObjFile::parse<ELF64LE>();
template void LazyObjFile::parse<ELF64BE>();
-template class elf::ELFFileBase<ELF32LE>;
-template class elf::ELFFileBase<ELF32BE>;
-template class elf::ELFFileBase<ELF64LE>;
-template class elf::ELFFileBase<ELF64BE>;
-
template class elf::ObjFile<ELF32LE>;
template class elf::ObjFile<ELF32BE>;
template class elf::ObjFile<ELF64LE>;
template class elf::ObjFile<ELF64BE>;
-template class elf::SharedFile<ELF32LE>;
-template class elf::SharedFile<ELF32BE>;
-template class elf::SharedFile<ELF64LE>;
-template class elf::SharedFile<ELF64BE>;
+template void SharedFile::parse<ELF32LE>();
+template void SharedFile::parse<ELF32BE>();
+template void SharedFile::parse<ELF64LE>();
+template void SharedFile::parse<ELF64BE>();
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index 5094ddd804a5f..5ccc3d402b376 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -1,9 +1,8 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -40,7 +39,7 @@ class InputSectionBase;
}
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
-std::string toString(const elf::InputFile *F);
+std::string toString(const elf::InputFile *f);
namespace elf {
@@ -50,10 +49,13 @@ class Symbol;
// If -reproduce option is given, all input files are written
// to this tar archive.
-extern std::unique_ptr<llvm::TarWriter> Tar;
+extern std::unique_ptr<llvm::TarWriter> tar;
// Opens a given file.
-llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
+llvm::Optional<MemoryBufferRef> readFile(StringRef path);
+
+// Add symbols in File to the symbol table.
+void parseFile(InputFile *file);
// The root class of input files.
class InputFile {
@@ -67,192 +69,230 @@ public:
BinaryKind,
};
- Kind kind() const { return FileKind; }
+ Kind kind() const { return fileKind; }
bool isElf() const {
- Kind K = kind();
- return K == ObjKind || K == SharedKind;
+ Kind k = kind();
+ return k == ObjKind || k == SharedKind;
}
- StringRef getName() const { return MB.getBufferIdentifier(); }
- MemoryBufferRef MB;
+ StringRef getName() const { return mb.getBufferIdentifier(); }
+ MemoryBufferRef mb;
// Returns sections. It is a runtime error to call this function
// on files that don't have the notion of sections.
ArrayRef<InputSectionBase *> getSections() const {
- assert(FileKind == ObjKind || FileKind == BinaryKind);
- return Sections;
+ assert(fileKind == ObjKind || fileKind == BinaryKind);
+ return sections;
}
// Returns object file symbols. It is a runtime error to call this
// function on files of other types.
ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); }
- std::vector<Symbol *> &getMutableSymbols() {
- assert(FileKind == BinaryKind || FileKind == ObjKind ||
- FileKind == BitcodeKind);
- return Symbols;
+ MutableArrayRef<Symbol *> getMutableSymbols() {
+ assert(fileKind == BinaryKind || fileKind == ObjKind ||
+ fileKind == BitcodeKind);
+ return symbols;
}
// Filename of .a which contained this file. If this file was
// not in an archive file, it is the empty string. We use this
// string for creating error messages.
- std::string ArchiveName;
+ std::string archiveName;
// If this is an architecture-specific file, the following members
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
- ELFKind EKind = ELFNoneKind;
- uint16_t EMachine = llvm::ELF::EM_NONE;
- uint8_t OSABI = 0;
+ ELFKind ekind = ELFNoneKind;
+ uint16_t emachine = llvm::ELF::EM_NONE;
+ uint8_t osabi = 0;
+ uint8_t abiVersion = 0;
// Cache for toString(). Only toString() should use this member.
- mutable std::string ToStringCache;
+ mutable std::string toStringCache;
- std::string getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
- uint64_t Offset);
+ std::string getSrcMsg(const Symbol &sym, InputSectionBase &sec,
+ uint64_t offset);
// True if this is an argument for --just-symbols. Usually false.
- bool JustSymbols = false;
-
- // GroupId is used for --warn-backrefs which is an optional error
+ bool justSymbols = false;
+
+ // outSecOff of .got2 in the current file. This is used by PPC32 -fPIC/-fPIE
+ // to compute offsets in PLT call stubs.
+ uint32_t ppc32Got2OutSecOff = 0;
+
+ // On PPC64 we need to keep track of which files contain small code model
+ // relocations that access the .toc section. To minimize the chance of a
+ // relocation overflow, files that do contain said relocations should have
+ // their .toc sections sorted closer to the .got section than files that do
+ // not contain any small code model relocations. Thats because the toc-pointer
+ // is defined to point at .got + 0x8000 and the instructions used with small
+ // code model relocations support immediates in the range [-0x8000, 0x7FFC],
+ // making the addressable range relative to the toc pointer
+ // [.got, .got + 0xFFFC].
+ bool ppc64SmallCodeModelTocRelocs = false;
+
+ // groupId is used for --warn-backrefs which is an optional error
// checking feature. All files within the same --{start,end}-group or
// --{start,end}-lib get the same group ID. Otherwise, each file gets a new
// group ID. For more info, see checkDependency() in SymbolTable.cpp.
- uint32_t GroupId;
- static bool IsInGroup;
- static uint32_t NextGroupId;
+ uint32_t groupId;
+ static bool isInGroup;
+ static uint32_t nextGroupId;
// Index of MIPS GOT built for this file.
- llvm::Optional<size_t> MipsGotIndex;
+ llvm::Optional<size_t> mipsGotIndex;
+
+ std::vector<Symbol *> symbols;
protected:
- InputFile(Kind K, MemoryBufferRef M);
- std::vector<InputSectionBase *> Sections;
- std::vector<Symbol *> Symbols;
+ InputFile(Kind k, MemoryBufferRef m);
+ std::vector<InputSectionBase *> sections;
private:
- const Kind FileKind;
+ const Kind fileKind;
};
-template <typename ELFT> class ELFFileBase : public InputFile {
+class ELFFileBase : public InputFile {
public:
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::Word Elf_Word;
- typedef typename ELFT::SymRange Elf_Sym_Range;
+ ELFFileBase(Kind k, MemoryBufferRef m);
+ static bool classof(const InputFile *f) { return f->isElf(); }
- ELFFileBase(Kind K, MemoryBufferRef M);
- static bool classof(const InputFile *F) { return F->isElf(); }
-
- llvm::object::ELFFile<ELFT> getObj() const {
- return check(llvm::object::ELFFile<ELFT>::create(MB.getBuffer()));
+ template <typename ELFT> llvm::object::ELFFile<ELFT> getObj() const {
+ return check(llvm::object::ELFFile<ELFT>::create(mb.getBuffer()));
}
- StringRef getStringTable() const { return StringTable; }
-
- uint32_t getSectionIndex(const Elf_Sym &Sym) const;
+ StringRef getStringTable() const { return stringTable; }
- Elf_Sym_Range getGlobalELFSyms();
- Elf_Sym_Range getELFSyms() const { return ELFSyms; }
+ template <typename ELFT> typename ELFT::SymRange getELFSyms() const {
+ return typename ELFT::SymRange(
+ reinterpret_cast<const typename ELFT::Sym *>(elfSyms), numELFSyms);
+ }
+ template <typename ELFT> typename ELFT::SymRange getGlobalELFSyms() const {
+ return getELFSyms<ELFT>().slice(firstGlobal);
+ }
protected:
- ArrayRef<Elf_Sym> ELFSyms;
- uint32_t FirstGlobal = 0;
- ArrayRef<Elf_Word> SymtabSHNDX;
- StringRef StringTable;
- void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab);
+ // Initializes this class's member variables.
+ template <typename ELFT> void init();
+
+ const void *elfSyms = nullptr;
+ size_t numELFSyms = 0;
+ uint32_t firstGlobal = 0;
+ StringRef stringTable;
};
// .o file.
-template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
- typedef ELFFileBase<ELFT> Base;
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Word Elf_Word;
- typedef typename ELFT::CGProfile Elf_CGProfile;
-
- StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
- const Elf_Shdr &Sec);
- ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec);
+template <class ELFT> class ObjFile : public ELFFileBase {
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
+ using Elf_Sym = typename ELFT::Sym;
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Word = typename ELFT::Word;
+ using Elf_CGProfile = typename ELFT::CGProfile;
public:
- static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; }
+ static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
+
+ llvm::object::ELFFile<ELFT> getObj() const {
+ return this->ELFFileBase::getObj<ELFT>();
+ }
ArrayRef<Symbol *> getLocalSymbols();
ArrayRef<Symbol *> getGlobalSymbols();
- ObjFile(MemoryBufferRef M, StringRef ArchiveName);
- void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
+ ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) {
+ this->archiveName = archiveName;
+ }
+
+ void parse(bool ignoreComdats = false);
- Symbol &getSymbol(uint32_t SymbolIndex) const {
- if (SymbolIndex >= this->Symbols.size())
+ StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
+ const Elf_Shdr &sec);
+
+ Symbol &getSymbol(uint32_t symbolIndex) const {
+ if (symbolIndex >= this->symbols.size())
fatal(toString(this) + ": invalid symbol index");
- return *this->Symbols[SymbolIndex];
+ return *this->symbols[symbolIndex];
}
- template <typename RelT> Symbol &getRelocTargetSym(const RelT &Rel) const {
- uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
- return getSymbol(SymIndex);
+ uint32_t getSectionIndex(const Elf_Sym &sym) const;
+
+ template <typename RelT> Symbol &getRelocTargetSym(const RelT &rel) const {
+ uint32_t symIndex = rel.getSymbol(config->isMips64EL);
+ return getSymbol(symIndex);
}
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
- llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name);
+ llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef name);
// MIPS GP0 value defined by this file. This value represents the gp value
// used to create the relocatable object and required to support
// R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
- uint32_t MipsGp0 = 0;
+ uint32_t mipsGp0 = 0;
+
+ uint32_t andFeatures = 0;
// Name of source file obtained from STT_FILE symbol value,
// or empty string if there is no such symbol in object file
// symbol table.
- StringRef SourceFile;
+ StringRef sourceFile;
// True if the file defines functions compiled with
// -fsplit-stack. Usually false.
- bool SplitStack = false;
+ bool splitStack = false;
// True if the file defines functions compiled with -fsplit-stack,
// but had one or more functions with the no_split_stack attribute.
- bool SomeNoSplitStack = false;
+ bool someNoSplitStack = false;
// Pointer to this input file's .llvm_addrsig section, if it has one.
- const Elf_Shdr *AddrsigSec = nullptr;
+ const Elf_Shdr *addrsigSec = nullptr;
// SHT_LLVM_CALL_GRAPH_PROFILE table
- ArrayRef<Elf_CGProfile> CGProfile;
+ ArrayRef<Elf_CGProfile> cgProfile;
private:
- void
- initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
+ void initializeSections(bool ignoreComdats);
void initializeSymbols();
void initializeJustSymbols();
void initializeDwarf();
- InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
- InputSectionBase *createInputSection(const Elf_Shdr &Sec);
- StringRef getSectionName(const Elf_Shdr &Sec);
-
- bool shouldMerge(const Elf_Shdr &Sec);
- Symbol *createSymbol(const Elf_Sym *Sym);
+ InputSectionBase *getRelocTarget(const Elf_Shdr &sec);
+ InputSectionBase *createInputSection(const Elf_Shdr &sec);
+ StringRef getSectionName(const Elf_Shdr &sec);
+
+ bool shouldMerge(const Elf_Shdr &sec);
+
+ // Each ELF symbol contains a section index which the symbol belongs to.
+ // However, because the number of bits dedicated for that is limited, a
+ // symbol can directly point to a section only when the section index is
+ // equal to or smaller than 65280.
+ //
+ // If an object file contains more than 65280 sections, the file must
+ // contain .symtab_shndx section. The section contains an array of
+ // 32-bit integers whose size is the same as the number of symbols.
+ // Nth symbol's section index is in the Nth entry of .symtab_shndx.
+ //
+ // The following variable contains the contents of .symtab_shndx.
+ // If the section does not exist (which is common), the array is empty.
+ ArrayRef<Elf_Word> shndxTable;
// .shstrtab contents.
- StringRef SectionStringTable;
+ StringRef sectionStringTable;
// Debugging information to retrieve source file and line for error
// reporting. Linker may find reasonable number of errors in a
// single object file, so we cache debugging information in order to
// parse it only once for each object file we link.
- std::unique_ptr<llvm::DWARFContext> Dwarf;
- std::vector<const llvm::DWARFDebugLine::LineTable *> LineTables;
+ std::unique_ptr<llvm::DWARFContext> dwarf;
+ std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
struct VarLoc {
- const llvm::DWARFDebugLine::LineTable *LT;
- unsigned File;
- unsigned Line;
+ const llvm::DWARFDebugLine::LineTable *lt;
+ unsigned file;
+ unsigned line;
};
- llvm::DenseMap<StringRef, VarLoc> VariableLoc;
- llvm::once_flag InitDwarfLine;
+ llvm::DenseMap<StringRef, VarLoc> variableLoc;
+ llvm::once_flag initDwarfLine;
};
// LazyObjFile is analogous to ArchiveFile in the sense that
@@ -264,118 +304,100 @@ private:
// archive file semantics.
class LazyObjFile : public InputFile {
public:
- LazyObjFile(MemoryBufferRef M, StringRef ArchiveName,
- uint64_t OffsetInArchive)
- : InputFile(LazyObjKind, M), OffsetInArchive(OffsetInArchive) {
- this->ArchiveName = ArchiveName;
+ LazyObjFile(MemoryBufferRef m, StringRef archiveName,
+ uint64_t offsetInArchive)
+ : InputFile(LazyObjKind, m), offsetInArchive(offsetInArchive) {
+ this->archiveName = archiveName;
}
- static bool classof(const InputFile *F) { return F->kind() == LazyObjKind; }
+ static bool classof(const InputFile *f) { return f->kind() == LazyObjKind; }
template <class ELFT> void parse();
- MemoryBufferRef getBuffer();
- InputFile *fetch();
- bool AddedToLink = false;
+ void fetch();
private:
- uint64_t OffsetInArchive;
+ uint64_t offsetInArchive;
};
// An ArchiveFile object represents a .a file.
class ArchiveFile : public InputFile {
public:
- explicit ArchiveFile(std::unique_ptr<Archive> &&File);
- static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
- template <class ELFT> void parse();
+ explicit ArchiveFile(std::unique_ptr<Archive> &&file);
+ static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
+ void parse();
// Pulls out an object file that contains a definition for Sym and
// returns it. If the same file was instantiated before, this
- // function returns a nullptr (so we don't instantiate the same file
+ // function does nothing (so we don't instantiate the same file
// more than once.)
- InputFile *fetch(const Archive::Symbol &Sym);
+ void fetch(const Archive::Symbol &sym);
private:
- std::unique_ptr<Archive> File;
- llvm::DenseSet<uint64_t> Seen;
+ std::unique_ptr<Archive> file;
+ llvm::DenseSet<uint64_t> seen;
};
class BitcodeFile : public InputFile {
public:
- BitcodeFile(MemoryBufferRef M, StringRef ArchiveName,
- uint64_t OffsetInArchive);
- static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
- template <class ELFT>
- void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
- std::unique_ptr<llvm::lto::InputFile> Obj;
+ BitcodeFile(MemoryBufferRef m, StringRef archiveName,
+ uint64_t offsetInArchive);
+ static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
+ template <class ELFT> void parse();
+ std::unique_ptr<llvm::lto::InputFile> obj;
};
// .so file.
-template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
- typedef ELFFileBase<ELFT> Base;
- typedef typename ELFT::Dyn Elf_Dyn;
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::SymRange Elf_Sym_Range;
- typedef typename ELFT::Verdef Elf_Verdef;
- typedef typename ELFT::Versym Elf_Versym;
-
- const Elf_Shdr *VersymSec = nullptr;
- const Elf_Shdr *VerdefSec = nullptr;
-
+class SharedFile : public ELFFileBase {
public:
- std::vector<const Elf_Verdef *> Verdefs;
- std::string SoName;
+ SharedFile(MemoryBufferRef m, StringRef defaultSoName)
+ : ELFFileBase(SharedKind, m), soName(defaultSoName),
+ isNeeded(!config->asNeeded) {}
- static bool classof(const InputFile *F) {
- return F->kind() == Base::SharedKind;
- }
+ // This is actually a vector of Elf_Verdef pointers.
+ std::vector<const void *> verdefs;
- SharedFile(MemoryBufferRef M, StringRef DefaultSoName);
+ // If the output file needs Elf_Verneed data structures for this file, this is
+ // a vector of Elf_Vernaux version identifiers that map onto the entries in
+ // Verdefs, otherwise it is empty.
+ std::vector<unsigned> vernauxs;
- void parseSoName();
- void parseRest();
- uint32_t getAlignment(ArrayRef<Elf_Shdr> Sections, const Elf_Sym &Sym);
- std::vector<const Elf_Verdef *> parseVerdefs();
- std::vector<uint32_t> parseVersyms();
+ static unsigned vernauxNum;
- struct NeededVer {
- // The string table offset of the version name in the output file.
- size_t StrTab;
+ std::vector<StringRef> dtNeeded;
+ std::string soName;
- // The version identifier for this version name.
- uint16_t Index;
- };
+ static bool classof(const InputFile *f) { return f->kind() == SharedKind; }
+
+ template <typename ELFT> void parse();
- // Mapping from Elf_Verdef data structures to information about Elf_Vernaux
- // data structures in the output file.
- std::map<const Elf_Verdef *, NeededVer> VerdefMap;
+ // Used for --no-allow-shlib-undefined.
+ bool allNeededIsKnown;
// Used for --as-needed
- bool IsNeeded;
+ bool isNeeded;
};
class BinaryFile : public InputFile {
public:
- explicit BinaryFile(MemoryBufferRef M) : InputFile(BinaryKind, M) {}
- static bool classof(const InputFile *F) { return F->kind() == BinaryKind; }
+ explicit BinaryFile(MemoryBufferRef m) : InputFile(BinaryKind, m) {}
+ static bool classof(const InputFile *f) { return f->kind() == BinaryKind; }
void parse();
};
-InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
- uint64_t OffsetInArchive = 0);
-InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName);
+InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "",
+ uint64_t offsetInArchive = 0);
-inline bool isBitcode(MemoryBufferRef MB) {
- return identify_magic(MB.getBuffer()) == llvm::file_magic::bitcode;
+inline bool isBitcode(MemoryBufferRef mb) {
+ return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
}
-std::string replaceThinLTOSuffix(StringRef Path);
+std::string replaceThinLTOSuffix(StringRef path);
-extern std::vector<BinaryFile *> BinaryFiles;
-extern std::vector<BitcodeFile *> BitcodeFiles;
-extern std::vector<LazyObjFile *> LazyObjFiles;
-extern std::vector<InputFile *> ObjectFiles;
-extern std::vector<InputFile *> SharedFiles;
+extern std::vector<BinaryFile *> binaryFiles;
+extern std::vector<BitcodeFile *> bitcodeFiles;
+extern std::vector<LazyObjFile *> lazyObjFiles;
+extern std::vector<InputFile *> objectFiles;
+extern std::vector<SharedFile *> sharedFiles;
} // namespace elf
} // namespace lld
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp
index 839bff7011eb8..a024ac307b0a9 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -1,9 +1,8 @@
//===- InputSection.cpp ---------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -41,52 +40,52 @@ using namespace llvm::sys;
using namespace lld;
using namespace lld::elf;
-std::vector<InputSectionBase *> elf::InputSections;
+std::vector<InputSectionBase *> elf::inputSections;
// Returns a string to construct an error message.
-std::string lld::toString(const InputSectionBase *Sec) {
- return (toString(Sec->File) + ":(" + Sec->Name + ")").str();
+std::string lld::toString(const InputSectionBase *sec) {
+ return (toString(sec->file) + ":(" + sec->name + ")").str();
}
template <class ELFT>
-static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &File,
- const typename ELFT::Shdr &Hdr) {
- if (Hdr.sh_type == SHT_NOBITS)
- return makeArrayRef<uint8_t>(nullptr, Hdr.sh_size);
- return check(File.getObj().getSectionContents(&Hdr));
+static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
+ const typename ELFT::Shdr &hdr) {
+ if (hdr.sh_type == SHT_NOBITS)
+ return makeArrayRef<uint8_t>(nullptr, hdr.sh_size);
+ return check(file.getObj().getSectionContents(&hdr));
}
-InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
- uint32_t Type, uint64_t Entsize,
- uint32_t Link, uint32_t Info,
- uint32_t Alignment, ArrayRef<uint8_t> Data,
- StringRef Name, Kind SectionKind)
- : SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info,
- Link),
- File(File), RawData(Data) {
+InputSectionBase::InputSectionBase(InputFile *file, uint64_t flags,
+ uint32_t type, uint64_t entsize,
+ uint32_t link, uint32_t info,
+ uint32_t alignment, ArrayRef<uint8_t> data,
+ StringRef name, Kind sectionKind)
+ : SectionBase(sectionKind, name, flags, entsize, alignment, type, info,
+ link),
+ file(file), rawData(data) {
// In order to reduce memory allocation, we assume that mergeable
// sections are smaller than 4 GiB, which is not an unreasonable
// assumption as of 2017.
- if (SectionKind == SectionBase::Merge && RawData.size() > UINT32_MAX)
+ if (sectionKind == SectionBase::Merge && rawData.size() > UINT32_MAX)
error(toString(this) + ": section too large");
- NumRelocations = 0;
- AreRelocsRela = false;
+ numRelocations = 0;
+ areRelocsRela = false;
// The ELF spec states that a value of 0 means the section has
// no alignment constraits.
- uint32_t V = std::max<uint64_t>(Alignment, 1);
- if (!isPowerOf2_64(V))
- fatal(toString(File) + ": section sh_addralign is not a power of 2");
- this->Alignment = V;
+ uint32_t v = std::max<uint32_t>(alignment, 1);
+ if (!isPowerOf2_64(v))
+ fatal(toString(this) + ": sh_addralign is not a power of 2");
+ this->alignment = v;
// In ELF, each section can be compressed by zlib, and if compressed,
// section name may be mangled by appending "z" (e.g. ".zdebug_info").
// If that's the case, demangle section name so that we can handle a
// section as if it weren't compressed.
- if ((Flags & SHF_COMPRESSED) || Name.startswith(".zdebug")) {
+ if ((flags & SHF_COMPRESSED) || name.startswith(".zdebug")) {
if (!zlib::isAvailable())
- error(toString(File) + ": contains a compressed section, " +
+ error(toString(file) + ": contains a compressed section, " +
"but zlib is not available");
parseCompressedHeader();
}
@@ -95,11 +94,11 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
// Drop SHF_GROUP bit unless we are producing a re-linkable object file.
// SHF_GROUP is a marker that a section belongs to some comdat group.
// That flag doesn't make sense in an executable.
-static uint64_t getFlags(uint64_t Flags) {
- Flags &= ~(uint64_t)SHF_INFO_LINK;
- if (!Config->Relocatable)
- Flags &= ~(uint64_t)SHF_GROUP;
- return Flags;
+static uint64_t getFlags(uint64_t flags) {
+ flags &= ~(uint64_t)SHF_INFO_LINK;
+ if (!config->relocatable)
+ flags &= ~(uint64_t)SHF_GROUP;
+ return flags;
}
// GNU assembler 2.24 and LLVM 4.0.0's MC (the newest release as of
@@ -112,205 +111,212 @@ static uint64_t getFlags(uint64_t Flags) {
//
// This function forces SHT_{INIT,FINI}_ARRAY so that we can handle
// incorrect inputs as if they were correct from the beginning.
-static uint64_t getType(uint64_t Type, StringRef Name) {
- if (Type == SHT_PROGBITS && Name.startswith(".init_array."))
+static uint64_t getType(uint64_t type, StringRef name) {
+ if (type == SHT_PROGBITS && name.startswith(".init_array."))
return SHT_INIT_ARRAY;
- if (Type == SHT_PROGBITS && Name.startswith(".fini_array."))
+ if (type == SHT_PROGBITS && name.startswith(".fini_array."))
return SHT_FINI_ARRAY;
- return Type;
+ return type;
}
template <class ELFT>
-InputSectionBase::InputSectionBase(ObjFile<ELFT> &File,
- const typename ELFT::Shdr &Hdr,
- StringRef Name, Kind SectionKind)
- : InputSectionBase(&File, getFlags(Hdr.sh_flags),
- getType(Hdr.sh_type, Name), Hdr.sh_entsize, Hdr.sh_link,
- Hdr.sh_info, Hdr.sh_addralign,
- getSectionContents(File, Hdr), Name, SectionKind) {
+InputSectionBase::InputSectionBase(ObjFile<ELFT> &file,
+ const typename ELFT::Shdr &hdr,
+ StringRef name, Kind sectionKind)
+ : InputSectionBase(&file, getFlags(hdr.sh_flags),
+ getType(hdr.sh_type, name), hdr.sh_entsize, hdr.sh_link,
+ hdr.sh_info, hdr.sh_addralign,
+ getSectionContents(file, hdr), name, sectionKind) {
// We reject object files having insanely large alignments even though
// they are allowed by the spec. I think 4GB is a reasonable limitation.
// We might want to relax this in the future.
- if (Hdr.sh_addralign > UINT32_MAX)
- fatal(toString(&File) + ": section sh_addralign is too large");
+ if (hdr.sh_addralign > UINT32_MAX)
+ fatal(toString(&file) + ": section sh_addralign is too large");
}
size_t InputSectionBase::getSize() const {
- if (auto *S = dyn_cast<SyntheticSection>(this))
- return S->getSize();
- if (UncompressedSize >= 0)
- return UncompressedSize;
- return RawData.size();
+ if (auto *s = dyn_cast<SyntheticSection>(this))
+ return s->getSize();
+ if (uncompressedSize >= 0)
+ return uncompressedSize;
+ return rawData.size();
}
void InputSectionBase::uncompress() const {
- size_t Size = UncompressedSize;
- UncompressedBuf.reset(new char[Size]);
+ size_t size = uncompressedSize;
+ char *uncompressedBuf;
+ {
+ static std::mutex mu;
+ std::lock_guard<std::mutex> lock(mu);
+ uncompressedBuf = bAlloc.Allocate<char>(size);
+ }
- if (Error E =
- zlib::uncompress(toStringRef(RawData), UncompressedBuf.get(), Size))
+ if (Error e = zlib::uncompress(toStringRef(rawData), uncompressedBuf, size))
fatal(toString(this) +
- ": uncompress failed: " + llvm::toString(std::move(E)));
- RawData = makeArrayRef((uint8_t *)UncompressedBuf.get(), Size);
+ ": uncompress failed: " + llvm::toString(std::move(e)));
+ rawData = makeArrayRef((uint8_t *)uncompressedBuf, size);
+ uncompressedSize = -1;
}
uint64_t InputSectionBase::getOffsetInFile() const {
- const uint8_t *FileStart = (const uint8_t *)File->MB.getBufferStart();
- const uint8_t *SecStart = data().begin();
- return SecStart - FileStart;
+ const uint8_t *fileStart = (const uint8_t *)file->mb.getBufferStart();
+ const uint8_t *secStart = data().begin();
+ return secStart - fileStart;
}
-uint64_t SectionBase::getOffset(uint64_t Offset) const {
+uint64_t SectionBase::getOffset(uint64_t offset) const {
switch (kind()) {
case Output: {
- auto *OS = cast<OutputSection>(this);
+ auto *os = cast<OutputSection>(this);
// For output sections we treat offset -1 as the end of the section.
- return Offset == uint64_t(-1) ? OS->Size : Offset;
+ return offset == uint64_t(-1) ? os->size : offset;
}
case Regular:
case Synthetic:
- return cast<InputSection>(this)->getOffset(Offset);
+ return cast<InputSection>(this)->getOffset(offset);
case EHFrame:
// The file crtbeginT.o has relocations pointing to the start of an empty
// .eh_frame that is known to be the first in the link. It does that to
// identify the start of the output .eh_frame.
- return Offset;
+ return offset;
case Merge:
- const MergeInputSection *MS = cast<MergeInputSection>(this);
- if (InputSection *IS = MS->getParent())
- return IS->getOffset(MS->getParentOffset(Offset));
- return MS->getParentOffset(Offset);
+ const MergeInputSection *ms = cast<MergeInputSection>(this);
+ if (InputSection *isec = ms->getParent())
+ return isec->getOffset(ms->getParentOffset(offset));
+ return ms->getParentOffset(offset);
}
llvm_unreachable("invalid section kind");
}
-uint64_t SectionBase::getVA(uint64_t Offset) const {
- const OutputSection *Out = getOutputSection();
- return (Out ? Out->Addr : 0) + getOffset(Offset);
+uint64_t SectionBase::getVA(uint64_t offset) const {
+ const OutputSection *out = getOutputSection();
+ return (out ? out->addr : 0) + getOffset(offset);
}
OutputSection *SectionBase::getOutputSection() {
- InputSection *Sec;
- if (auto *IS = dyn_cast<InputSection>(this))
- Sec = IS;
- else if (auto *MS = dyn_cast<MergeInputSection>(this))
- Sec = MS->getParent();
- else if (auto *EH = dyn_cast<EhInputSection>(this))
- Sec = EH->getParent();
+ InputSection *sec;
+ if (auto *isec = dyn_cast<InputSection>(this))
+ sec = isec;
+ else if (auto *ms = dyn_cast<MergeInputSection>(this))
+ sec = ms->getParent();
+ else if (auto *eh = dyn_cast<EhInputSection>(this))
+ sec = eh->getParent();
else
return cast<OutputSection>(this);
- return Sec ? Sec->getParent() : nullptr;
+ return sec ? sec->getParent() : nullptr;
}
-// When a section is compressed, `RawData` consists with a header followed
+// When a section is compressed, `rawData` consists with a header followed
// by zlib-compressed data. This function parses a header to initialize
-// `UncompressedSize` member and remove the header from `RawData`.
+// `uncompressedSize` member and remove the header from `rawData`.
void InputSectionBase::parseCompressedHeader() {
- typedef typename ELF64LE::Chdr Chdr64;
- typedef typename ELF32LE::Chdr Chdr32;
+ using Chdr64 = typename ELF64LE::Chdr;
+ using Chdr32 = typename ELF32LE::Chdr;
// Old-style header
- if (Name.startswith(".zdebug")) {
- if (!toStringRef(RawData).startswith("ZLIB")) {
+ if (name.startswith(".zdebug")) {
+ if (!toStringRef(rawData).startswith("ZLIB")) {
error(toString(this) + ": corrupted compressed section header");
return;
}
- RawData = RawData.slice(4);
+ rawData = rawData.slice(4);
- if (RawData.size() < 8) {
+ if (rawData.size() < 8) {
error(toString(this) + ": corrupted compressed section header");
return;
}
- UncompressedSize = read64be(RawData.data());
- RawData = RawData.slice(8);
+ uncompressedSize = read64be(rawData.data());
+ rawData = rawData.slice(8);
// Restore the original section name.
// (e.g. ".zdebug_info" -> ".debug_info")
- Name = Saver.save("." + Name.substr(2));
+ name = saver.save("." + name.substr(2));
return;
}
- assert(Flags & SHF_COMPRESSED);
- Flags &= ~(uint64_t)SHF_COMPRESSED;
+ assert(flags & SHF_COMPRESSED);
+ flags &= ~(uint64_t)SHF_COMPRESSED;
// New-style 64-bit header
- if (Config->Is64) {
- if (RawData.size() < sizeof(Chdr64)) {
+ if (config->is64) {
+ if (rawData.size() < sizeof(Chdr64)) {
error(toString(this) + ": corrupted compressed section");
return;
}
- auto *Hdr = reinterpret_cast<const Chdr64 *>(RawData.data());
- if (Hdr->ch_type != ELFCOMPRESS_ZLIB) {
+ auto *hdr = reinterpret_cast<const Chdr64 *>(rawData.data());
+ if (hdr->ch_type != ELFCOMPRESS_ZLIB) {
error(toString(this) + ": unsupported compression type");
return;
}
- UncompressedSize = Hdr->ch_size;
- RawData = RawData.slice(sizeof(*Hdr));
+ uncompressedSize = hdr->ch_size;
+ alignment = std::max<uint32_t>(hdr->ch_addralign, 1);
+ rawData = rawData.slice(sizeof(*hdr));
return;
}
// New-style 32-bit header
- if (RawData.size() < sizeof(Chdr32)) {
+ if (rawData.size() < sizeof(Chdr32)) {
error(toString(this) + ": corrupted compressed section");
return;
}
- auto *Hdr = reinterpret_cast<const Chdr32 *>(RawData.data());
- if (Hdr->ch_type != ELFCOMPRESS_ZLIB) {
+ auto *hdr = reinterpret_cast<const Chdr32 *>(rawData.data());
+ if (hdr->ch_type != ELFCOMPRESS_ZLIB) {
error(toString(this) + ": unsupported compression type");
return;
}
- UncompressedSize = Hdr->ch_size;
- RawData = RawData.slice(sizeof(*Hdr));
+ uncompressedSize = hdr->ch_size;
+ alignment = std::max<uint32_t>(hdr->ch_addralign, 1);
+ rawData = rawData.slice(sizeof(*hdr));
}
InputSection *InputSectionBase::getLinkOrderDep() const {
- assert(Link);
- assert(Flags & SHF_LINK_ORDER);
- return cast<InputSection>(File->getSections()[Link]);
+ assert(link);
+ assert(flags & SHF_LINK_ORDER);
+ return cast<InputSection>(file->getSections()[link]);
}
// Find a function symbol that encloses a given location.
template <class ELFT>
-Defined *InputSectionBase::getEnclosingFunction(uint64_t Offset) {
- for (Symbol *B : File->getSymbols())
- if (Defined *D = dyn_cast<Defined>(B))
- if (D->Section == this && D->Type == STT_FUNC && D->Value <= Offset &&
- Offset < D->Value + D->Size)
- return D;
+Defined *InputSectionBase::getEnclosingFunction(uint64_t offset) {
+ for (Symbol *b : file->getSymbols())
+ if (Defined *d = dyn_cast<Defined>(b))
+ if (d->section == this && d->type == STT_FUNC && d->value <= offset &&
+ offset < d->value + d->size)
+ return d;
return nullptr;
}
// Returns a source location string. Used to construct an error message.
template <class ELFT>
-std::string InputSectionBase::getLocation(uint64_t Offset) {
- std::string SecAndOffset = (Name + "+0x" + utohexstr(Offset)).str();
+std::string InputSectionBase::getLocation(uint64_t offset) {
+ std::string secAndOffset = (name + "+0x" + utohexstr(offset)).str();
// We don't have file for synthetic sections.
if (getFile<ELFT>() == nullptr)
- return (Config->OutputFile + ":(" + SecAndOffset + ")")
+ return (config->outputFile + ":(" + secAndOffset + ")")
.str();
// First check if we can get desired values from debugging information.
- if (Optional<DILineInfo> Info = getFile<ELFT>()->getDILineInfo(this, Offset))
- return Info->FileName + ":" + std::to_string(Info->Line) + ":(" +
- SecAndOffset + ")";
+ if (Optional<DILineInfo> info = getFile<ELFT>()->getDILineInfo(this, offset))
+ return info->FileName + ":" + std::to_string(info->Line) + ":(" +
+ secAndOffset + ")";
- // File->SourceFile contains STT_FILE symbol that contains a
+ // File->sourceFile contains STT_FILE symbol that contains a
// source file name. If it's missing, we use an object file name.
- std::string SrcFile = getFile<ELFT>()->SourceFile;
- if (SrcFile.empty())
- SrcFile = toString(File);
+ std::string srcFile = getFile<ELFT>()->sourceFile;
+ if (srcFile.empty())
+ srcFile = toString(file);
- if (Defined *D = getEnclosingFunction<ELFT>(Offset))
- return SrcFile + ":(function " + toString(*D) + ": " + SecAndOffset + ")";
+ if (Defined *d = getEnclosingFunction<ELFT>(offset))
+ return srcFile + ":(function " + toString(*d) + ": " + secAndOffset + ")";
// If there's no symbol, print out the offset in the section.
- return (SrcFile + ":(" + SecAndOffset + ")");
+ return (srcFile + ":(" + secAndOffset + ")");
}
// This function is intended to be used for constructing an error message.
@@ -319,8 +325,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
// foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42)
//
// Returns an empty string if there's no way to get line info.
-std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
- return File->getSrcMsg(Sym, *this, Offset);
+std::string InputSectionBase::getSrcMsg(const Symbol &sym, uint64_t offset) {
+ return file->getSrcMsg(sym, *this, offset);
}
// Returns a filename string along with an optional section name. This
@@ -332,95 +338,96 @@ std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
// or
//
// path/to/foo.o:(function bar) in archive path/to/bar.a
-std::string InputSectionBase::getObjMsg(uint64_t Off) {
- std::string Filename = File->getName();
+std::string InputSectionBase::getObjMsg(uint64_t off) {
+ std::string filename = file->getName();
- std::string Archive;
- if (!File->ArchiveName.empty())
- Archive = " in archive " + File->ArchiveName;
+ std::string archive;
+ if (!file->archiveName.empty())
+ archive = " in archive " + file->archiveName;
// Find a symbol that encloses a given location.
- for (Symbol *B : File->getSymbols())
- if (auto *D = dyn_cast<Defined>(B))
- if (D->Section == this && D->Value <= Off && Off < D->Value + D->Size)
- return Filename + ":(" + toString(*D) + ")" + Archive;
+ for (Symbol *b : file->getSymbols())
+ if (auto *d = dyn_cast<Defined>(b))
+ if (d->section == this && d->value <= off && off < d->value + d->size)
+ return filename + ":(" + toString(*d) + ")" + archive;
// If there's no symbol, print out the offset in the section.
- return (Filename + ":(" + Name + "+0x" + utohexstr(Off) + ")" + Archive)
+ return (filename + ":(" + name + "+0x" + utohexstr(off) + ")" + archive)
.str();
}
-InputSection InputSection::Discarded(nullptr, 0, 0, 0, ArrayRef<uint8_t>(), "");
+InputSection InputSection::discarded(nullptr, 0, 0, 0, ArrayRef<uint8_t>(), "");
-InputSection::InputSection(InputFile *F, uint64_t Flags, uint32_t Type,
- uint32_t Alignment, ArrayRef<uint8_t> Data,
- StringRef Name, Kind K)
- : InputSectionBase(F, Flags, Type,
- /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, Alignment, Data,
- Name, K) {}
+InputSection::InputSection(InputFile *f, uint64_t flags, uint32_t type,
+ uint32_t alignment, ArrayRef<uint8_t> data,
+ StringRef name, Kind k)
+ : InputSectionBase(f, flags, type,
+ /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, alignment, data,
+ name, k) {}
template <class ELFT>
-InputSection::InputSection(ObjFile<ELFT> &F, const typename ELFT::Shdr &Header,
- StringRef Name)
- : InputSectionBase(F, Header, Name, InputSectionBase::Regular) {}
+InputSection::InputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
+ StringRef name)
+ : InputSectionBase(f, header, name, InputSectionBase::Regular) {}
-bool InputSection::classof(const SectionBase *S) {
- return S->kind() == SectionBase::Regular ||
- S->kind() == SectionBase::Synthetic;
+bool InputSection::classof(const SectionBase *s) {
+ return s->kind() == SectionBase::Regular ||
+ s->kind() == SectionBase::Synthetic;
}
OutputSection *InputSection::getParent() const {
- return cast_or_null<OutputSection>(Parent);
+ return cast_or_null<OutputSection>(parent);
}
// Copy SHT_GROUP section contents. Used only for the -r option.
-template <class ELFT> void InputSection::copyShtGroup(uint8_t *Buf) {
+template <class ELFT> void InputSection::copyShtGroup(uint8_t *buf) {
// ELFT::Word is the 32-bit integral type in the target endianness.
- typedef typename ELFT::Word u32;
- ArrayRef<u32> From = getDataAs<u32>();
- auto *To = reinterpret_cast<u32 *>(Buf);
+ using u32 = typename ELFT::Word;
+ ArrayRef<u32> from = getDataAs<u32>();
+ auto *to = reinterpret_cast<u32 *>(buf);
// The first entry is not a section number but a flag.
- *To++ = From[0];
+ *to++ = from[0];
// Adjust section numbers because section numbers in an input object
// files are different in the output.
- ArrayRef<InputSectionBase *> Sections = File->getSections();
- for (uint32_t Idx : From.slice(1))
- *To++ = Sections[Idx]->getOutputSection()->SectionIndex;
+ ArrayRef<InputSectionBase *> sections = file->getSections();
+ for (uint32_t idx : from.slice(1))
+ *to++ = sections[idx]->getOutputSection()->sectionIndex;
}
InputSectionBase *InputSection::getRelocatedSection() const {
- if (!File || (Type != SHT_RELA && Type != SHT_REL))
+ if (!file || (type != SHT_RELA && type != SHT_REL))
return nullptr;
- ArrayRef<InputSectionBase *> Sections = File->getSections();
- return Sections[Info];
+ ArrayRef<InputSectionBase *> sections = file->getSections();
+ return sections[info];
}
// This is used for -r and --emit-relocs. We can't use memcpy to copy
// relocations because we need to update symbol table offset and section index
// for each relocation. So we copy relocations one by one.
template <class ELFT, class RelTy>
-void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
- InputSectionBase *Sec = getRelocatedSection();
+void InputSection::copyRelocations(uint8_t *buf, ArrayRef<RelTy> rels) {
+ InputSectionBase *sec = getRelocatedSection();
- for (const RelTy &Rel : Rels) {
- RelType Type = Rel.getType(Config->IsMips64EL);
- Symbol &Sym = getFile<ELFT>()->getRelocTargetSym(Rel);
+ for (const RelTy &rel : rels) {
+ RelType type = rel.getType(config->isMips64EL);
+ const ObjFile<ELFT> *file = getFile<ELFT>();
+ Symbol &sym = file->getRelocTargetSym(rel);
- auto *P = reinterpret_cast<typename ELFT::Rela *>(Buf);
- Buf += sizeof(RelTy);
+ auto *p = reinterpret_cast<typename ELFT::Rela *>(buf);
+ buf += sizeof(RelTy);
if (RelTy::IsRela)
- P->r_addend = getAddend<ELFT>(Rel);
+ p->r_addend = getAddend<ELFT>(rel);
// Output section VA is zero for -r, so r_offset is an offset within the
// section, but for --emit-relocs it is an virtual address.
- P->r_offset = Sec->getVA(Rel.r_offset);
- P->setSymbolAndType(In.SymTab->getSymbolIndex(&Sym), Type,
- Config->IsMips64EL);
+ p->r_offset = sec->getVA(rel.r_offset);
+ p->setSymbolAndType(in.symTab->getSymbolIndex(&sym), type,
+ config->isMips64EL);
- if (Sym.Type == STT_SECTION) {
+ if (sym.type == STT_SECTION) {
// We combine multiple section symbols into only one per
// section. This means we have to update the addend. That is
// trivial for Elf_Rela, but for Elf_Rel we have to write to the
@@ -429,25 +436,38 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// .eh_frame is horribly special and can reference discarded sections. To
// avoid having to parse and recreate .eh_frame, we just replace any
// relocation in it pointing to discarded sections with R_*_NONE, which
- // hopefully creates a frame that is ignored at runtime.
- auto *D = dyn_cast<Defined>(&Sym);
- if (!D) {
- error("STT_SECTION symbol should be defined");
+ // hopefully creates a frame that is ignored at runtime. Also, don't warn
+ // on .gcc_except_table and debug sections.
+ //
+ // See the comment in maybeReportUndefined for PPC64 .toc .
+ auto *d = dyn_cast<Defined>(&sym);
+ if (!d) {
+ if (!sec->name.startswith(".debug") &&
+ !sec->name.startswith(".zdebug") && sec->name != ".eh_frame" &&
+ sec->name != ".gcc_except_table" && sec->name != ".toc") {
+ uint32_t secIdx = cast<Undefined>(sym).discardedSecIdx;
+ Elf_Shdr_Impl<ELFT> sec =
+ CHECK(file->getObj().sections(), file)[secIdx];
+ warn("relocation refers to a discarded section: " +
+ CHECK(file->getObj().getSectionName(&sec), file) +
+ "\n>>> referenced by " + getObjMsg(p->r_offset));
+ }
+ p->setSymbolAndType(0, 0, false);
continue;
}
- SectionBase *Section = D->Section->Repl;
- if (!Section->Live) {
- P->setSymbolAndType(0, 0, false);
+ SectionBase *section = d->section->repl;
+ if (!section->isLive()) {
+ p->setSymbolAndType(0, 0, false);
continue;
}
- int64_t Addend = getAddend<ELFT>(Rel);
- const uint8_t *BufLoc = Sec->data().begin() + Rel.r_offset;
+ int64_t addend = getAddend<ELFT>(rel);
+ const uint8_t *bufLoc = sec->data().begin() + rel.r_offset;
if (!RelTy::IsRela)
- Addend = Target->getImplicitAddend(BufLoc, Type);
+ addend = target->getImplicitAddend(bufLoc, type);
- if (Config->EMachine == EM_MIPS && Config->Relocatable &&
- Target->getRelExpr(Type, Sym, BufLoc) == R_MIPS_GOTREL) {
+ if (config->emachine == EM_MIPS && config->relocatable &&
+ target->getRelExpr(type, sym, bufLoc) == R_MIPS_GOTREL) {
// Some MIPS relocations depend on "gp" value. By default,
// this value has 0x7ff0 offset from a .got section. But
// relocatable files produced by a complier or a linker
@@ -459,13 +479,13 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// individual "gp" values used by each input object file.
// As a workaround we add the "gp" value to the relocation
// addend and save it back to the file.
- Addend += Sec->getFile<ELFT>()->MipsGp0;
+ addend += sec->getFile<ELFT>()->mipsGp0;
}
if (RelTy::IsRela)
- P->r_addend = Sym.getVA(Addend) - Section->getOutputSection()->Addr;
- else if (Config->Relocatable)
- Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset, Addend, &Sym});
+ p->r_addend = sym.getVA(addend) - section->getOutputSection()->addr;
+ else if (config->relocatable && type != target->noneRel)
+ sec->relocations.push_back({R_ABS, type, rel.r_offset, addend, &sym});
}
}
}
@@ -475,13 +495,13 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// this context is the address of the place P. A further special case is that
// branch relocations to an undefined weak reference resolve to the next
// instruction.
-static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
- uint32_t P) {
- switch (Type) {
+static uint32_t getARMUndefinedRelativeWeakVA(RelType type, uint32_t a,
+ uint32_t p) {
+ switch (type) {
// Unresolved branch relocations to weak references resolve to next
// instruction, this will be either 2 or 4 bytes on from P.
case R_ARM_THM_JUMP11:
- return P + 2 + A;
+ return p + 2 + a;
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_PC24:
@@ -489,10 +509,10 @@ static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
case R_ARM_PREL31:
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
- return P + 4 + A;
+ return p + 4 + a;
case R_ARM_THM_CALL:
// We don't want an interworking BLX to ARM
- return P + 5 + A;
+ return p + 5 + a;
// Unresolved non branch pc-relative relocations
// R_ARM_TARGET2 which can be resolved relatively is not present as it never
// targets a weak-reference.
@@ -501,29 +521,29 @@ static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
case R_ARM_REL32:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
- return P + A;
+ return p + a;
}
llvm_unreachable("ARM pc-relative relocation expected\n");
}
// The comment above getARMUndefinedRelativeWeakVA applies to this function.
-static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
- uint64_t P) {
- switch (Type) {
+static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t type, uint64_t a,
+ uint64_t p) {
+ switch (type) {
// Unresolved branch relocations to weak references resolve to next
// instruction, this is 4 bytes on from P.
case R_AARCH64_CALL26:
case R_AARCH64_CONDBR19:
case R_AARCH64_JUMP26:
case R_AARCH64_TSTBR14:
- return P + 4 + A;
+ return p + 4 + a;
// Unresolved non branch pc-relative relocations
case R_AARCH64_PREL16:
case R_AARCH64_PREL32:
case R_AARCH64_PREL64:
case R_AARCH64_ADR_PREL_LO21:
case R_AARCH64_LD_PREL_LO19:
- return P + A;
+ return p + a;
}
llvm_unreachable("AArch64 pc-relative relocation expected\n");
}
@@ -535,11 +555,11 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
// The procedure call standard only defines a Read Write Position Independent
// RWPI variant so in practice we should expect the static base to be the base
// of the RW segment.
-static uint64_t getARMStaticBase(const Symbol &Sym) {
- OutputSection *OS = Sym.getOutputSection();
- if (!OS || !OS->PtLoad || !OS->PtLoad->FirstSec)
- fatal("SBREL relocation to " + Sym.getName() + " without static base");
- return OS->PtLoad->FirstSec->Addr;
+static uint64_t getARMStaticBase(const Symbol &sym) {
+ OutputSection *os = sym.getOutputSection();
+ if (!os || !os->ptLoad || !os->ptLoad->firstSec)
+ fatal("SBREL relocation to " + sym.getName() + " without static base");
+ return os->ptLoad->firstSec->addr;
}
// For R_RISCV_PC_INDIRECT (R_RISCV_PCREL_LO12_{I,S}), the symbol actually
@@ -548,101 +568,115 @@ static uint64_t getARMStaticBase(const Symbol &Sym) {
//
// This function returns the R_RISCV_PCREL_HI20 relocation from
// R_RISCV_PCREL_LO12's symbol and addend.
-static Relocation *getRISCVPCRelHi20(const Symbol *Sym, uint64_t Addend) {
- const Defined *D = cast<Defined>(Sym);
- InputSection *IS = cast<InputSection>(D->Section);
+static Relocation *getRISCVPCRelHi20(const Symbol *sym, uint64_t addend) {
+ const Defined *d = cast<Defined>(sym);
+ if (!d->section) {
+ error("R_RISCV_PCREL_LO12 relocation points to an absolute symbol: " +
+ sym->getName());
+ return nullptr;
+ }
+ InputSection *isec = cast<InputSection>(d->section);
- if (Addend != 0)
+ if (addend != 0)
warn("Non-zero addend in R_RISCV_PCREL_LO12 relocation to " +
- IS->getObjMsg(D->Value) + " is ignored");
+ isec->getObjMsg(d->value) + " is ignored");
// Relocations are sorted by offset, so we can use std::equal_range to do
// binary search.
- auto Range = std::equal_range(IS->Relocations.begin(), IS->Relocations.end(),
- D->Value, RelocationOffsetComparator{});
- for (auto It = std::get<0>(Range); It != std::get<1>(Range); ++It)
- if (isRelExprOneOf<R_PC>(It->Expr))
- return &*It;
-
- error("R_RISCV_PCREL_LO12 relocation points to " + IS->getObjMsg(D->Value) +
+ Relocation r;
+ r.offset = d->value;
+ auto range =
+ std::equal_range(isec->relocations.begin(), isec->relocations.end(), r,
+ [](const Relocation &lhs, const Relocation &rhs) {
+ return lhs.offset < rhs.offset;
+ });
+
+ for (auto it = range.first; it != range.second; ++it)
+ if (it->type == R_RISCV_PCREL_HI20 || it->type == R_RISCV_GOT_HI20 ||
+ it->type == R_RISCV_TLS_GD_HI20 || it->type == R_RISCV_TLS_GOT_HI20)
+ return &*it;
+
+ error("R_RISCV_PCREL_LO12 relocation points to " + isec->getObjMsg(d->value) +
" without an associated R_RISCV_PCREL_HI20 relocation");
return nullptr;
}
// A TLS symbol's virtual address is relative to the TLS segment. Add a
// target-specific adjustment to produce a thread-pointer-relative offset.
-static int64_t getTlsTpOffset() {
- switch (Config->EMachine) {
+static int64_t getTlsTpOffset(const Symbol &s) {
+ // On targets that support TLSDESC, _TLS_MODULE_BASE_@tpoff = 0.
+ if (&s == ElfSym::tlsModuleBase)
+ return 0;
+
+ switch (config->emachine) {
case EM_ARM:
case EM_AARCH64:
// Variant 1. The thread pointer points to a TCB with a fixed 2-word size,
// followed by a variable amount of alignment padding, followed by the TLS
// segment.
- //
- // NB: While the ARM/AArch64 ABI formally has a 2-word TCB size, lld
- // effectively increases the TCB size to 8 words for Android compatibility.
- // It accomplishes this by increasing the segment's alignment.
- return alignTo(Config->Wordsize * 2, Out::TlsPhdr->p_align);
+ return s.getVA(0) + alignTo(config->wordsize * 2, Out::tlsPhdr->p_align);
case EM_386:
case EM_X86_64:
// Variant 2. The TLS segment is located just before the thread pointer.
- return -Out::TlsPhdr->p_memsz;
+ return s.getVA(0) - alignTo(Out::tlsPhdr->p_memsz, Out::tlsPhdr->p_align);
+ case EM_PPC:
case EM_PPC64:
// The thread pointer points to a fixed offset from the start of the
// executable's TLS segment. An offset of 0x7000 allows a signed 16-bit
// offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the
// program's TLS segment.
- return -0x7000;
+ return s.getVA(0) - 0x7000;
+ case EM_RISCV:
+ return s.getVA(0);
default:
llvm_unreachable("unhandled Config->EMachine");
}
}
-static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
- uint64_t P, const Symbol &Sym, RelExpr Expr) {
- switch (Expr) {
- case R_INVALID:
- return 0;
+static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a,
+ uint64_t p, const Symbol &sym, RelExpr expr) {
+ switch (expr) {
case R_ABS:
+ case R_DTPREL:
case R_RELAX_TLS_LD_TO_LE_ABS:
case R_RELAX_GOT_PC_NOPIC:
- return Sym.getVA(A);
+ case R_RISCV_ADD:
+ return sym.getVA(a);
case R_ADDEND:
- return A;
+ return a;
case R_ARM_SBREL:
- return Sym.getVA(A) - getARMStaticBase(Sym);
+ return sym.getVA(a) - getARMStaticBase(sym);
case R_GOT:
- case R_GOT_PLT:
case R_RELAX_TLS_GD_TO_IE_ABS:
- return Sym.getGotVA() + A;
+ return sym.getGotVA() + a;
case R_GOTONLY_PC:
- return In.Got->getVA() + A - P;
- case R_GOTONLY_PC_FROM_END:
- return In.Got->getVA() + A - P + In.Got->getSize();
+ return in.got->getVA() + a - p;
+ case R_GOTPLTONLY_PC:
+ return in.gotPlt->getVA() + a - p;
case R_GOTREL:
- return Sym.getVA(A) - In.Got->getVA();
- case R_GOTREL_FROM_END:
- return Sym.getVA(A) - In.Got->getVA() - In.Got->getSize();
- case R_GOT_FROM_END:
- case R_RELAX_TLS_GD_TO_IE_END:
- return Sym.getGotOffset() + A - In.Got->getSize();
+ case R_PPC64_RELAX_TOC:
+ return sym.getVA(a) - in.got->getVA();
+ case R_GOTPLTREL:
+ return sym.getVA(a) - in.gotPlt->getVA();
+ case R_GOTPLT:
+ case R_RELAX_TLS_GD_TO_IE_GOTPLT:
+ return sym.getGotVA() + a - in.gotPlt->getVA();
case R_TLSLD_GOT_OFF:
case R_GOT_OFF:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
- return Sym.getGotOffset() + A;
+ return sym.getGotOffset() + a;
case R_AARCH64_GOT_PAGE_PC:
- case R_AARCH64_GOT_PAGE_PC_PLT:
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
- return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P);
+ return getAArch64Page(sym.getGotVA() + a) - getAArch64Page(p);
case R_GOT_PC:
case R_RELAX_TLS_GD_TO_IE:
- return Sym.getGotVA() + A - P;
+ return sym.getGotVA() + a - p;
case R_HEXAGON_GOT:
- return Sym.getGotVA() - In.GotPlt->getVA();
+ return sym.getGotVA() - in.gotPlt->getVA();
case R_MIPS_GOTREL:
- return Sym.getVA(A) - In.MipsGot->getGp(File);
+ return sym.getVA(a) - in.mipsGot->getGp(file);
case R_MIPS_GOT_GP:
- return In.MipsGot->getGp(File) + A;
+ return in.mipsGot->getGp(file) + a;
case R_MIPS_GOT_GP_PC: {
// R_MIPS_LO16 expression has R_MIPS_GOT_GP_PC type iif the target
// is _gp_disp symbol. In that case we should use the following
@@ -651,73 +685,76 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// microMIPS variants of these relocations use slightly different
// expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi()
// to correctly handle less-sugnificant bit of the microMIPS symbol.
- uint64_t V = In.MipsGot->getGp(File) + A - P;
- if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16)
- V += 4;
- if (Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_HI16)
- V -= 1;
- return V;
+ uint64_t v = in.mipsGot->getGp(file) + a - p;
+ if (type == R_MIPS_LO16 || type == R_MICROMIPS_LO16)
+ v += 4;
+ if (type == R_MICROMIPS_LO16 || type == R_MICROMIPS_HI16)
+ v -= 1;
+ return v;
}
case R_MIPS_GOT_LOCAL_PAGE:
// If relocation against MIPS local symbol requires GOT entry, this entry
// should be initialized by 'page address'. This address is high 16-bits
// of sum the symbol's value and the addend.
- return In.MipsGot->getVA() + In.MipsGot->getPageEntryOffset(File, Sym, A) -
- In.MipsGot->getGp(File);
+ return in.mipsGot->getVA() + in.mipsGot->getPageEntryOffset(file, sym, a) -
+ in.mipsGot->getGp(file);
case R_MIPS_GOT_OFF:
case R_MIPS_GOT_OFF32:
// In case of MIPS if a GOT relocation has non-zero addend this addend
// should be applied to the GOT entry content not to the GOT entry offset.
// That is why we use separate expression type.
- return In.MipsGot->getVA() + In.MipsGot->getSymEntryOffset(File, Sym, A) -
- In.MipsGot->getGp(File);
+ return in.mipsGot->getVA() + in.mipsGot->getSymEntryOffset(file, sym, a) -
+ in.mipsGot->getGp(file);
case R_MIPS_TLSGD:
- return In.MipsGot->getVA() + In.MipsGot->getGlobalDynOffset(File, Sym) -
- In.MipsGot->getGp(File);
+ return in.mipsGot->getVA() + in.mipsGot->getGlobalDynOffset(file, sym) -
+ in.mipsGot->getGp(file);
case R_MIPS_TLSLD:
- return In.MipsGot->getVA() + In.MipsGot->getTlsIndexOffset(File) -
- In.MipsGot->getGp(File);
+ return in.mipsGot->getVA() + in.mipsGot->getTlsIndexOffset(file) -
+ in.mipsGot->getGp(file);
case R_AARCH64_PAGE_PC: {
- uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getVA(A);
- return getAArch64Page(Val) - getAArch64Page(P);
- }
- case R_AARCH64_PLT_PAGE_PC: {
- uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getPltVA() + A;
- return getAArch64Page(Val) - getAArch64Page(P);
+ uint64_t val = sym.isUndefWeak() ? p + a : sym.getVA(a);
+ return getAArch64Page(val) - getAArch64Page(p);
}
case R_RISCV_PC_INDIRECT: {
- if (const Relocation *HiRel = getRISCVPCRelHi20(&Sym, A))
- return getRelocTargetVA(File, HiRel->Type, HiRel->Addend, Sym.getVA(),
- *HiRel->Sym, HiRel->Expr);
+ if (const Relocation *hiRel = getRISCVPCRelHi20(&sym, a))
+ return getRelocTargetVA(file, hiRel->type, hiRel->addend, sym.getVA(),
+ *hiRel->sym, hiRel->expr);
return 0;
}
case R_PC: {
- uint64_t Dest;
- if (Sym.isUndefWeak()) {
+ uint64_t dest;
+ if (sym.isUndefWeak()) {
// On ARM and AArch64 a branch to an undefined weak resolves to the
// next instruction, otherwise the place.
- if (Config->EMachine == EM_ARM)
- Dest = getARMUndefinedRelativeWeakVA(Type, A, P);
- else if (Config->EMachine == EM_AARCH64)
- Dest = getAArch64UndefinedRelativeWeakVA(Type, A, P);
+ if (config->emachine == EM_ARM)
+ dest = getARMUndefinedRelativeWeakVA(type, a, p);
+ else if (config->emachine == EM_AARCH64)
+ dest = getAArch64UndefinedRelativeWeakVA(type, a, p);
+ else if (config->emachine == EM_PPC)
+ dest = p;
else
- Dest = Sym.getVA(A);
+ dest = sym.getVA(a);
} else {
- Dest = Sym.getVA(A);
+ dest = sym.getVA(a);
}
- return Dest - P;
+ return dest - p;
}
case R_PLT:
- return Sym.getPltVA() + A;
+ return sym.getPltVA() + a;
case R_PLT_PC:
- case R_PPC_CALL_PLT:
- return Sym.getPltVA() + A - P;
- case R_PPC_CALL: {
- uint64_t SymVA = Sym.getVA(A);
+ case R_PPC64_CALL_PLT:
+ return sym.getPltVA() + a - p;
+ case R_PPC32_PLTREL:
+ // R_PPC_PLTREL24 uses the addend (usually 0 or 0x8000) to indicate r30
+ // stores _GLOBAL_OFFSET_TABLE_ or .got2+0x8000. The addend is ignored for
+ // target VA compuation.
+ return sym.getPltVA() - p;
+ case R_PPC64_CALL: {
+ uint64_t symVA = sym.getVA(a);
// If we have an undefined weak symbol, we might get here with a symbol
// address of zero. That could overflow, but the code must be unreachable,
// so don't bother doing anything at all.
- if (!SymVA)
+ if (!symVA)
return 0;
// PPC64 V2 ABI describes two entry points to a function. The global entry
@@ -726,46 +763,49 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// the callee. For local calls the caller and callee share the same
// TOC base and so the TOC pointer initialization code should be skipped by
// branching to the local entry point.
- return SymVA - P + getPPC64GlobalEntryToLocalEntryOffset(Sym.StOther);
+ return symVA - p + getPPC64GlobalEntryToLocalEntryOffset(sym.stOther);
}
- case R_PPC_TOC:
- return getPPC64TocBase() + A;
+ case R_PPC64_TOCBASE:
+ return getPPC64TocBase() + a;
case R_RELAX_GOT_PC:
- return Sym.getVA(A) - P;
+ return sym.getVA(a) - p;
case R_RELAX_TLS_GD_TO_LE:
case R_RELAX_TLS_IE_TO_LE:
case R_RELAX_TLS_LD_TO_LE:
case R_TLS:
- // A weak undefined TLS symbol resolves to the base of the TLS
- // block, i.e. gets a value of zero. If we pass --gc-sections to
- // lld and .tbss is not referenced, it gets reclaimed and we don't
- // create a TLS program header. Therefore, we resolve this
- // statically to zero.
- if (Sym.isTls() && Sym.isUndefWeak())
- return 0;
- return Sym.getVA(A) + getTlsTpOffset();
+ // It is not very clear what to return if the symbol is undefined. With
+ // --noinhibit-exec, even a non-weak undefined reference may reach here.
+ // Just return A, which matches R_ABS, and the behavior of some dynamic
+ // loaders.
+ if (sym.isUndefined())
+ return a;
+ return getTlsTpOffset(sym) + a;
case R_RELAX_TLS_GD_TO_LE_NEG:
case R_NEG_TLS:
- return Out::TlsPhdr->p_memsz - Sym.getVA(A);
+ if (sym.isUndefined())
+ return a;
+ return -getTlsTpOffset(sym) + a;
case R_SIZE:
- return Sym.getSize() + A;
+ return sym.getSize() + a;
case R_TLSDESC:
- return In.Got->getGlobalDynAddr(Sym) + A;
+ return in.got->getGlobalDynAddr(sym) + a;
+ case R_TLSDESC_PC:
+ return in.got->getGlobalDynAddr(sym) + a - p;
case R_AARCH64_TLSDESC_PAGE:
- return getAArch64Page(In.Got->getGlobalDynAddr(Sym) + A) -
- getAArch64Page(P);
+ return getAArch64Page(in.got->getGlobalDynAddr(sym) + a) -
+ getAArch64Page(p);
case R_TLSGD_GOT:
- return In.Got->getGlobalDynOffset(Sym) + A;
- case R_TLSGD_GOT_FROM_END:
- return In.Got->getGlobalDynOffset(Sym) + A - In.Got->getSize();
+ return in.got->getGlobalDynOffset(sym) + a;
+ case R_TLSGD_GOTPLT:
+ return in.got->getVA() + in.got->getGlobalDynOffset(sym) + a - in.gotPlt->getVA();
case R_TLSGD_PC:
- return In.Got->getGlobalDynAddr(Sym) + A - P;
- case R_TLSLD_GOT_FROM_END:
- return In.Got->getTlsIndexOff() + A - In.Got->getSize();
+ return in.got->getGlobalDynAddr(sym) + a - p;
+ case R_TLSLD_GOTPLT:
+ return in.got->getVA() + in.got->getTlsIndexOff() + a - in.gotPlt->getVA();
case R_TLSLD_GOT:
- return In.Got->getTlsIndexOff() + A;
+ return in.got->getTlsIndexOff() + a;
case R_TLSLD_PC:
- return In.Got->getTlsIndexVA() + A - P;
+ return in.got->getTlsIndexVA() + a - p;
default:
llvm_unreachable("invalid expression");
}
@@ -779,36 +819,36 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// So, we handle relocations for non-alloc sections directly in this
// function as a performance optimization.
template <class ELFT, class RelTy>
-void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
- const unsigned Bits = sizeof(typename ELFT::uint) * 8;
+void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
+ const unsigned bits = sizeof(typename ELFT::uint) * 8;
- for (const RelTy &Rel : Rels) {
- RelType Type = Rel.getType(Config->IsMips64EL);
+ for (const RelTy &rel : rels) {
+ RelType type = rel.getType(config->isMips64EL);
// GCC 8.0 or earlier have a bug that they emit R_386_GOTPC relocations
// against _GLOBAL_OFFSET_TABLE_ for .debug_info. The bug has been fixed
// in 2017 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630), but we
// need to keep this bug-compatible code for a while.
- if (Config->EMachine == EM_386 && Type == R_386_GOTPC)
+ if (config->emachine == EM_386 && type == R_386_GOTPC)
continue;
- uint64_t Offset = getOffset(Rel.r_offset);
- uint8_t *BufLoc = Buf + Offset;
- int64_t Addend = getAddend<ELFT>(Rel);
+ uint64_t offset = getOffset(rel.r_offset);
+ uint8_t *bufLoc = buf + offset;
+ int64_t addend = getAddend<ELFT>(rel);
if (!RelTy::IsRela)
- Addend += Target->getImplicitAddend(BufLoc, Type);
+ addend += target->getImplicitAddend(bufLoc, type);
- Symbol &Sym = getFile<ELFT>()->getRelocTargetSym(Rel);
- RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc);
- if (Expr == R_NONE)
+ Symbol &sym = getFile<ELFT>()->getRelocTargetSym(rel);
+ RelExpr expr = target->getRelExpr(type, sym, bufLoc);
+ if (expr == R_NONE)
continue;
- if (Expr != R_ABS) {
- std::string Msg = getLocation<ELFT>(Offset) +
- ": has non-ABS relocation " + toString(Type) +
- " against symbol '" + toString(Sym) + "'";
- if (Expr != R_PC) {
- error(Msg);
+ if (expr != R_ABS && expr != R_DTPREL && expr != R_RISCV_ADD) {
+ std::string msg = getLocation<ELFT>(offset) +
+ ": has non-ABS relocation " + toString(type) +
+ " against symbol '" + toString(sym) + "'";
+ if (expr != R_PC) {
+ error(msg);
return;
}
@@ -819,16 +859,16 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// relocations without any errors and relocate them as if they were at
// address 0. For bug-compatibilty, we accept them with warnings. We
// know Steel Bank Common Lisp as of 2018 have this bug.
- warn(Msg);
- Target->relocateOne(BufLoc, Type,
- SignExtend64<Bits>(Sym.getVA(Addend - Offset)));
+ warn(msg);
+ target->relocateOne(bufLoc, type,
+ SignExtend64<bits>(sym.getVA(addend - offset)));
continue;
}
- if (Sym.isTls() && !Out::TlsPhdr)
- Target->relocateOne(BufLoc, Type, 0);
+ if (sym.isTls() && !Out::tlsPhdr)
+ target->relocateOne(bufLoc, type, 0);
else
- Target->relocateOne(BufLoc, Type, SignExtend64<Bits>(Sym.getVA(Addend)));
+ target->relocateOne(bufLoc, type, SignExtend64<bits>(sym.getVA(addend)));
}
}
@@ -837,96 +877,100 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// relocations aimed to update addends. They are handled in relocateAlloc()
// for allocatable sections, and this function does the same for
// non-allocatable sections, such as sections with debug information.
-static void relocateNonAllocForRelocatable(InputSection *Sec, uint8_t *Buf) {
- const unsigned Bits = Config->Is64 ? 64 : 32;
+static void relocateNonAllocForRelocatable(InputSection *sec, uint8_t *buf) {
+ const unsigned bits = config->is64 ? 64 : 32;
- for (const Relocation &Rel : Sec->Relocations) {
+ for (const Relocation &rel : sec->relocations) {
// InputSection::copyRelocations() adds only R_ABS relocations.
- assert(Rel.Expr == R_ABS);
- uint8_t *BufLoc = Buf + Rel.Offset + Sec->OutSecOff;
- uint64_t TargetVA = SignExtend64(Rel.Sym->getVA(Rel.Addend), Bits);
- Target->relocateOne(BufLoc, Rel.Type, TargetVA);
+ assert(rel.expr == R_ABS);
+ uint8_t *bufLoc = buf + rel.offset + sec->outSecOff;
+ uint64_t targetVA = SignExtend64(rel.sym->getVA(rel.addend), bits);
+ target->relocateOne(bufLoc, rel.type, targetVA);
}
}
template <class ELFT>
-void InputSectionBase::relocate(uint8_t *Buf, uint8_t *BufEnd) {
- if (Flags & SHF_EXECINSTR)
- adjustSplitStackFunctionPrologues<ELFT>(Buf, BufEnd);
+void InputSectionBase::relocate(uint8_t *buf, uint8_t *bufEnd) {
+ if (flags & SHF_EXECINSTR)
+ adjustSplitStackFunctionPrologues<ELFT>(buf, bufEnd);
- if (Flags & SHF_ALLOC) {
- relocateAlloc(Buf, BufEnd);
+ if (flags & SHF_ALLOC) {
+ relocateAlloc(buf, bufEnd);
return;
}
- auto *Sec = cast<InputSection>(this);
- if (Config->Relocatable)
- relocateNonAllocForRelocatable(Sec, Buf);
- else if (Sec->AreRelocsRela)
- Sec->relocateNonAlloc<ELFT>(Buf, Sec->template relas<ELFT>());
+ auto *sec = cast<InputSection>(this);
+ if (config->relocatable)
+ relocateNonAllocForRelocatable(sec, buf);
+ else if (sec->areRelocsRela)
+ sec->relocateNonAlloc<ELFT>(buf, sec->template relas<ELFT>());
else
- Sec->relocateNonAlloc<ELFT>(Buf, Sec->template rels<ELFT>());
+ sec->relocateNonAlloc<ELFT>(buf, sec->template rels<ELFT>());
}
-void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
- assert(Flags & SHF_ALLOC);
- const unsigned Bits = Config->Wordsize * 8;
+void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) {
+ assert(flags & SHF_ALLOC);
+ const unsigned bits = config->wordsize * 8;
- for (const Relocation &Rel : Relocations) {
- uint64_t Offset = Rel.Offset;
- if (auto *Sec = dyn_cast<InputSection>(this))
- Offset += Sec->OutSecOff;
- uint8_t *BufLoc = Buf + Offset;
- RelType Type = Rel.Type;
+ for (const Relocation &rel : relocations) {
+ uint64_t offset = rel.offset;
+ if (auto *sec = dyn_cast<InputSection>(this))
+ offset += sec->outSecOff;
+ uint8_t *bufLoc = buf + offset;
+ RelType type = rel.type;
- uint64_t AddrLoc = getOutputSection()->Addr + Offset;
- RelExpr Expr = Rel.Expr;
- uint64_t TargetVA = SignExtend64(
- getRelocTargetVA(File, Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr),
- Bits);
+ uint64_t addrLoc = getOutputSection()->addr + offset;
+ RelExpr expr = rel.expr;
+ uint64_t targetVA = SignExtend64(
+ getRelocTargetVA(file, type, rel.addend, addrLoc, *rel.sym, expr),
+ bits);
- switch (Expr) {
+ switch (expr) {
case R_RELAX_GOT_PC:
case R_RELAX_GOT_PC_NOPIC:
- Target->relaxGot(BufLoc, TargetVA);
+ target->relaxGot(bufLoc, type, targetVA);
+ break;
+ case R_PPC64_RELAX_TOC:
+ if (!tryRelaxPPC64TocIndirection(type, rel, bufLoc))
+ target->relocateOne(bufLoc, type, targetVA);
break;
case R_RELAX_TLS_IE_TO_LE:
- Target->relaxTlsIeToLe(BufLoc, Type, TargetVA);
+ target->relaxTlsIeToLe(bufLoc, type, targetVA);
break;
case R_RELAX_TLS_LD_TO_LE:
case R_RELAX_TLS_LD_TO_LE_ABS:
- Target->relaxTlsLdToLe(BufLoc, Type, TargetVA);
+ target->relaxTlsLdToLe(bufLoc, type, targetVA);
break;
case R_RELAX_TLS_GD_TO_LE:
case R_RELAX_TLS_GD_TO_LE_NEG:
- Target->relaxTlsGdToLe(BufLoc, Type, TargetVA);
+ target->relaxTlsGdToLe(bufLoc, type, targetVA);
break;
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE:
case R_RELAX_TLS_GD_TO_IE_ABS:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
- case R_RELAX_TLS_GD_TO_IE_END:
- Target->relaxTlsGdToIe(BufLoc, Type, TargetVA);
+ case R_RELAX_TLS_GD_TO_IE_GOTPLT:
+ target->relaxTlsGdToIe(bufLoc, type, targetVA);
break;
- case R_PPC_CALL:
+ case R_PPC64_CALL:
// If this is a call to __tls_get_addr, it may be part of a TLS
// sequence that has been relaxed and turned into a nop. In this
// case, we don't want to handle it as a call.
- if (read32(BufLoc) == 0x60000000) // nop
+ if (read32(bufLoc) == 0x60000000) // nop
break;
// Patch a nop (0x60000000) to a ld.
- if (Rel.Sym->NeedsTocRestore) {
- if (BufLoc + 8 > BufEnd || read32(BufLoc + 4) != 0x60000000) {
- error(getErrorLocation(BufLoc) + "call lacks nop, can't restore toc");
+ if (rel.sym->needsTocRestore) {
+ if (bufLoc + 8 > bufEnd || read32(bufLoc + 4) != 0x60000000) {
+ error(getErrorLocation(bufLoc) + "call lacks nop, can't restore toc");
break;
}
- write32(BufLoc + 4, 0xe8410018); // ld %r2, 24(%r1)
+ write32(bufLoc + 4, 0xe8410018); // ld %r2, 24(%r1)
}
- Target->relocateOne(BufLoc, Type, TargetVA);
+ target->relocateOne(bufLoc, type, targetVA);
break;
default:
- Target->relocateOne(BufLoc, Type, TargetVA);
+ target->relocateOne(bufLoc, type, targetVA);
break;
}
}
@@ -935,44 +979,44 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
// For each function-defining prologue, find any calls to __morestack,
// and replace them with calls to __morestack_non_split.
static void switchMorestackCallsToMorestackNonSplit(
- DenseSet<Defined *> &Prologues, std::vector<Relocation *> &MorestackCalls) {
+ DenseSet<Defined *> &prologues, std::vector<Relocation *> &morestackCalls) {
// If the target adjusted a function's prologue, all calls to
// __morestack inside that function should be switched to
// __morestack_non_split.
- Symbol *MoreStackNonSplit = Symtab->find("__morestack_non_split");
- if (!MoreStackNonSplit) {
+ Symbol *moreStackNonSplit = symtab->find("__morestack_non_split");
+ if (!moreStackNonSplit) {
error("Mixing split-stack objects requires a definition of "
"__morestack_non_split");
return;
}
// Sort both collections to compare addresses efficiently.
- llvm::sort(MorestackCalls, [](const Relocation *L, const Relocation *R) {
- return L->Offset < R->Offset;
+ llvm::sort(morestackCalls, [](const Relocation *l, const Relocation *r) {
+ return l->offset < r->offset;
});
- std::vector<Defined *> Functions(Prologues.begin(), Prologues.end());
- llvm::sort(Functions, [](const Defined *L, const Defined *R) {
- return L->Value < R->Value;
+ std::vector<Defined *> functions(prologues.begin(), prologues.end());
+ llvm::sort(functions, [](const Defined *l, const Defined *r) {
+ return l->value < r->value;
});
- auto It = MorestackCalls.begin();
- for (Defined *F : Functions) {
+ auto it = morestackCalls.begin();
+ for (Defined *f : functions) {
// Find the first call to __morestack within the function.
- while (It != MorestackCalls.end() && (*It)->Offset < F->Value)
- ++It;
+ while (it != morestackCalls.end() && (*it)->offset < f->value)
+ ++it;
// Adjust all calls inside the function.
- while (It != MorestackCalls.end() && (*It)->Offset < F->Value + F->Size) {
- (*It)->Sym = MoreStackNonSplit;
- ++It;
+ while (it != morestackCalls.end() && (*it)->offset < f->value + f->size) {
+ (*it)->sym = moreStackNonSplit;
+ ++it;
}
}
}
-static bool enclosingPrologueAttempted(uint64_t Offset,
- const DenseSet<Defined *> &Prologues) {
- for (Defined *F : Prologues)
- if (F->Value <= Offset && Offset < F->Value + F->Size)
+static bool enclosingPrologueAttempted(uint64_t offset,
+ const DenseSet<Defined *> &prologues) {
+ for (Defined *f : prologues)
+ if (f->value <= offset && offset < f->value + f->size)
return true;
return false;
}
@@ -982,30 +1026,30 @@ static bool enclosingPrologueAttempted(uint64_t Offset,
// adjusted to ensure that the called function will have enough stack
// available. Find those functions, and adjust their prologues.
template <class ELFT>
-void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
- uint8_t *End) {
- if (!getFile<ELFT>()->SplitStack)
+void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *buf,
+ uint8_t *end) {
+ if (!getFile<ELFT>()->splitStack)
return;
- DenseSet<Defined *> Prologues;
- std::vector<Relocation *> MorestackCalls;
+ DenseSet<Defined *> prologues;
+ std::vector<Relocation *> morestackCalls;
- for (Relocation &Rel : Relocations) {
+ for (Relocation &rel : relocations) {
// Local symbols can't possibly be cross-calls, and should have been
// resolved long before this line.
- if (Rel.Sym->isLocal())
+ if (rel.sym->isLocal())
continue;
// Ignore calls into the split-stack api.
- if (Rel.Sym->getName().startswith("__morestack")) {
- if (Rel.Sym->getName().equals("__morestack"))
- MorestackCalls.push_back(&Rel);
+ if (rel.sym->getName().startswith("__morestack")) {
+ if (rel.sym->getName().equals("__morestack"))
+ morestackCalls.push_back(&rel);
continue;
}
// A relocation to non-function isn't relevant. Sometimes
// __morestack is not marked as a function, so this check comes
// after the name check.
- if (Rel.Sym->Type != STT_FUNC)
+ if (rel.sym->type != STT_FUNC)
continue;
// If the callee's-file was compiled with split stack, nothing to do. In
@@ -1013,106 +1057,117 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
// being produced". So an "undefined" symbol might be provided by a shared
// library. It is not possible to tell how such symbols were compiled, so be
// conservative.
- if (Defined *D = dyn_cast<Defined>(Rel.Sym))
- if (InputSection *IS = cast_or_null<InputSection>(D->Section))
- if (!IS || !IS->getFile<ELFT>() || IS->getFile<ELFT>()->SplitStack)
+ if (Defined *d = dyn_cast<Defined>(rel.sym))
+ if (InputSection *isec = cast_or_null<InputSection>(d->section))
+ if (!isec || !isec->getFile<ELFT>() || isec->getFile<ELFT>()->splitStack)
continue;
- if (enclosingPrologueAttempted(Rel.Offset, Prologues))
+ if (enclosingPrologueAttempted(rel.offset, prologues))
continue;
- if (Defined *F = getEnclosingFunction<ELFT>(Rel.Offset)) {
- Prologues.insert(F);
- if (Target->adjustPrologueForCrossSplitStack(Buf + getOffset(F->Value),
- End, F->StOther))
+ if (Defined *f = getEnclosingFunction<ELFT>(rel.offset)) {
+ prologues.insert(f);
+ if (target->adjustPrologueForCrossSplitStack(buf + getOffset(f->value),
+ end, f->stOther))
continue;
- if (!getFile<ELFT>()->SomeNoSplitStack)
- error(lld::toString(this) + ": " + F->getName() +
- " (with -fsplit-stack) calls " + Rel.Sym->getName() +
+ if (!getFile<ELFT>()->someNoSplitStack)
+ error(lld::toString(this) + ": " + f->getName() +
+ " (with -fsplit-stack) calls " + rel.sym->getName() +
" (without -fsplit-stack), but couldn't adjust its prologue");
}
}
- if (Target->NeedsMoreStackNonSplit)
- switchMorestackCallsToMorestackNonSplit(Prologues, MorestackCalls);
+ if (target->needsMoreStackNonSplit)
+ switchMorestackCallsToMorestackNonSplit(prologues, morestackCalls);
}
-template <class ELFT> void InputSection::writeTo(uint8_t *Buf) {
- if (Type == SHT_NOBITS)
+template <class ELFT> void InputSection::writeTo(uint8_t *buf) {
+ if (type == SHT_NOBITS)
return;
- if (auto *S = dyn_cast<SyntheticSection>(this)) {
- S->writeTo(Buf + OutSecOff);
+ if (auto *s = dyn_cast<SyntheticSection>(this)) {
+ s->writeTo(buf + outSecOff);
return;
}
// If -r or --emit-relocs is given, then an InputSection
// may be a relocation section.
- if (Type == SHT_RELA) {
- copyRelocations<ELFT>(Buf + OutSecOff, getDataAs<typename ELFT::Rela>());
+ if (type == SHT_RELA) {
+ copyRelocations<ELFT>(buf + outSecOff, getDataAs<typename ELFT::Rela>());
return;
}
- if (Type == SHT_REL) {
- copyRelocations<ELFT>(Buf + OutSecOff, getDataAs<typename ELFT::Rel>());
+ if (type == SHT_REL) {
+ copyRelocations<ELFT>(buf + outSecOff, getDataAs<typename ELFT::Rel>());
return;
}
// If -r is given, we may have a SHT_GROUP section.
- if (Type == SHT_GROUP) {
- copyShtGroup<ELFT>(Buf + OutSecOff);
+ if (type == SHT_GROUP) {
+ copyShtGroup<ELFT>(buf + outSecOff);
return;
}
// If this is a compressed section, uncompress section contents directly
// to the buffer.
- if (UncompressedSize >= 0 && !UncompressedBuf) {
- size_t Size = UncompressedSize;
- if (Error E = zlib::uncompress(toStringRef(RawData),
- (char *)(Buf + OutSecOff), Size))
+ if (uncompressedSize >= 0) {
+ size_t size = uncompressedSize;
+ if (Error e = zlib::uncompress(toStringRef(rawData),
+ (char *)(buf + outSecOff), size))
fatal(toString(this) +
- ": uncompress failed: " + llvm::toString(std::move(E)));
- uint8_t *BufEnd = Buf + OutSecOff + Size;
- relocate<ELFT>(Buf, BufEnd);
+ ": uncompress failed: " + llvm::toString(std::move(e)));
+ uint8_t *bufEnd = buf + outSecOff + size;
+ relocate<ELFT>(buf, bufEnd);
return;
}
// Copy section contents from source object file to output file
// and then apply relocations.
- memcpy(Buf + OutSecOff, data().data(), data().size());
- uint8_t *BufEnd = Buf + OutSecOff + data().size();
- relocate<ELFT>(Buf, BufEnd);
+ memcpy(buf + outSecOff, data().data(), data().size());
+ uint8_t *bufEnd = buf + outSecOff + data().size();
+ relocate<ELFT>(buf, bufEnd);
}
-void InputSection::replace(InputSection *Other) {
- Alignment = std::max(Alignment, Other->Alignment);
- Other->Repl = Repl;
- Other->Live = false;
+void InputSection::replace(InputSection *other) {
+ alignment = std::max(alignment, other->alignment);
+
+ // When a section is replaced with another section that was allocated to
+ // another partition, the replacement section (and its associated sections)
+ // need to be placed in the main partition so that both partitions will be
+ // able to access it.
+ if (partition != other->partition) {
+ partition = 1;
+ for (InputSection *isec : dependentSections)
+ isec->partition = 1;
+ }
+
+ other->repl = repl;
+ other->markDead();
}
template <class ELFT>
-EhInputSection::EhInputSection(ObjFile<ELFT> &F,
- const typename ELFT::Shdr &Header,
- StringRef Name)
- : InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) {}
+EhInputSection::EhInputSection(ObjFile<ELFT> &f,
+ const typename ELFT::Shdr &header,
+ StringRef name)
+ : InputSectionBase(f, header, name, InputSectionBase::EHFrame) {}
SyntheticSection *EhInputSection::getParent() const {
- return cast_or_null<SyntheticSection>(Parent);
+ return cast_or_null<SyntheticSection>(parent);
}
// Returns the index of the first relocation that points to a region between
// Begin and Begin+Size.
template <class IntTy, class RelTy>
-static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef<RelTy> &Rels,
- unsigned &RelocI) {
+static unsigned getReloc(IntTy begin, IntTy size, const ArrayRef<RelTy> &rels,
+ unsigned &relocI) {
// Start search from RelocI for fast access. That works because the
// relocations are sorted in .eh_frame.
- for (unsigned N = Rels.size(); RelocI < N; ++RelocI) {
- const RelTy &Rel = Rels[RelocI];
- if (Rel.r_offset < Begin)
+ for (unsigned n = rels.size(); relocI < n; ++relocI) {
+ const RelTy &rel = rels[relocI];
+ if (rel.r_offset < begin)
continue;
- if (Rel.r_offset < Begin + Size)
- return RelocI;
+ if (rel.r_offset < begin + size)
+ return relocI;
return -1;
}
return -1;
@@ -1121,84 +1176,84 @@ static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef<RelTy> &Rels,
// .eh_frame is a sequence of CIE or FDE records.
// This function splits an input section into records and returns them.
template <class ELFT> void EhInputSection::split() {
- if (AreRelocsRela)
+ if (areRelocsRela)
split<ELFT>(relas<ELFT>());
else
split<ELFT>(rels<ELFT>());
}
template <class ELFT, class RelTy>
-void EhInputSection::split(ArrayRef<RelTy> Rels) {
- unsigned RelI = 0;
- for (size_t Off = 0, End = data().size(); Off != End;) {
- size_t Size = readEhRecordSize(this, Off);
- Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI));
+void EhInputSection::split(ArrayRef<RelTy> rels) {
+ unsigned relI = 0;
+ for (size_t off = 0, end = data().size(); off != end;) {
+ size_t size = readEhRecordSize(this, off);
+ pieces.emplace_back(off, this, size, getReloc(off, size, rels, relI));
// The empty record is the end marker.
- if (Size == 4)
+ if (size == 4)
break;
- Off += Size;
+ off += size;
}
}
-static size_t findNull(StringRef S, size_t EntSize) {
+static size_t findNull(StringRef s, size_t entSize) {
// Optimize the common case.
- if (EntSize == 1)
- return S.find(0);
+ if (entSize == 1)
+ return s.find(0);
- for (unsigned I = 0, N = S.size(); I != N; I += EntSize) {
- const char *B = S.begin() + I;
- if (std::all_of(B, B + EntSize, [](char C) { return C == 0; }))
- return I;
+ for (unsigned i = 0, n = s.size(); i != n; i += entSize) {
+ const char *b = s.begin() + i;
+ if (std::all_of(b, b + entSize, [](char c) { return c == 0; }))
+ return i;
}
return StringRef::npos;
}
SyntheticSection *MergeInputSection::getParent() const {
- return cast_or_null<SyntheticSection>(Parent);
+ return cast_or_null<SyntheticSection>(parent);
}
// Split SHF_STRINGS section. Such section is a sequence of
// null-terminated strings.
-void MergeInputSection::splitStrings(ArrayRef<uint8_t> Data, size_t EntSize) {
- size_t Off = 0;
- bool IsAlloc = Flags & SHF_ALLOC;
- StringRef S = toStringRef(Data);
-
- while (!S.empty()) {
- size_t End = findNull(S, EntSize);
- if (End == StringRef::npos)
+void MergeInputSection::splitStrings(ArrayRef<uint8_t> data, size_t entSize) {
+ size_t off = 0;
+ bool isAlloc = flags & SHF_ALLOC;
+ StringRef s = toStringRef(data);
+
+ while (!s.empty()) {
+ size_t end = findNull(s, entSize);
+ if (end == StringRef::npos)
fatal(toString(this) + ": string is not null terminated");
- size_t Size = End + EntSize;
+ size_t size = end + entSize;
- Pieces.emplace_back(Off, xxHash64(S.substr(0, Size)), !IsAlloc);
- S = S.substr(Size);
- Off += Size;
+ pieces.emplace_back(off, xxHash64(s.substr(0, size)), !isAlloc);
+ s = s.substr(size);
+ off += size;
}
}
// Split non-SHF_STRINGS section. Such section is a sequence of
// fixed size records.
-void MergeInputSection::splitNonStrings(ArrayRef<uint8_t> Data,
- size_t EntSize) {
- size_t Size = Data.size();
- assert((Size % EntSize) == 0);
- bool IsAlloc = Flags & SHF_ALLOC;
-
- for (size_t I = 0; I != Size; I += EntSize)
- Pieces.emplace_back(I, xxHash64(Data.slice(I, EntSize)), !IsAlloc);
+void MergeInputSection::splitNonStrings(ArrayRef<uint8_t> data,
+ size_t entSize) {
+ size_t size = data.size();
+ assert((size % entSize) == 0);
+ bool isAlloc = flags & SHF_ALLOC;
+
+ for (size_t i = 0; i != size; i += entSize)
+ pieces.emplace_back(i, xxHash64(data.slice(i, entSize)), !isAlloc);
}
template <class ELFT>
-MergeInputSection::MergeInputSection(ObjFile<ELFT> &F,
- const typename ELFT::Shdr &Header,
- StringRef Name)
- : InputSectionBase(F, Header, Name, InputSectionBase::Merge) {}
+MergeInputSection::MergeInputSection(ObjFile<ELFT> &f,
+ const typename ELFT::Shdr &header,
+ StringRef name)
+ : InputSectionBase(f, header, name, InputSectionBase::Merge) {}
-MergeInputSection::MergeInputSection(uint64_t Flags, uint32_t Type,
- uint64_t Entsize, ArrayRef<uint8_t> Data,
- StringRef Name)
- : InputSectionBase(nullptr, Flags, Type, Entsize, /*Link*/ 0, /*Info*/ 0,
- /*Alignment*/ Entsize, Data, Name, SectionBase::Merge) {}
+MergeInputSection::MergeInputSection(uint64_t flags, uint32_t type,
+ uint64_t entsize, ArrayRef<uint8_t> data,
+ StringRef name)
+ : InputSectionBase(nullptr, flags, type, entsize, /*Link*/ 0, /*Info*/ 0,
+ /*Alignment*/ entsize, data, name, SectionBase::Merge) {}
// This function is called after we obtain a complete list of input sections
// that need to be linked. This is responsible to split section contents
@@ -1207,37 +1262,35 @@ MergeInputSection::MergeInputSection(uint64_t Flags, uint32_t Type,
// Note that this function is called from parallelForEach. This must be
// thread-safe (i.e. no memory allocation from the pools).
void MergeInputSection::splitIntoPieces() {
- assert(Pieces.empty());
+ assert(pieces.empty());
- if (Flags & SHF_STRINGS)
- splitStrings(data(), Entsize);
+ if (flags & SHF_STRINGS)
+ splitStrings(data(), entsize);
else
- splitNonStrings(data(), Entsize);
+ splitNonStrings(data(), entsize);
}
-SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
- if (this->data().size() <= Offset)
+SectionPiece *MergeInputSection::getSectionPiece(uint64_t offset) {
+ if (this->data().size() <= offset)
fatal(toString(this) + ": offset is outside the section");
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to do a binary search of the original section piece vector.
- auto It2 =
- llvm::upper_bound(Pieces, Offset, [](uint64_t Offset, SectionPiece P) {
- return Offset < P.InputOff;
- });
- return &It2[-1];
+ auto it = partition_point(
+ pieces, [=](SectionPiece p) { return p.inputOff <= offset; });
+ return &it[-1];
}
// Returns the offset in an output section for a given input offset.
// Because contents of a mergeable section is not contiguous in output,
// it is not just an addition to a base output offset.
-uint64_t MergeInputSection::getParentOffset(uint64_t Offset) const {
+uint64_t MergeInputSection::getParentOffset(uint64_t offset) const {
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to search from the original section piece vector.
- const SectionPiece &Piece =
- *(const_cast<MergeInputSection *>(this)->getSectionPiece (Offset));
- uint64_t Addend = Offset - Piece.InputOff;
- return Piece.OutputOff + Addend;
+ const SectionPiece &piece =
+ *(const_cast<MergeInputSection *>(this)->getSectionPiece (offset));
+ uint64_t addend = offset - piece.inputOff;
+ return piece.outputOff + addend;
}
template InputSection::InputSection(ObjFile<ELF32LE> &, const ELF32LE::Shdr &,
diff --git a/ELF/InputSection.h b/ELF/InputSection.h
index 34f411e87200d..3a974074e0e51 100644
--- a/ELF/InputSection.h
+++ b/ELF/InputSection.h
@@ -1,9 +1,8 @@
//===- InputSection.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -26,11 +25,14 @@ class Symbol;
struct SectionPiece;
class Defined;
+struct Partition;
class SyntheticSection;
class MergeSyntheticSection;
template <class ELFT> class ObjFile;
class OutputSection;
+extern std::vector<Partition> partitions;
+
// This is the base class of all sections that lld handles. Some are sections in
// input files, some are sections in the produced output file and some exist
// just as a convenience for implementing special ways of combining some
@@ -39,38 +41,53 @@ class SectionBase {
public:
enum Kind { Regular, EHFrame, Merge, Synthetic, Output };
- Kind kind() const { return (Kind)SectionKind; }
+ Kind kind() const { return (Kind)sectionKind; }
- StringRef Name;
+ StringRef name;
// This pointer points to the "real" instance of this instance.
// Usually Repl == this. However, if ICF merges two sections,
// Repl pointer of one section points to another section. So,
// if you need to get a pointer to this instance, do not use
// this but instead this->Repl.
- SectionBase *Repl;
+ SectionBase *repl;
- unsigned SectionKind : 3;
+ unsigned sectionKind : 3;
- // The next two bit fields are only used by InputSectionBase, but we
+ // The next three bit fields are only used by InputSectionBase, but we
// put them here so the struct packs better.
- // The garbage collector sets sections' Live bits.
- // If GC is disabled, all sections are considered live by default.
- unsigned Live : 1;
+ // True if this section has already been placed to a linker script
+ // output section. This is needed because, in a linker script, you
+ // can refer to the same section more than once. For example, in
+ // the following linker script,
+ //
+ // .foo : { *(.text) }
+ // .bar : { *(.text) }
+ //
+ // .foo takes all .text sections, and .bar becomes empty. To achieve
+ // this, we need to memorize whether a section has been placed or
+ // not for each input section.
+ unsigned assigned : 1;
- unsigned Bss : 1;
+ unsigned bss : 1;
// Set for sections that should not be folded by ICF.
- unsigned KeepUnique : 1;
+ unsigned keepUnique : 1;
+
+ // The 1-indexed partition that this section is assigned to by the garbage
+ // collector, or 0 if this section is dead. Normally there is only one
+ // partition, so this will either be 0 or 1.
+ uint8_t partition;
+ elf::Partition &getPartition() const;
// These corresponds to the fields in Elf_Shdr.
- uint32_t Alignment;
- uint64_t Flags;
- uint64_t Entsize;
- uint32_t Type;
- uint32_t Link;
- uint32_t Info;
+ uint32_t alignment;
+ uint64_t flags;
+ uint64_t entsize;
+ uint32_t type;
+ uint32_t link;
+ uint32_t info;
OutputSection *getOutputSection();
const OutputSection *getOutputSection() const {
@@ -79,90 +96,81 @@ public:
// Translate an offset in the input section to an offset in the output
// section.
- uint64_t getOffset(uint64_t Offset) const;
+ uint64_t getOffset(uint64_t offset) const;
- uint64_t getVA(uint64_t Offset = 0) const;
+ uint64_t getVA(uint64_t offset = 0) const;
+
+ bool isLive() const { return partition != 0; }
+ void markLive() { partition = 1; }
+ void markDead() { partition = 0; }
protected:
- SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags,
- uint64_t Entsize, uint64_t Alignment, uint32_t Type,
- uint32_t Info, uint32_t Link)
- : Name(Name), Repl(this), SectionKind(SectionKind), Live(false),
- Bss(false), KeepUnique(false), Alignment(Alignment), Flags(Flags),
- Entsize(Entsize), Type(Type), Link(Link), Info(Info) {}
+ SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
+ uint64_t entsize, uint64_t alignment, uint32_t type,
+ uint32_t info, uint32_t link)
+ : name(name), repl(this), sectionKind(sectionKind), assigned(false),
+ bss(false), keepUnique(false), partition(0), alignment(alignment),
+ flags(flags), entsize(entsize), type(type), link(link), info(info) {}
};
// This corresponds to a section of an input file.
class InputSectionBase : public SectionBase {
public:
template <class ELFT>
- InputSectionBase(ObjFile<ELFT> &File, const typename ELFT::Shdr &Header,
- StringRef Name, Kind SectionKind);
+ InputSectionBase(ObjFile<ELFT> &file, const typename ELFT::Shdr &header,
+ StringRef name, Kind sectionKind);
+
+ InputSectionBase(InputFile *file, uint64_t flags, uint32_t type,
+ uint64_t entsize, uint32_t link, uint32_t info,
+ uint32_t alignment, ArrayRef<uint8_t> data, StringRef name,
+ Kind sectionKind);
- InputSectionBase(InputFile *File, uint64_t Flags, uint32_t Type,
- uint64_t Entsize, uint32_t Link, uint32_t Info,
- uint32_t Alignment, ArrayRef<uint8_t> Data, StringRef Name,
- Kind SectionKind);
+ static bool classof(const SectionBase *s) { return s->kind() != Output; }
- static bool classof(const SectionBase *S) { return S->kind() != Output; }
+ // Relocations that refer to this section.
+ unsigned numRelocations : 31;
+ unsigned areRelocsRela : 1;
+ const void *firstRelocation = nullptr;
// The file which contains this section. Its dynamic type is always
// ObjFile<ELFT>, but in order to avoid ELFT, we use InputFile as
// its static type.
- InputFile *File;
+ InputFile *file;
template <class ELFT> ObjFile<ELFT> *getFile() const {
- return cast_or_null<ObjFile<ELFT>>(File);
+ return cast_or_null<ObjFile<ELFT>>(file);
}
ArrayRef<uint8_t> data() const {
- if (UncompressedSize >= 0 && !UncompressedBuf)
+ if (uncompressedSize >= 0)
uncompress();
- return RawData;
+ return rawData;
}
uint64_t getOffsetInFile() const;
- // True if this section has already been placed to a linker script
- // output section. This is needed because, in a linker script, you
- // can refer to the same section more than once. For example, in
- // the following linker script,
- //
- // .foo : { *(.text) }
- // .bar : { *(.text) }
- //
- // .foo takes all .text sections, and .bar becomes empty. To achieve
- // this, we need to memorize whether a section has been placed or
- // not for each input section.
- bool Assigned = false;
-
// Input sections are part of an output section. Special sections
// like .eh_frame and merge sections are first combined into a
// synthetic section that is then added to an output section. In all
// cases this points one level up.
- SectionBase *Parent = nullptr;
-
- // Relocations that refer to this section.
- const void *FirstRelocation = nullptr;
- unsigned NumRelocations : 31;
- unsigned AreRelocsRela : 1;
+ SectionBase *parent = nullptr;
template <class ELFT> ArrayRef<typename ELFT::Rel> rels() const {
- assert(!AreRelocsRela);
+ assert(!areRelocsRela);
return llvm::makeArrayRef(
- static_cast<const typename ELFT::Rel *>(FirstRelocation),
- NumRelocations);
+ static_cast<const typename ELFT::Rel *>(firstRelocation),
+ numRelocations);
}
template <class ELFT> ArrayRef<typename ELFT::Rela> relas() const {
- assert(AreRelocsRela);
+ assert(areRelocsRela);
return llvm::makeArrayRef(
- static_cast<const typename ELFT::Rela *>(FirstRelocation),
- NumRelocations);
+ static_cast<const typename ELFT::Rela *>(firstRelocation),
+ numRelocations);
}
// InputSections that are dependent on us (reverse dependency for GC)
- llvm::TinyPtrVector<InputSection *> DependentSections;
+ llvm::TinyPtrVector<InputSection *> dependentSections;
// Returns the size of this section (even if this is a common or BSS.)
size_t getSize() const;
@@ -172,23 +180,23 @@ public:
// Get the function symbol that encloses this offset from within the
// section.
template <class ELFT>
- Defined *getEnclosingFunction(uint64_t Offset);
+ Defined *getEnclosingFunction(uint64_t offset);
// Returns a source location string. Used to construct an error message.
- template <class ELFT> std::string getLocation(uint64_t Offset);
- std::string getSrcMsg(const Symbol &Sym, uint64_t Offset);
- std::string getObjMsg(uint64_t Offset);
+ template <class ELFT> std::string getLocation(uint64_t offset);
+ std::string getSrcMsg(const Symbol &sym, uint64_t offset);
+ std::string getObjMsg(uint64_t offset);
// Each section knows how to relocate itself. These functions apply
// relocations, assuming that Buf points to this section's copy in
// the mmap'ed output buffer.
- template <class ELFT> void relocate(uint8_t *Buf, uint8_t *BufEnd);
- void relocateAlloc(uint8_t *Buf, uint8_t *BufEnd);
+ template <class ELFT> void relocate(uint8_t *buf, uint8_t *bufEnd);
+ void relocateAlloc(uint8_t *buf, uint8_t *bufEnd);
// The native ELF reloc data type is not very convenient to handle.
// So we convert ELF reloc records to our own records in Relocations.cpp.
// This vector contains such "cooked" relocations.
- std::vector<Relocation> Relocations;
+ std::vector<Relocation> relocations;
// A function compiled with -fsplit-stack calling a function
// compiled without -fsplit-stack needs its prologue adjusted. Find
@@ -196,25 +204,26 @@ public:
// to relocation. See https://gcc.gnu.org/wiki/SplitStacks for more
// information.
template <typename ELFT>
- void adjustSplitStackFunctionPrologues(uint8_t *Buf, uint8_t *End);
+ void adjustSplitStackFunctionPrologues(uint8_t *buf, uint8_t *end);
template <typename T> llvm::ArrayRef<T> getDataAs() const {
- size_t S = data().size();
- assert(S % sizeof(T) == 0);
- return llvm::makeArrayRef<T>((const T *)data().data(), S / sizeof(T));
+ size_t s = data().size();
+ assert(s % sizeof(T) == 0);
+ return llvm::makeArrayRef<T>((const T *)data().data(), s / sizeof(T));
}
protected:
void parseCompressedHeader();
void uncompress() const;
- mutable ArrayRef<uint8_t> RawData;
+ mutable ArrayRef<uint8_t> rawData;
- // A pointer that owns uncompressed data if a section is compressed by zlib.
- // Since the feature is not used often, this is usually a nullptr.
- mutable std::unique_ptr<char[]> UncompressedBuf;
- int64_t UncompressedSize = -1;
+ // This field stores the uncompressed size of the compressed data in rawData,
+ // or -1 if rawData is not compressed (either because the section wasn't
+ // compressed in the first place, or because we ended up uncompressing it).
+ // Since the feature is not used often, this is usually -1.
+ mutable int64_t uncompressedSize = -1;
};
// SectionPiece represents a piece of splittable section contents.
@@ -222,14 +231,13 @@ protected:
// have to be as compact as possible, which is why we don't store the size (can
// be found by looking at the next one).
struct SectionPiece {
- SectionPiece(size_t Off, uint32_t Hash, bool Live)
- : InputOff(Off), Hash(Hash), OutputOff(0),
- Live(Live || !Config->GcSections) {}
-
- uint32_t InputOff;
- uint32_t Hash;
- int64_t OutputOff : 63;
- uint64_t Live : 1;
+ SectionPiece(size_t off, uint32_t hash, bool live)
+ : inputOff(off), live(live || !config->gcSections), hash(hash >> 1) {}
+
+ uint32_t inputOff;
+ uint32_t live : 1;
+ uint32_t hash : 31;
+ uint64_t outputOff = 0;
};
static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big");
@@ -238,74 +246,74 @@ static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big");
class MergeInputSection : public InputSectionBase {
public:
template <class ELFT>
- MergeInputSection(ObjFile<ELFT> &F, const typename ELFT::Shdr &Header,
- StringRef Name);
- MergeInputSection(uint64_t Flags, uint32_t Type, uint64_t Entsize,
- ArrayRef<uint8_t> Data, StringRef Name);
+ MergeInputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
+ StringRef name);
+ MergeInputSection(uint64_t flags, uint32_t type, uint64_t entsize,
+ ArrayRef<uint8_t> data, StringRef name);
- static bool classof(const SectionBase *S) { return S->kind() == Merge; }
+ static bool classof(const SectionBase *s) { return s->kind() == Merge; }
void splitIntoPieces();
// Translate an offset in the input section to an offset in the parent
// MergeSyntheticSection.
- uint64_t getParentOffset(uint64_t Offset) const;
+ uint64_t getParentOffset(uint64_t offset) const;
// Splittable sections are handled as a sequence of data
// rather than a single large blob of data.
- std::vector<SectionPiece> Pieces;
+ std::vector<SectionPiece> pieces;
// Returns I'th piece's data. This function is very hot when
// string merging is enabled, so we want to inline.
LLVM_ATTRIBUTE_ALWAYS_INLINE
- llvm::CachedHashStringRef getData(size_t I) const {
- size_t Begin = Pieces[I].InputOff;
- size_t End =
- (Pieces.size() - 1 == I) ? data().size() : Pieces[I + 1].InputOff;
- return {toStringRef(data().slice(Begin, End - Begin)), Pieces[I].Hash};
+ llvm::CachedHashStringRef getData(size_t i) const {
+ size_t begin = pieces[i].inputOff;
+ size_t end =
+ (pieces.size() - 1 == i) ? data().size() : pieces[i + 1].inputOff;
+ return {toStringRef(data().slice(begin, end - begin)), pieces[i].hash};
}
// Returns the SectionPiece at a given input section offset.
- SectionPiece *getSectionPiece(uint64_t Offset);
- const SectionPiece *getSectionPiece(uint64_t Offset) const {
- return const_cast<MergeInputSection *>(this)->getSectionPiece(Offset);
+ SectionPiece *getSectionPiece(uint64_t offset);
+ const SectionPiece *getSectionPiece(uint64_t offset) const {
+ return const_cast<MergeInputSection *>(this)->getSectionPiece(offset);
}
SyntheticSection *getParent() const;
private:
- void splitStrings(ArrayRef<uint8_t> A, size_t Size);
- void splitNonStrings(ArrayRef<uint8_t> A, size_t Size);
+ void splitStrings(ArrayRef<uint8_t> a, size_t size);
+ void splitNonStrings(ArrayRef<uint8_t> a, size_t size);
};
struct EhSectionPiece {
- EhSectionPiece(size_t Off, InputSectionBase *Sec, uint32_t Size,
- unsigned FirstRelocation)
- : InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {}
+ EhSectionPiece(size_t off, InputSectionBase *sec, uint32_t size,
+ unsigned firstRelocation)
+ : inputOff(off), sec(sec), size(size), firstRelocation(firstRelocation) {}
ArrayRef<uint8_t> data() {
- return {Sec->data().data() + this->InputOff, Size};
+ return {sec->data().data() + this->inputOff, size};
}
- size_t InputOff;
- ssize_t OutputOff = -1;
- InputSectionBase *Sec;
- uint32_t Size;
- unsigned FirstRelocation;
+ size_t inputOff;
+ ssize_t outputOff = -1;
+ InputSectionBase *sec;
+ uint32_t size;
+ unsigned firstRelocation;
};
// This corresponds to a .eh_frame section of an input file.
class EhInputSection : public InputSectionBase {
public:
template <class ELFT>
- EhInputSection(ObjFile<ELFT> &F, const typename ELFT::Shdr &Header,
- StringRef Name);
- static bool classof(const SectionBase *S) { return S->kind() == EHFrame; }
+ EhInputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
+ StringRef name);
+ static bool classof(const SectionBase *s) { return s->kind() == EHFrame; }
template <class ELFT> void split();
- template <class ELFT, class RelTy> void split(ArrayRef<RelTy> Rels);
+ template <class ELFT, class RelTy> void split(ArrayRef<RelTy> rels);
// Splittable sections are handled as a sequence of data
// rather than a single large blob of data.
- std::vector<EhSectionPiece> Pieces;
+ std::vector<EhSectionPiece> pieces;
SyntheticSection *getParent() const;
};
@@ -316,17 +324,17 @@ public:
// .eh_frame. It also includes the synthetic sections themselves.
class InputSection : public InputSectionBase {
public:
- InputSection(InputFile *F, uint64_t Flags, uint32_t Type, uint32_t Alignment,
- ArrayRef<uint8_t> Data, StringRef Name, Kind K = Regular);
+ InputSection(InputFile *f, uint64_t flags, uint32_t type, uint32_t alignment,
+ ArrayRef<uint8_t> data, StringRef name, Kind k = Regular);
template <class ELFT>
- InputSection(ObjFile<ELFT> &F, const typename ELFT::Shdr &Header,
- StringRef Name);
+ InputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
+ StringRef name);
// Write this section to a mmap'ed file, assuming Buf is pointing to
// beginning of the output section.
- template <class ELFT> void writeTo(uint8_t *Buf);
+ template <class ELFT> void writeTo(uint8_t *buf);
- uint64_t getOffset(uint64_t Offset) const { return OutSecOff + Offset; }
+ uint64_t getOffset(uint64_t offset) const { return outSecOff + offset; }
OutputSection *getParent() const;
@@ -334,32 +342,32 @@ public:
// OutputSection's InputSection list, and is used when ordering SHF_LINK_ORDER
// sections. After assignAddresses is called, it represents the offset from
// the beginning of the output section this section was assigned to.
- uint64_t OutSecOff = 0;
+ uint64_t outSecOff = 0;
- static bool classof(const SectionBase *S);
+ static bool classof(const SectionBase *s);
InputSectionBase *getRelocatedSection() const;
template <class ELFT, class RelTy>
- void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
+ void relocateNonAlloc(uint8_t *buf, llvm::ArrayRef<RelTy> rels);
// Used by ICF.
- uint32_t Class[2] = {0, 0};
+ uint32_t eqClass[2] = {0, 0};
// Called by ICF to merge two input sections.
- void replace(InputSection *Other);
+ void replace(InputSection *other);
- static InputSection Discarded;
+ static InputSection discarded;
private:
template <class ELFT, class RelTy>
- void copyRelocations(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
+ void copyRelocations(uint8_t *buf, llvm::ArrayRef<RelTy> rels);
- template <class ELFT> void copyShtGroup(uint8_t *Buf);
+ template <class ELFT> void copyShtGroup(uint8_t *buf);
};
// The list of all input sections.
-extern std::vector<InputSectionBase *> InputSections;
+extern std::vector<InputSectionBase *> inputSections;
} // namespace elf
diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp
index ca44581780e47..28d4bfe77c5d4 100644
--- a/ELF/LTO.cpp
+++ b/ELF/LTO.cpp
@@ -1,9 +1,8 @@
//===- LTO.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -13,6 +12,7 @@
#include "LinkerScript.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "lld/Common/Args.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
#include "llvm/ADT/STLExtras.h"
@@ -47,134 +47,135 @@ using namespace lld::elf;
// Creates an empty file to store a list of object files for final
// linking of distributed ThinLTO.
-static std::unique_ptr<raw_fd_ostream> openFile(StringRef File) {
- std::error_code EC;
- auto Ret =
- llvm::make_unique<raw_fd_ostream>(File, EC, sys::fs::OpenFlags::F_None);
- if (EC) {
- error("cannot open " + File + ": " + EC.message());
+static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
+ std::error_code ec;
+ auto ret =
+ llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
+ if (ec) {
+ error("cannot open " + file + ": " + ec.message());
return nullptr;
}
- return Ret;
+ return ret;
}
-static std::string getThinLTOOutputFile(StringRef ModulePath) {
- return lto::getThinLTOOutputFile(ModulePath,
- Config->ThinLTOPrefixReplace.first,
- Config->ThinLTOPrefixReplace.second);
+static std::string getThinLTOOutputFile(StringRef modulePath) {
+ return lto::getThinLTOOutputFile(modulePath,
+ config->thinLTOPrefixReplace.first,
+ config->thinLTOPrefixReplace.second);
}
static lto::Config createConfig() {
- lto::Config C;
+ lto::Config c;
// LLD supports the new relocations and address-significance tables.
- C.Options = InitTargetOptionsFromCodeGenFlags();
- C.Options.RelaxELFRelocations = true;
- C.Options.EmitAddrsig = true;
+ c.Options = initTargetOptionsFromCodeGenFlags();
+ c.Options.RelaxELFRelocations = true;
+ c.Options.EmitAddrsig = true;
// Always emit a section per function/datum with LTO.
- C.Options.FunctionSections = true;
- C.Options.DataSections = true;
+ c.Options.FunctionSections = true;
+ c.Options.DataSections = true;
- if (Config->Relocatable)
- C.RelocModel = None;
- else if (Config->Pic)
- C.RelocModel = Reloc::PIC_;
+ if (config->relocatable)
+ c.RelocModel = None;
+ else if (config->isPic)
+ c.RelocModel = Reloc::PIC_;
else
- C.RelocModel = Reloc::Static;
+ c.RelocModel = Reloc::Static;
- C.CodeModel = GetCodeModelFromCMModel();
- C.DisableVerify = Config->DisableVerify;
- C.DiagHandler = diagnosticHandler;
- C.OptLevel = Config->LTOO;
- C.CPU = GetCPUStr();
- C.MAttrs = GetMAttrs();
+ c.CodeModel = getCodeModelFromCMModel();
+ c.DisableVerify = config->disableVerify;
+ c.DiagHandler = diagnosticHandler;
+ c.OptLevel = config->ltoo;
+ c.CPU = getCPUStr();
+ c.MAttrs = getMAttrs();
+ c.CGOptLevel = args::getCGOptLevel(config->ltoo);
// Set up a custom pipeline if we've been asked to.
- C.OptPipeline = Config->LTONewPmPasses;
- C.AAPipeline = Config->LTOAAPipeline;
+ c.OptPipeline = config->ltoNewPmPasses;
+ c.AAPipeline = config->ltoAAPipeline;
// Set up optimization remarks if we've been asked to.
- C.RemarksFilename = Config->OptRemarksFilename;
- C.RemarksWithHotness = Config->OptRemarksWithHotness;
-
- C.SampleProfile = Config->LTOSampleProfile;
- C.UseNewPM = Config->LTONewPassManager;
- C.DebugPassManager = Config->LTODebugPassManager;
- C.DwoDir = Config->DwoDir;
-
- if (Config->EmitLLVM) {
- C.PostInternalizeModuleHook = [](size_t Task, const Module &M) {
- if (std::unique_ptr<raw_fd_ostream> OS = openFile(Config->OutputFile))
- WriteBitcodeToFile(M, *OS, false);
+ c.RemarksFilename = config->optRemarksFilename;
+ c.RemarksPasses = config->optRemarksPasses;
+ c.RemarksWithHotness = config->optRemarksWithHotness;
+ c.RemarksFormat = config->optRemarksFormat;
+
+ c.SampleProfile = config->ltoSampleProfile;
+ c.UseNewPM = config->ltoNewPassManager;
+ c.DebugPassManager = config->ltoDebugPassManager;
+ c.DwoDir = config->dwoDir;
+
+ c.CSIRProfile = config->ltoCSProfileFile;
+ c.RunCSIRInstr = config->ltoCSProfileGenerate;
+
+ if (config->emitLLVM) {
+ c.PostInternalizeModuleHook = [](size_t task, const Module &m) {
+ if (std::unique_ptr<raw_fd_ostream> os = openFile(config->outputFile))
+ WriteBitcodeToFile(m, *os, false);
return false;
};
}
- if (Config->SaveTemps)
- checkError(C.addSaveTemps(Config->OutputFile.str() + ".",
+ if (config->saveTemps)
+ checkError(c.addSaveTemps(config->outputFile.str() + ".",
/*UseInputModulePath*/ true));
- return C;
+ return c;
}
BitcodeCompiler::BitcodeCompiler() {
- // Initialize IndexFile.
- if (!Config->ThinLTOIndexOnlyArg.empty())
- IndexFile = openFile(Config->ThinLTOIndexOnlyArg);
-
- // Initialize LTOObj.
- lto::ThinBackend Backend;
- if (Config->ThinLTOIndexOnly) {
- auto OnIndexWrite = [&](StringRef S) { ThinIndices.erase(S); };
- Backend = lto::createWriteIndexesThinBackend(
- Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second,
- Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite);
- } else if (Config->ThinLTOJobs != -1U) {
- Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
+ // Initialize indexFile.
+ if (!config->thinLTOIndexOnlyArg.empty())
+ indexFile = openFile(config->thinLTOIndexOnlyArg);
+
+ // Initialize ltoObj.
+ lto::ThinBackend backend;
+ if (config->thinLTOIndexOnly) {
+ auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
+ backend = lto::createWriteIndexesThinBackend(
+ config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
+ } else if (config->thinLTOJobs != -1U) {
+ backend = lto::createInProcessThinBackend(config->thinLTOJobs);
}
- LTOObj = llvm::make_unique<lto::LTO>(createConfig(), Backend,
- Config->LTOPartitions);
+ ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
+ config->ltoPartitions);
- // Initialize UsedStartStop.
- for (Symbol *Sym : Symtab->getSymbols()) {
- StringRef S = Sym->getName();
- for (StringRef Prefix : {"__start_", "__stop_"})
- if (S.startswith(Prefix))
- UsedStartStop.insert(S.substr(Prefix.size()));
- }
+ // Initialize usedStartStop.
+ symtab->forEachSymbol([&](Symbol *sym) {
+ StringRef s = sym->getName();
+ for (StringRef prefix : {"__start_", "__stop_"})
+ if (s.startswith(prefix))
+ usedStartStop.insert(s.substr(prefix.size()));
+ });
}
BitcodeCompiler::~BitcodeCompiler() = default;
-static void undefine(Symbol *S) {
- replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_GLOBAL, STV_DEFAULT,
- S->Type);
-}
-
-void BitcodeCompiler::add(BitcodeFile &F) {
- lto::InputFile &Obj = *F.Obj;
- bool IsExec = !Config->Shared && !Config->Relocatable;
+void BitcodeCompiler::add(BitcodeFile &f) {
+ lto::InputFile &obj = *f.obj;
+ bool isExec = !config->shared && !config->relocatable;
- if (Config->ThinLTOIndexOnly)
- ThinIndices.insert(Obj.getName());
+ if (config->thinLTOIndexOnly)
+ thinIndices.insert(obj.getName());
- ArrayRef<Symbol *> Syms = F.getSymbols();
- ArrayRef<lto::InputFile::Symbol> ObjSyms = Obj.symbols();
- std::vector<lto::SymbolResolution> Resols(Syms.size());
+ ArrayRef<Symbol *> syms = f.getSymbols();
+ ArrayRef<lto::InputFile::Symbol> objSyms = obj.symbols();
+ std::vector<lto::SymbolResolution> resols(syms.size());
// Provide a resolution to the LTO API for each symbol.
- for (size_t I = 0, E = Syms.size(); I != E; ++I) {
- Symbol *Sym = Syms[I];
- const lto::InputFile::Symbol &ObjSym = ObjSyms[I];
- lto::SymbolResolution &R = Resols[I];
+ for (size_t i = 0, e = syms.size(); i != e; ++i) {
+ Symbol *sym = syms[i];
+ const lto::InputFile::Symbol &objSym = objSyms[i];
+ lto::SymbolResolution &r = resols[i];
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
// reports two symbols for module ASM defined. Without this check, lld
// flags an undefined in IR with a definition in ASM as prevailing.
// Once IRObjectFile is fixed to report only one symbol this hack can
// be removed.
- R.Prevailing = !ObjSym.isUndefined() && Sym->File == &F;
+ r.Prevailing = !objSym.isUndefined() && sym->file == &f;
// We ask LTO to preserve following global symbols:
// 1) All symbols when doing relocatable link, so that them can be used
@@ -182,115 +183,121 @@ void BitcodeCompiler::add(BitcodeFile &F) {
// 2) Symbols that are used in regular objects.
// 3) C named sections if we have corresponding __start_/__stop_ symbol.
// 4) Symbols that are defined in bitcode files and used for dynamic linking.
- R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj ||
- (R.Prevailing && Sym->includeInDynsym()) ||
- UsedStartStop.count(ObjSym.getSectionName());
- const auto *DR = dyn_cast<Defined>(Sym);
- R.FinalDefinitionInLinkageUnit =
- (IsExec || Sym->Visibility != STV_DEFAULT) && DR &&
+ r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj ||
+ (r.Prevailing && sym->includeInDynsym()) ||
+ usedStartStop.count(objSym.getSectionName());
+ const auto *dr = dyn_cast<Defined>(sym);
+ r.FinalDefinitionInLinkageUnit =
+ (isExec || sym->visibility != STV_DEFAULT) && dr &&
// Skip absolute symbols from ELF objects, otherwise PC-rel relocations
// will be generated by for them, triggering linker errors.
// Symbol section is always null for bitcode symbols, hence the check
// for isElf(). Skip linker script defined symbols as well: they have
// no File defined.
- !(DR->Section == nullptr && (!Sym->File || Sym->File->isElf()));
+ !(dr->section == nullptr && (!sym->file || sym->file->isElf()));
- if (R.Prevailing)
- undefine(Sym);
+ if (r.Prevailing)
+ sym->replace(Undefined{nullptr, sym->getName(), STB_GLOBAL, STV_DEFAULT,
+ sym->type});
// We tell LTO to not apply interprocedural optimization for wrapped
// (with --wrap) symbols because otherwise LTO would inline them while
// their values are still not final.
- R.LinkerRedefined = !Sym->CanInline;
+ r.LinkerRedefined = !sym->canInline;
}
- checkError(LTOObj->add(std::move(F.Obj), Resols));
+ checkError(ltoObj->add(std::move(f.obj), resols));
}
-static void createEmptyIndex(StringRef ModulePath) {
- std::string Path = replaceThinLTOSuffix(getThinLTOOutputFile(ModulePath));
- std::unique_ptr<raw_fd_ostream> OS = openFile(Path + ".thinlto.bc");
- if (!OS)
- return;
-
- ModuleSummaryIndex M(/*HaveGVs*/ false);
- M.setSkipModuleByDistributedBackend();
- WriteIndexToFile(M, *OS);
+// If LazyObjFile has not been added to link, emit empty index files.
+// This is needed because this is what GNU gold plugin does and we have a
+// distributed build system that depends on that behavior.
+static void thinLTOCreateEmptyIndexFiles() {
+ for (LazyObjFile *f : lazyObjFiles) {
+ if (!isBitcode(f->mb))
+ continue;
+ std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->getName()));
+ std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
+ if (!os)
+ continue;
- if (Config->ThinLTOEmitImportsFiles)
- openFile(Path + ".imports");
+ ModuleSummaryIndex m(/*HaveGVs*/ false);
+ m.setSkipModuleByDistributedBackend();
+ WriteIndexToFile(m, *os);
+ if (config->thinLTOEmitImportsFiles)
+ openFile(path + ".imports");
+ }
}
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting ObjectFile(s).
std::vector<InputFile *> BitcodeCompiler::compile() {
- unsigned MaxTasks = LTOObj->getMaxTasks();
- Buf.resize(MaxTasks);
- Files.resize(MaxTasks);
+ unsigned maxTasks = ltoObj->getMaxTasks();
+ buf.resize(maxTasks);
+ files.resize(maxTasks);
// The --thinlto-cache-dir option specifies the path to a directory in which
// to cache native object files for ThinLTO incremental builds. If a path was
// specified, configure LTO to use it as the cache directory.
- lto::NativeObjectCache Cache;
- if (!Config->ThinLTOCacheDir.empty())
- Cache = check(
- lto::localCache(Config->ThinLTOCacheDir,
- [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
- Files[Task] = std::move(MB);
+ lto::NativeObjectCache cache;
+ if (!config->thinLTOCacheDir.empty())
+ cache = check(
+ lto::localCache(config->thinLTOCacheDir,
+ [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
+ files[task] = std::move(mb);
}));
- checkError(LTOObj->run(
- [&](size_t Task) {
- return llvm::make_unique<lto::NativeObjectStream>(
- llvm::make_unique<raw_svector_ostream>(Buf[Task]));
- },
- Cache));
+ if (!bitcodeFiles.empty())
+ checkError(ltoObj->run(
+ [&](size_t task) {
+ return llvm::make_unique<lto::NativeObjectStream>(
+ llvm::make_unique<raw_svector_ostream>(buf[task]));
+ },
+ cache));
// Emit empty index files for non-indexed files
- for (StringRef S : ThinIndices) {
- std::string Path = getThinLTOOutputFile(S);
- openFile(Path + ".thinlto.bc");
- if (Config->ThinLTOEmitImportsFiles)
- openFile(Path + ".imports");
+ for (StringRef s : thinIndices) {
+ std::string path = getThinLTOOutputFile(s);
+ openFile(path + ".thinlto.bc");
+ if (config->thinLTOEmitImportsFiles)
+ openFile(path + ".imports");
}
- // If LazyObjFile has not been added to link, emit empty index files.
- // This is needed because this is what GNU gold plugin does and we have a
- // distributed build system that depends on that behavior.
- if (Config->ThinLTOIndexOnly) {
- for (LazyObjFile *F : LazyObjFiles)
- if (!F->AddedToLink && isBitcode(F->MB))
- createEmptyIndex(F->getName());
+ if (config->thinLTOIndexOnly) {
+ thinLTOCreateEmptyIndexFiles();
- if (!Config->LTOObjPath.empty())
- saveBuffer(Buf[0], Config->LTOObjPath);
+ if (!config->ltoObjPath.empty())
+ saveBuffer(buf[0], config->ltoObjPath);
// ThinLTO with index only option is required to generate only the index
// files. After that, we exit from linker and ThinLTO backend runs in a
// distributed environment.
- if (IndexFile)
- IndexFile->close();
+ if (indexFile)
+ indexFile->close();
return {};
}
- if (!Config->ThinLTOCacheDir.empty())
- pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy);
+ if (!config->thinLTOCacheDir.empty())
+ pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy);
- std::vector<InputFile *> Ret;
- for (unsigned I = 0; I != MaxTasks; ++I) {
- if (Buf[I].empty())
- continue;
- if (Config->SaveTemps) {
- if (I == 0)
- saveBuffer(Buf[I], Config->OutputFile + ".lto.o");
- else
- saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.o");
- }
- InputFile *Obj = createObjectFile(MemoryBufferRef(Buf[I], "lto.tmp"));
- Ret.push_back(Obj);
+ if (!config->ltoObjPath.empty()) {
+ saveBuffer(buf[0], config->ltoObjPath);
+ for (unsigned i = 1; i != maxTasks; ++i)
+ saveBuffer(buf[i], config->ltoObjPath + Twine(i));
}
- for (std::unique_ptr<MemoryBuffer> &File : Files)
- if (File)
- Ret.push_back(createObjectFile(*File));
- return Ret;
+ if (config->saveTemps) {
+ saveBuffer(buf[0], config->outputFile + ".lto.o");
+ for (unsigned i = 1; i != maxTasks; ++i)
+ saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");
+ }
+
+ std::vector<InputFile *> ret;
+ for (unsigned i = 0; i != maxTasks; ++i)
+ if (!buf[i].empty())
+ ret.push_back(createObjectFile(MemoryBufferRef(buf[i], "lto.tmp")));
+
+ for (std::unique_ptr<MemoryBuffer> &file : files)
+ if (file)
+ ret.push_back(createObjectFile(*file));
+ return ret;
}
diff --git a/ELF/LTO.h b/ELF/LTO.h
index a190da3e5996e..4cb42d84d919f 100644
--- a/ELF/LTO.h
+++ b/ELF/LTO.h
@@ -1,9 +1,8 @@
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -46,16 +45,16 @@ public:
BitcodeCompiler();
~BitcodeCompiler();
- void add(BitcodeFile &F);
+ void add(BitcodeFile &f);
std::vector<InputFile *> compile();
private:
- std::unique_ptr<llvm::lto::LTO> LTOObj;
- std::vector<SmallString<0>> Buf;
- std::vector<std::unique_ptr<MemoryBuffer>> Files;
- llvm::DenseSet<StringRef> UsedStartStop;
- std::unique_ptr<llvm::raw_fd_ostream> IndexFile;
- llvm::DenseSet<StringRef> ThinIndices;
+ std::unique_ptr<llvm::lto::LTO> ltoObj;
+ std::vector<SmallString<0>> buf;
+ std::vector<std::unique_ptr<MemoryBuffer>> files;
+ llvm::DenseSet<StringRef> usedStartStop;
+ std::unique_ptr<llvm::raw_fd_ostream> indexFile;
+ llvm::DenseSet<StringRef> thinIndices;
};
} // namespace elf
} // namespace lld
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index fbc0254162059..49e44d7804761 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -1,9 +1,8 @@
//===- LinkerScript.cpp ---------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -47,26 +46,26 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-LinkerScript *elf::Script;
+LinkerScript *elf::script;
-static uint64_t getOutputSectionVA(SectionBase *InputSec, StringRef Loc) {
- if (OutputSection *OS = InputSec->getOutputSection())
- return OS->Addr;
- error(Loc + ": unable to evaluate expression: input section " +
- InputSec->Name + " has no output section assigned");
+static uint64_t getOutputSectionVA(SectionBase *inputSec, StringRef loc) {
+ if (OutputSection *os = inputSec->getOutputSection())
+ return os->addr;
+ error(loc + ": unable to evaluate expression: input section " +
+ inputSec->name + " has no output section assigned");
return 0;
}
uint64_t ExprValue::getValue() const {
- if (Sec)
- return alignTo(Sec->getOffset(Val) + getOutputSectionVA(Sec, Loc),
- Alignment);
- return alignTo(Val, Alignment);
+ if (sec)
+ return alignTo(sec->getOffset(val) + getOutputSectionVA(sec, loc),
+ alignment);
+ return alignTo(val, alignment);
}
uint64_t ExprValue::getSecAddr() const {
- if (Sec)
- return Sec->getOffset(0) + getOutputSectionVA(Sec, Loc);
+ if (sec)
+ return sec->getOffset(0) + getOutputSectionVA(sec, loc);
return 0;
}
@@ -74,106 +73,100 @@ uint64_t ExprValue::getSectionOffset() const {
// If the alignment is trivial, we don't have to compute the full
// value to know the offset. This allows this function to succeed in
// cases where the output section is not yet known.
- if (Alignment == 1 && (!Sec || !Sec->getOutputSection()))
- return Val;
+ if (alignment == 1 && (!sec || !sec->getOutputSection()))
+ return val;
return getValue() - getSecAddr();
}
-OutputSection *LinkerScript::createOutputSection(StringRef Name,
- StringRef Location) {
- OutputSection *&SecRef = NameToOutputSection[Name];
- OutputSection *Sec;
- if (SecRef && SecRef->Location.empty()) {
+OutputSection *LinkerScript::createOutputSection(StringRef name,
+ StringRef location) {
+ OutputSection *&secRef = nameToOutputSection[name];
+ OutputSection *sec;
+ if (secRef && secRef->location.empty()) {
// There was a forward reference.
- Sec = SecRef;
+ sec = secRef;
} else {
- Sec = make<OutputSection>(Name, SHT_NOBITS, 0);
- if (!SecRef)
- SecRef = Sec;
+ sec = make<OutputSection>(name, SHT_PROGBITS, 0);
+ if (!secRef)
+ secRef = sec;
}
- Sec->Location = Location;
- return Sec;
+ sec->location = location;
+ return sec;
}
-OutputSection *LinkerScript::getOrCreateOutputSection(StringRef Name) {
- OutputSection *&CmdRef = NameToOutputSection[Name];
- if (!CmdRef)
- CmdRef = make<OutputSection>(Name, SHT_PROGBITS, 0);
- return CmdRef;
+OutputSection *LinkerScript::getOrCreateOutputSection(StringRef name) {
+ OutputSection *&cmdRef = nameToOutputSection[name];
+ if (!cmdRef)
+ cmdRef = make<OutputSection>(name, SHT_PROGBITS, 0);
+ return cmdRef;
}
// Expands the memory region by the specified size.
-static void expandMemoryRegion(MemoryRegion *MemRegion, uint64_t Size,
- StringRef RegionName, StringRef SecName) {
- MemRegion->CurPos += Size;
- uint64_t NewSize = MemRegion->CurPos - MemRegion->Origin;
- if (NewSize > MemRegion->Length)
- error("section '" + SecName + "' will not fit in region '" + RegionName +
- "': overflowed by " + Twine(NewSize - MemRegion->Length) + " bytes");
+static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size,
+ StringRef regionName, StringRef secName) {
+ memRegion->curPos += size;
+ uint64_t newSize = memRegion->curPos - memRegion->origin;
+ if (newSize > memRegion->length)
+ error("section '" + secName + "' will not fit in region '" + regionName +
+ "': overflowed by " + Twine(newSize - memRegion->length) + " bytes");
}
-void LinkerScript::expandMemoryRegions(uint64_t Size) {
- if (Ctx->MemRegion)
- expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name,
- Ctx->OutSec->Name);
- // Only expand the LMARegion if it is different from MemRegion.
- if (Ctx->LMARegion && Ctx->MemRegion != Ctx->LMARegion)
- expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name,
- Ctx->OutSec->Name);
+void LinkerScript::expandMemoryRegions(uint64_t size) {
+ if (ctx->memRegion)
+ expandMemoryRegion(ctx->memRegion, size, ctx->memRegion->name,
+ ctx->outSec->name);
+ // Only expand the LMARegion if it is different from memRegion.
+ if (ctx->lmaRegion && ctx->memRegion != ctx->lmaRegion)
+ expandMemoryRegion(ctx->lmaRegion, size, ctx->lmaRegion->name,
+ ctx->outSec->name);
}
-void LinkerScript::expandOutputSection(uint64_t Size) {
- Ctx->OutSec->Size += Size;
- expandMemoryRegions(Size);
+void LinkerScript::expandOutputSection(uint64_t size) {
+ ctx->outSec->size += size;
+ expandMemoryRegions(size);
}
-void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) {
- uint64_t Val = E().getValue();
- if (Val < Dot && InSec)
- error(Loc + ": unable to move location counter backward for: " +
- Ctx->OutSec->Name);
+void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec) {
+ uint64_t val = e().getValue();
+ if (val < dot && inSec)
+ error(loc + ": unable to move location counter backward for: " +
+ ctx->outSec->name);
// Update to location counter means update to section size.
- if (InSec)
- expandOutputSection(Val - Dot);
- else
- expandMemoryRegions(Val - Dot);
+ if (inSec)
+ expandOutputSection(val - dot);
- Dot = Val;
+ dot = val;
}
// Used for handling linker symbol assignments, for both finalizing
// their values and doing early declarations. Returns true if symbol
// should be defined from linker script.
-static bool shouldDefineSym(SymbolAssignment *Cmd) {
- if (Cmd->Name == ".")
+static bool shouldDefineSym(SymbolAssignment *cmd) {
+ if (cmd->name == ".")
return false;
- if (!Cmd->Provide)
+ if (!cmd->provide)
return true;
// If a symbol was in PROVIDE(), we need to define it only
// when it is a referenced undefined symbol.
- Symbol *B = Symtab->find(Cmd->Name);
- if (B && !B->isDefined())
+ Symbol *b = symtab->find(cmd->name);
+ if (b && !b->isDefined())
return true;
return false;
}
// This function is called from processSectionCommands,
// while we are fixing the output section layout.
-void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
- if (!shouldDefineSym(Cmd))
+void LinkerScript::addSymbol(SymbolAssignment *cmd) {
+ if (!shouldDefineSym(cmd))
return;
// Define a symbol.
- Symbol *Sym;
- uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
- std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility,
- /*CanOmitFromDynSym*/ false,
- /*File*/ nullptr);
- ExprValue Value = Cmd->Expression();
- SectionBase *Sec = Value.isAbsolute() ? nullptr : Value.Sec;
+ ExprValue value = cmd->expression();
+ SectionBase *sec = value.isAbsolute() ? nullptr : value.sec;
+ uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT;
// When this function is called, section addresses have not been
// fixed yet. So, we may or may not know the value of the RHS
@@ -186,68 +179,73 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
// We want to set symbol values early if we can. This allows us to
// use symbols as variables in linker scripts. Doing so allows us to
// write expressions like this: `alignment = 16; . = ALIGN(., alignment)`.
- uint64_t SymValue = Value.Sec ? 0 : Value.getValue();
+ uint64_t symValue = value.sec ? 0 : value.getValue();
+
+ Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, symValue,
+ 0, sec);
- replaceSymbol<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility,
- STT_NOTYPE, SymValue, 0, Sec);
- Cmd->Sym = cast<Defined>(Sym);
+ Symbol *sym = symtab->insert(cmd->name);
+ sym->mergeProperties(New);
+ sym->replace(New);
+ cmd->sym = cast<Defined>(sym);
}
// This function is called from LinkerScript::declareSymbols.
// It creates a placeholder symbol if needed.
-static void declareSymbol(SymbolAssignment *Cmd) {
- if (!shouldDefineSym(Cmd))
+static void declareSymbol(SymbolAssignment *cmd) {
+ if (!shouldDefineSym(cmd))
return;
+ uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT;
+ Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0,
+ nullptr);
+
// We can't calculate final value right now.
- Symbol *Sym;
- uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
- std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility,
- /*CanOmitFromDynSym*/ false,
- /*File*/ nullptr);
- replaceSymbol<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility,
- STT_NOTYPE, 0, 0, nullptr);
- Cmd->Sym = cast<Defined>(Sym);
- Cmd->Provide = false;
- Sym->ScriptDefined = true;
+ Symbol *sym = symtab->insert(cmd->name);
+ sym->mergeProperties(New);
+ sym->replace(New);
+
+ cmd->sym = cast<Defined>(sym);
+ cmd->provide = false;
+ sym->scriptDefined = true;
}
// This method is used to handle INSERT AFTER statement. Here we rebuild
// the list of script commands to mix sections inserted into.
void LinkerScript::processInsertCommands() {
- std::vector<BaseCommand *> V;
- auto Insert = [&](std::vector<BaseCommand *> &From) {
- V.insert(V.end(), From.begin(), From.end());
- From.clear();
+ std::vector<BaseCommand *> v;
+ auto insert = [&](std::vector<BaseCommand *> &from) {
+ v.insert(v.end(), from.begin(), from.end());
+ from.clear();
};
- for (BaseCommand *Base : SectionCommands) {
- if (auto *OS = dyn_cast<OutputSection>(Base)) {
- Insert(InsertBeforeCommands[OS->Name]);
- V.push_back(Base);
- Insert(InsertAfterCommands[OS->Name]);
+ for (BaseCommand *base : sectionCommands) {
+ if (auto *os = dyn_cast<OutputSection>(base)) {
+ insert(insertBeforeCommands[os->name]);
+ v.push_back(base);
+ insert(insertAfterCommands[os->name]);
continue;
}
- V.push_back(Base);
+ v.push_back(base);
}
- for (auto &Cmds : {InsertBeforeCommands, InsertAfterCommands})
- for (const std::pair<StringRef, std::vector<BaseCommand *>> &P : Cmds)
- if (!P.second.empty())
- error("unable to INSERT AFTER/BEFORE " + P.first +
+ for (auto &cmds : {insertBeforeCommands, insertAfterCommands})
+ for (const std::pair<StringRef, std::vector<BaseCommand *>> &p : cmds)
+ if (!p.second.empty())
+ error("unable to INSERT AFTER/BEFORE " + p.first +
": section not defined");
- SectionCommands = std::move(V);
+ sectionCommands = std::move(v);
}
// Symbols defined in script should not be inlined by LTO. At the same time
// we don't know their final values until late stages of link. Here we scan
// over symbol assignment commands and create placeholder symbols if needed.
void LinkerScript::declareSymbols() {
- assert(!Ctx);
- for (BaseCommand *Base : SectionCommands) {
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- declareSymbol(Cmd);
+ assert(!ctx);
+ for (BaseCommand *base : sectionCommands) {
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ declareSymbol(cmd);
continue;
}
@@ -255,75 +253,75 @@ void LinkerScript::declareSymbols() {
// we can't say for sure if it is going to be included or not.
// Skip such sections for now. Improve the checks if we ever
// need symbols from that sections to be declared early.
- auto *Sec = cast<OutputSection>(Base);
- if (Sec->Constraint != ConstraintKind::NoConstraint)
+ auto *sec = cast<OutputSection>(base);
+ if (sec->constraint != ConstraintKind::NoConstraint)
continue;
- for (BaseCommand *Base2 : Sec->SectionCommands)
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base2))
- declareSymbol(Cmd);
+ for (BaseCommand *base2 : sec->sectionCommands)
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base2))
+ declareSymbol(cmd);
}
}
// This function is called from assignAddresses, while we are
// fixing the output section addresses. This function is supposed
// to set the final value for a given symbol assignment.
-void LinkerScript::assignSymbol(SymbolAssignment *Cmd, bool InSec) {
- if (Cmd->Name == ".") {
- setDot(Cmd->Expression, Cmd->Location, InSec);
+void LinkerScript::assignSymbol(SymbolAssignment *cmd, bool inSec) {
+ if (cmd->name == ".") {
+ setDot(cmd->expression, cmd->location, inSec);
return;
}
- if (!Cmd->Sym)
+ if (!cmd->sym)
return;
- ExprValue V = Cmd->Expression();
- if (V.isAbsolute()) {
- Cmd->Sym->Section = nullptr;
- Cmd->Sym->Value = V.getValue();
+ ExprValue v = cmd->expression();
+ if (v.isAbsolute()) {
+ cmd->sym->section = nullptr;
+ cmd->sym->value = v.getValue();
} else {
- Cmd->Sym->Section = V.Sec;
- Cmd->Sym->Value = V.getSectionOffset();
+ cmd->sym->section = v.sec;
+ cmd->sym->value = v.getSectionOffset();
}
}
-static std::string getFilename(InputFile *File) {
- if (!File)
+static std::string getFilename(InputFile *file) {
+ if (!file)
return "";
- if (File->ArchiveName.empty())
- return File->getName();
- return (File->ArchiveName + "(" + File->getName() + ")").str();
+ if (file->archiveName.empty())
+ return file->getName();
+ return (file->archiveName + "(" + file->getName() + ")").str();
}
-bool LinkerScript::shouldKeep(InputSectionBase *S) {
- if (KeptSections.empty())
+bool LinkerScript::shouldKeep(InputSectionBase *s) {
+ if (keptSections.empty())
return false;
- std::string Filename = getFilename(S->File);
- for (InputSectionDescription *ID : KeptSections)
- if (ID->FilePat.match(Filename))
- for (SectionPattern &P : ID->SectionPatterns)
- if (P.SectionPat.match(S->Name))
+ std::string filename = getFilename(s->file);
+ for (InputSectionDescription *id : keptSections)
+ if (id->filePat.match(filename))
+ for (SectionPattern &p : id->sectionPatterns)
+ if (p.sectionPat.match(s->name))
return true;
return false;
}
// A helper function for the SORT() command.
static std::function<bool(InputSectionBase *, InputSectionBase *)>
-getComparator(SortSectionPolicy K) {
- switch (K) {
+getComparator(SortSectionPolicy k) {
+ switch (k) {
case SortSectionPolicy::Alignment:
- return [](InputSectionBase *A, InputSectionBase *B) {
+ return [](InputSectionBase *a, InputSectionBase *b) {
// ">" is not a mistake. Sections with larger alignments are placed
// before sections with smaller alignments in order to reduce the
// amount of padding necessary. This is compatible with GNU.
- return A->Alignment > B->Alignment;
+ return a->alignment > b->alignment;
};
case SortSectionPolicy::Name:
- return [](InputSectionBase *A, InputSectionBase *B) {
- return A->Name < B->Name;
+ return [](InputSectionBase *a, InputSectionBase *b) {
+ return a->name < b->name;
};
case SortSectionPolicy::Priority:
- return [](InputSectionBase *A, InputSectionBase *B) {
- return getPriority(A->Name) < getPriority(B->Name);
+ return [](InputSectionBase *a, InputSectionBase *b) {
+ return getPriority(a->name) < getPriority(b->name);
};
default:
llvm_unreachable("unknown sort policy");
@@ -331,22 +329,22 @@ getComparator(SortSectionPolicy K) {
}
// A helper function for the SORT() command.
-static bool matchConstraints(ArrayRef<InputSection *> Sections,
- ConstraintKind Kind) {
- if (Kind == ConstraintKind::NoConstraint)
+static bool matchConstraints(ArrayRef<InputSection *> sections,
+ ConstraintKind kind) {
+ if (kind == ConstraintKind::NoConstraint)
return true;
- bool IsRW = llvm::any_of(
- Sections, [](InputSection *Sec) { return Sec->Flags & SHF_WRITE; });
+ bool isRW = llvm::any_of(
+ sections, [](InputSection *sec) { return sec->flags & SHF_WRITE; });
- return (IsRW && Kind == ConstraintKind::ReadWrite) ||
- (!IsRW && Kind == ConstraintKind::ReadOnly);
+ return (isRW && kind == ConstraintKind::ReadWrite) ||
+ (!isRW && kind == ConstraintKind::ReadOnly);
}
-static void sortSections(MutableArrayRef<InputSection *> Vec,
- SortSectionPolicy K) {
- if (K != SortSectionPolicy::Default && K != SortSectionPolicy::None)
- std::stable_sort(Vec.begin(), Vec.end(), getComparator(K));
+static void sortSections(MutableArrayRef<InputSection *> vec,
+ SortSectionPolicy k) {
+ if (k != SortSectionPolicy::Default && k != SortSectionPolicy::None)
+ llvm::stable_sort(vec, getComparator(k));
}
// Sort sections as instructed by SORT-family commands and --sort-section
@@ -360,29 +358,29 @@ static void sortSections(MutableArrayRef<InputSection *> Vec,
// --sort-section is handled as an inner SORT command.
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
// 4. If no SORT command is given, sort according to --sort-section.
-static void sortInputSections(MutableArrayRef<InputSection *> Vec,
- const SectionPattern &Pat) {
- if (Pat.SortOuter == SortSectionPolicy::None)
+static void sortInputSections(MutableArrayRef<InputSection *> vec,
+ const SectionPattern &pat) {
+ if (pat.sortOuter == SortSectionPolicy::None)
return;
- if (Pat.SortInner == SortSectionPolicy::Default)
- sortSections(Vec, Config->SortSection);
+ if (pat.sortInner == SortSectionPolicy::Default)
+ sortSections(vec, config->sortSection);
else
- sortSections(Vec, Pat.SortInner);
- sortSections(Vec, Pat.SortOuter);
+ sortSections(vec, pat.sortInner);
+ sortSections(vec, pat.sortOuter);
}
// Compute and remember which sections the InputSectionDescription matches.
std::vector<InputSection *>
-LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
- std::vector<InputSection *> Ret;
+LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
+ std::vector<InputSection *> ret;
// Collects all sections that satisfy constraints of Cmd.
- for (const SectionPattern &Pat : Cmd->SectionPatterns) {
- size_t SizeBefore = Ret.size();
+ for (const SectionPattern &pat : cmd->sectionPatterns) {
+ size_t sizeBefore = ret.size();
- for (InputSectionBase *Sec : InputSections) {
- if (!Sec->Live || Sec->Assigned)
+ for (InputSectionBase *sec : inputSections) {
+ if (!sec->isLive() || sec->assigned)
continue;
// For -emit-relocs we have to ignore entries like
@@ -390,59 +388,59 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
// which are common because they are in the default bfd script.
// We do not ignore SHT_REL[A] linker-synthesized sections here because
// want to support scripts that do custom layout for them.
- if (auto *IS = dyn_cast<InputSection>(Sec))
- if (IS->getRelocatedSection())
+ if (auto *isec = dyn_cast<InputSection>(sec))
+ if (isec->getRelocatedSection())
continue;
- std::string Filename = getFilename(Sec->File);
- if (!Cmd->FilePat.match(Filename) ||
- Pat.ExcludedFilePat.match(Filename) ||
- !Pat.SectionPat.match(Sec->Name))
+ std::string filename = getFilename(sec->file);
+ if (!cmd->filePat.match(filename) ||
+ pat.excludedFilePat.match(filename) ||
+ !pat.sectionPat.match(sec->name))
continue;
// It is safe to assume that Sec is an InputSection
// because mergeable or EH input sections have already been
// handled and eliminated.
- Ret.push_back(cast<InputSection>(Sec));
- Sec->Assigned = true;
+ ret.push_back(cast<InputSection>(sec));
+ sec->assigned = true;
}
- sortInputSections(MutableArrayRef<InputSection *>(Ret).slice(SizeBefore),
- Pat);
+ sortInputSections(MutableArrayRef<InputSection *>(ret).slice(sizeBefore),
+ pat);
}
- return Ret;
+ return ret;
}
-void LinkerScript::discard(ArrayRef<InputSection *> V) {
- for (InputSection *S : V) {
- if (S == In.ShStrTab || S == In.RelaDyn || S == In.RelrDyn)
- error("discarding " + S->Name + " section is not allowed");
+void LinkerScript::discard(ArrayRef<InputSection *> v) {
+ for (InputSection *s : v) {
+ if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
+ error("discarding " + s->name + " section is not allowed");
// You can discard .hash and .gnu.hash sections by linker scripts. Since
// they are synthesized sections, we need to handle them differently than
// other regular sections.
- if (S == In.GnuHashTab)
- In.GnuHashTab = nullptr;
- if (S == In.HashTab)
- In.HashTab = nullptr;
-
- S->Assigned = false;
- S->Live = false;
- discard(S->DependentSections);
+ if (s == mainPart->gnuHashTab)
+ mainPart->gnuHashTab = nullptr;
+ if (s == mainPart->hashTab)
+ mainPart->hashTab = nullptr;
+
+ s->assigned = false;
+ s->markDead();
+ discard(s->dependentSections);
}
}
std::vector<InputSection *>
-LinkerScript::createInputSectionList(OutputSection &OutCmd) {
- std::vector<InputSection *> Ret;
+LinkerScript::createInputSectionList(OutputSection &outCmd) {
+ std::vector<InputSection *> ret;
- for (BaseCommand *Base : OutCmd.SectionCommands) {
- if (auto *Cmd = dyn_cast<InputSectionDescription>(Base)) {
- Cmd->Sections = computeInputSections(Cmd);
- Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end());
+ for (BaseCommand *base : outCmd.sectionCommands) {
+ if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
+ cmd->sections = computeInputSections(cmd);
+ ret.insert(ret.end(), cmd->sections.begin(), cmd->sections.end());
}
}
- return Ret;
+ return ret;
}
void LinkerScript::processSectionCommands() {
@@ -455,34 +453,34 @@ void LinkerScript::processSectionCommands() {
// To handle that, create a dummy aether section that fills the void before
// the linker scripts switches to another section. It has an index of one
// which will map to whatever the first actual section is.
- Aether = make<OutputSection>("", 0, SHF_ALLOC);
- Aether->SectionIndex = 1;
+ aether = make<OutputSection>("", 0, SHF_ALLOC);
+ aether->sectionIndex = 1;
// Ctx captures the local AddressState and makes it accessible deliberately.
// This is needed as there are some cases where we cannot just
// thread the current state through to a lambda function created by the
// script parser.
- auto Deleter = make_unique<AddressState>();
- Ctx = Deleter.get();
- Ctx->OutSec = Aether;
+ auto deleter = make_unique<AddressState>();
+ ctx = deleter.get();
+ ctx->outSec = aether;
- size_t I = 0;
+ size_t i = 0;
// Add input sections to output sections.
- for (BaseCommand *Base : SectionCommands) {
+ for (BaseCommand *base : sectionCommands) {
// Handle symbol assignments outside of any output section.
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- addSymbol(Cmd);
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ addSymbol(cmd);
continue;
}
- if (auto *Sec = dyn_cast<OutputSection>(Base)) {
- std::vector<InputSection *> V = createInputSectionList(*Sec);
+ if (auto *sec = dyn_cast<OutputSection>(base)) {
+ std::vector<InputSection *> v = createInputSectionList(*sec);
// The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded.
- if (Sec->Name == "/DISCARD/") {
- discard(V);
- Sec->SectionCommands.clear();
+ if (sec->name == "/DISCARD/") {
+ discard(v);
+ sec->sectionCommands.clear();
continue;
}
@@ -493,60 +491,61 @@ void LinkerScript::processSectionCommands() {
//
// Because we'll iterate over SectionCommands many more times, the easy
// way to "make it as if it wasn't present" is to make it empty.
- if (!matchConstraints(V, Sec->Constraint)) {
- for (InputSectionBase *S : V)
- S->Assigned = false;
- Sec->SectionCommands.clear();
+ if (!matchConstraints(v, sec->constraint)) {
+ for (InputSectionBase *s : v)
+ s->assigned = false;
+ sec->sectionCommands.clear();
continue;
}
// A directive may contain symbol definitions like this:
// ".foo : { ...; bar = .; }". Handle them.
- for (BaseCommand *Base : Sec->SectionCommands)
- if (auto *OutCmd = dyn_cast<SymbolAssignment>(Base))
- addSymbol(OutCmd);
+ for (BaseCommand *base : sec->sectionCommands)
+ if (auto *outCmd = dyn_cast<SymbolAssignment>(base))
+ addSymbol(outCmd);
// Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
// is given, input sections are aligned to that value, whether the
// given value is larger or smaller than the original section alignment.
- if (Sec->SubalignExpr) {
- uint32_t Subalign = Sec->SubalignExpr().getValue();
- for (InputSectionBase *S : V)
- S->Alignment = Subalign;
+ if (sec->subalignExpr) {
+ uint32_t subalign = sec->subalignExpr().getValue();
+ for (InputSectionBase *s : v)
+ s->alignment = subalign;
}
// Add input sections to an output section.
- for (InputSection *S : V)
- Sec->addSection(S);
-
- Sec->SectionIndex = I++;
- if (Sec->Noload)
- Sec->Type = SHT_NOBITS;
- if (Sec->NonAlloc)
- Sec->Flags &= ~(uint64_t)SHF_ALLOC;
+ for (InputSection *s : v)
+ sec->addSection(s);
+
+ sec->sectionIndex = i++;
+ if (sec->noload)
+ sec->type = SHT_NOBITS;
+ if (sec->nonAlloc)
+ sec->flags &= ~(uint64_t)SHF_ALLOC;
}
}
- Ctx = nullptr;
+ ctx = nullptr;
}
-static OutputSection *findByName(ArrayRef<BaseCommand *> Vec,
- StringRef Name) {
- for (BaseCommand *Base : Vec)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- if (Sec->Name == Name)
- return Sec;
+static OutputSection *findByName(ArrayRef<BaseCommand *> vec,
+ StringRef name) {
+ for (BaseCommand *base : vec)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ if (sec->name == name)
+ return sec;
return nullptr;
}
-static OutputSection *createSection(InputSectionBase *IS,
- StringRef OutsecName) {
- OutputSection *Sec = Script->createOutputSection(OutsecName, "<internal>");
- Sec->addSection(cast<InputSection>(IS));
- return Sec;
+static OutputSection *createSection(InputSectionBase *isec,
+ StringRef outsecName) {
+ OutputSection *sec = script->createOutputSection(outsecName, "<internal>");
+ sec->addSection(cast<InputSection>(isec));
+ return sec;
}
-static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
- InputSectionBase *IS, StringRef OutsecName) {
+static OutputSection *
+addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
+ InputSectionBase *isec, StringRef outsecName) {
// Sections with SHT_GROUP or SHF_GROUP attributes reach here only when the -r
// option is given. A section with SHT_GROUP defines a "section group", and
// its members have SHF_GROUP attribute. Usually these flags have already been
@@ -554,8 +553,8 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
// However, for the -r option, we want to pass through all section groups
// as-is because adding/removing members or merging them with other groups
// change their semantics.
- if (IS->Type == SHT_GROUP || (IS->Flags & SHF_GROUP))
- return createSection(IS, OutsecName);
+ if (isec->type == SHT_GROUP || (isec->flags & SHF_GROUP))
+ return createSection(isec, outsecName);
// Imagine .zed : { *(.foo) *(.bar) } script. Both foo and bar may have
// relocation sections .rela.foo and .rela.bar for example. Most tools do
@@ -563,25 +562,25 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
// should combine these relocation sections into single output.
// We skip synthetic sections because it can be .rela.dyn/.rela.plt or any
// other REL[A] sections created by linker itself.
- if (!isa<SyntheticSection>(IS) &&
- (IS->Type == SHT_REL || IS->Type == SHT_RELA)) {
- auto *Sec = cast<InputSection>(IS);
- OutputSection *Out = Sec->getRelocatedSection()->getOutputSection();
+ if (!isa<SyntheticSection>(isec) &&
+ (isec->type == SHT_REL || isec->type == SHT_RELA)) {
+ auto *sec = cast<InputSection>(isec);
+ OutputSection *out = sec->getRelocatedSection()->getOutputSection();
- if (Out->RelocationSection) {
- Out->RelocationSection->addSection(Sec);
+ if (out->relocationSection) {
+ out->relocationSection->addSection(sec);
return nullptr;
}
- Out->RelocationSection = createSection(IS, OutsecName);
- return Out->RelocationSection;
+ out->relocationSection = createSection(isec, outsecName);
+ return out->relocationSection;
}
// When control reaches here, mergeable sections have already been merged into
// synthetic sections. For relocatable case we want to create one output
// section per syntetic section so that they have a valid sh_entsize.
- if (Config->Relocatable && (IS->Flags & SHF_MERGE))
- return createSection(IS, OutsecName);
+ if (config->relocatable && (isec->flags & SHF_MERGE))
+ return createSection(isec, outsecName);
// The ELF spec just says
// ----------------------------------------------------------------
@@ -625,159 +624,161 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
//
// Given the above issues, we instead merge sections by name and error on
// incompatible types and flags.
- OutputSection *&Sec = Map[OutsecName];
- if (Sec) {
- Sec->addSection(cast<InputSection>(IS));
+ TinyPtrVector<OutputSection *> &v = map[outsecName];
+ for (OutputSection *sec : v) {
+ if (sec->partition != isec->partition)
+ continue;
+ sec->addSection(cast<InputSection>(isec));
return nullptr;
}
- Sec = createSection(IS, OutsecName);
- return Sec;
+ OutputSection *sec = createSection(isec, outsecName);
+ v.push_back(sec);
+ return sec;
}
// Add sections that didn't match any sections command.
void LinkerScript::addOrphanSections() {
- unsigned End = SectionCommands.size();
- StringMap<OutputSection *> Map;
- std::vector<OutputSection *> V;
+ StringMap<TinyPtrVector<OutputSection *>> map;
+ std::vector<OutputSection *> v;
- auto Add = [&](InputSectionBase *S) {
- if (!S->Live || S->Parent)
+ auto add = [&](InputSectionBase *s) {
+ if (!s->isLive() || s->parent)
return;
- StringRef Name = getOutputSectionName(S);
+ StringRef name = getOutputSectionName(s);
- if (Config->OrphanHandling == OrphanHandlingPolicy::Error)
- error(toString(S) + " is being placed in '" + Name + "'");
- else if (Config->OrphanHandling == OrphanHandlingPolicy::Warn)
- warn(toString(S) + " is being placed in '" + Name + "'");
+ if (config->orphanHandling == OrphanHandlingPolicy::Error)
+ error(toString(s) + " is being placed in '" + name + "'");
+ else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
+ warn(toString(s) + " is being placed in '" + name + "'");
- if (OutputSection *Sec =
- findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) {
- Sec->addSection(cast<InputSection>(S));
+ if (OutputSection *sec = findByName(sectionCommands, name)) {
+ sec->addSection(cast<InputSection>(s));
return;
}
- if (OutputSection *OS = addInputSec(Map, S, Name))
- V.push_back(OS);
- assert(S->getOutputSection()->SectionIndex == UINT32_MAX);
+ if (OutputSection *os = addInputSec(map, s, name))
+ v.push_back(os);
+ assert(s->getOutputSection()->sectionIndex == UINT32_MAX);
};
// For futher --emit-reloc handling code we need target output section
// to be created before we create relocation output section, so we want
// to create target sections first. We do not want priority handling
// for synthetic sections because them are special.
- for (InputSectionBase *IS : InputSections) {
- if (auto *Sec = dyn_cast<InputSection>(IS))
- if (InputSectionBase *Rel = Sec->getRelocatedSection())
- if (auto *RelIS = dyn_cast_or_null<InputSectionBase>(Rel->Parent))
- Add(RelIS);
- Add(IS);
+ for (InputSectionBase *isec : inputSections) {
+ if (auto *sec = dyn_cast<InputSection>(isec))
+ if (InputSectionBase *rel = sec->getRelocatedSection())
+ if (auto *relIS = dyn_cast_or_null<InputSectionBase>(rel->parent))
+ add(relIS);
+ add(isec);
}
// If no SECTIONS command was given, we should insert sections commands
// before others, so that we can handle scripts which refers them,
// for example: "foo = ABSOLUTE(ADDR(.text)));".
// When SECTIONS command is present we just add all orphans to the end.
- if (HasSectionsCommand)
- SectionCommands.insert(SectionCommands.end(), V.begin(), V.end());
+ if (hasSectionsCommand)
+ sectionCommands.insert(sectionCommands.end(), v.begin(), v.end());
else
- SectionCommands.insert(SectionCommands.begin(), V.begin(), V.end());
+ sectionCommands.insert(sectionCommands.begin(), v.begin(), v.end());
}
-uint64_t LinkerScript::advance(uint64_t Size, unsigned Alignment) {
- bool IsTbss =
- (Ctx->OutSec->Flags & SHF_TLS) && Ctx->OutSec->Type == SHT_NOBITS;
- uint64_t Start = IsTbss ? Dot + Ctx->ThreadBssOffset : Dot;
- Start = alignTo(Start, Alignment);
- uint64_t End = Start + Size;
+uint64_t LinkerScript::advance(uint64_t size, unsigned alignment) {
+ bool isTbss =
+ (ctx->outSec->flags & SHF_TLS) && ctx->outSec->type == SHT_NOBITS;
+ uint64_t start = isTbss ? dot + ctx->threadBssOffset : dot;
+ start = alignTo(start, alignment);
+ uint64_t end = start + size;
- if (IsTbss)
- Ctx->ThreadBssOffset = End - Dot;
+ if (isTbss)
+ ctx->threadBssOffset = end - dot;
else
- Dot = End;
- return End;
+ dot = end;
+ return end;
}
-void LinkerScript::output(InputSection *S) {
- assert(Ctx->OutSec == S->getParent());
- uint64_t Before = advance(0, 1);
- uint64_t Pos = advance(S->getSize(), S->Alignment);
- S->OutSecOff = Pos - S->getSize() - Ctx->OutSec->Addr;
+void LinkerScript::output(InputSection *s) {
+ assert(ctx->outSec == s->getParent());
+ uint64_t before = advance(0, 1);
+ uint64_t pos = advance(s->getSize(), s->alignment);
+ s->outSecOff = pos - s->getSize() - ctx->outSec->addr;
// Update output section size after adding each section. This is so that
// SIZEOF works correctly in the case below:
// .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) }
- expandOutputSection(Pos - Before);
+ expandOutputSection(pos - before);
}
-void LinkerScript::switchTo(OutputSection *Sec) {
- Ctx->OutSec = Sec;
+void LinkerScript::switchTo(OutputSection *sec) {
+ ctx->outSec = sec;
- uint64_t Before = advance(0, 1);
- Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment);
- expandMemoryRegions(Ctx->OutSec->Addr - Before);
+ uint64_t before = advance(0, 1);
+ ctx->outSec->addr = advance(0, ctx->outSec->alignment);
+ expandMemoryRegions(ctx->outSec->addr - before);
}
// This function searches for a memory region to place the given output
// section in. If found, a pointer to the appropriate memory region is
// returned. Otherwise, a nullptr is returned.
-MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) {
+MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *sec) {
// If a memory region name was specified in the output section command,
// then try to find that region first.
- if (!Sec->MemoryRegionName.empty()) {
- if (MemoryRegion *M = MemoryRegions.lookup(Sec->MemoryRegionName))
- return M;
- error("memory region '" + Sec->MemoryRegionName + "' not declared");
+ if (!sec->memoryRegionName.empty()) {
+ if (MemoryRegion *m = memoryRegions.lookup(sec->memoryRegionName))
+ return m;
+ error("memory region '" + sec->memoryRegionName + "' not declared");
return nullptr;
}
// If at least one memory region is defined, all sections must
// belong to some memory region. Otherwise, we don't need to do
// anything for memory regions.
- if (MemoryRegions.empty())
+ if (memoryRegions.empty())
return nullptr;
// See if a region can be found by matching section flags.
- for (auto &Pair : MemoryRegions) {
- MemoryRegion *M = Pair.second;
- if ((M->Flags & Sec->Flags) && (M->NegFlags & Sec->Flags) == 0)
- return M;
+ for (auto &pair : memoryRegions) {
+ MemoryRegion *m = pair.second;
+ if ((m->flags & sec->flags) && (m->negFlags & sec->flags) == 0)
+ return m;
}
// Otherwise, no suitable region was found.
- if (Sec->Flags & SHF_ALLOC)
- error("no memory region specified for section '" + Sec->Name + "'");
+ if (sec->flags & SHF_ALLOC)
+ error("no memory region specified for section '" + sec->name + "'");
return nullptr;
}
-static OutputSection *findFirstSection(PhdrEntry *Load) {
- for (OutputSection *Sec : OutputSections)
- if (Sec->PtLoad == Load)
- return Sec;
+static OutputSection *findFirstSection(PhdrEntry *load) {
+ for (OutputSection *sec : outputSections)
+ if (sec->ptLoad == load)
+ return sec;
return nullptr;
}
// This function assigns offsets to input sections and an output section
// for a single sections command (e.g. ".text { *(.text); }").
-void LinkerScript::assignOffsets(OutputSection *Sec) {
- if (!(Sec->Flags & SHF_ALLOC))
- Dot = 0;
- else if (Sec->AddrExpr)
- setDot(Sec->AddrExpr, Sec->Location, false);
+void LinkerScript::assignOffsets(OutputSection *sec) {
+ if (!(sec->flags & SHF_ALLOC))
+ dot = 0;
- Ctx->MemRegion = Sec->MemRegion;
- Ctx->LMARegion = Sec->LMARegion;
- if (Ctx->MemRegion)
- Dot = Ctx->MemRegion->CurPos;
+ ctx->memRegion = sec->memRegion;
+ ctx->lmaRegion = sec->lmaRegion;
+ if (ctx->memRegion)
+ dot = ctx->memRegion->curPos;
- switchTo(Sec);
+ if ((sec->flags & SHF_ALLOC) && sec->addrExpr)
+ setDot(sec->addrExpr, sec->location, false);
- if (Sec->LMAExpr)
- Ctx->LMAOffset = Sec->LMAExpr().getValue() - Dot;
+ switchTo(sec);
- if (MemoryRegion *MR = Sec->LMARegion)
- Ctx->LMAOffset = MR->CurPos - Dot;
+ if (sec->lmaExpr)
+ ctx->lmaOffset = sec->lmaExpr().getValue() - dot;
+
+ if (MemoryRegion *mr = sec->lmaRegion)
+ ctx->lmaOffset = mr->curPos - dot;
// If neither AT nor AT> is specified for an allocatable section, the linker
// will set the LMA such that the difference between VMA and LMA for the
@@ -785,62 +786,71 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
// https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
// This, however, should only be done by the first "non-header" section
// in the segment.
- if (PhdrEntry *L = Ctx->OutSec->PtLoad)
- if (Sec == findFirstSection(L))
- L->LMAOffset = Ctx->LMAOffset;
+ if (PhdrEntry *l = ctx->outSec->ptLoad)
+ if (sec == findFirstSection(l))
+ l->lmaOffset = ctx->lmaOffset;
// We can call this method multiple times during the creation of
// thunks and want to start over calculation each time.
- Sec->Size = 0;
+ sec->size = 0;
// We visited SectionsCommands from processSectionCommands to
// layout sections. Now, we visit SectionsCommands again to fix
// section offsets.
- for (BaseCommand *Base : Sec->SectionCommands) {
+ for (BaseCommand *base : sec->sectionCommands) {
// This handles the assignments to symbol or to the dot.
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- Cmd->Addr = Dot;
- assignSymbol(Cmd, true);
- Cmd->Size = Dot - Cmd->Addr;
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ cmd->addr = dot;
+ assignSymbol(cmd, true);
+ cmd->size = dot - cmd->addr;
continue;
}
// Handle BYTE(), SHORT(), LONG(), or QUAD().
- if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
- Cmd->Offset = Dot - Ctx->OutSec->Addr;
- Dot += Cmd->Size;
- expandOutputSection(Cmd->Size);
+ if (auto *cmd = dyn_cast<ByteCommand>(base)) {
+ cmd->offset = dot - ctx->outSec->addr;
+ dot += cmd->size;
+ expandOutputSection(cmd->size);
continue;
}
// Handle a single input section description command.
// It calculates and assigns the offsets for each section and also
// updates the output section size.
- for (InputSection *Sec : cast<InputSectionDescription>(Base)->Sections)
- output(Sec);
+ for (InputSection *sec : cast<InputSectionDescription>(base)->sections)
+ output(sec);
}
}
-static bool isDiscardable(OutputSection &Sec) {
+static bool isDiscardable(OutputSection &sec) {
+ if (sec.name == "/DISCARD/")
+ return true;
+
// We do not remove empty sections that are explicitly
// assigned to any segment.
- if (!Sec.Phdrs.empty())
+ if (!sec.phdrs.empty())
return false;
- // We do not want to remove sections that reference symbols in address and
- // other expressions. We add script symbols as undefined, and want to ensure
- // all of them are defined in the output, hence have to keep them.
- if (Sec.ExpressionsUseSymbols)
+ // We do not want to remove OutputSections with expressions that reference
+ // symbols even if the OutputSection is empty. We want to ensure that the
+ // expressions can be evaluated and report an error if they cannot.
+ if (sec.expressionsUseSymbols)
return false;
- for (BaseCommand *Base : Sec.SectionCommands) {
- if (auto Cmd = dyn_cast<SymbolAssignment>(Base))
+ // OutputSections may be referenced by name in ADDR and LOADADDR expressions,
+ // as an empty Section can has a valid VMA and LMA we keep the OutputSection
+ // to maintain the integrity of the other Expression.
+ if (sec.usedInExpression)
+ return false;
+
+ for (BaseCommand *base : sec.sectionCommands) {
+ if (auto cmd = dyn_cast<SymbolAssignment>(base))
// Don't create empty output sections just for unreferenced PROVIDE
// symbols.
- if (Cmd->Name != "." && !Cmd->Sym)
+ if (cmd->name != "." && !cmd->sym)
continue;
- if (!isa<InputSectionDescription>(*Base))
+ if (!isa<InputSectionDescription>(*base))
return false;
}
return true;
@@ -867,33 +877,35 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// The other option is to pick flags that minimize the impact the section
// will have on the rest of the linker. That is why we copy the flags from
// the previous sections. Only a few flags are needed to keep the impact low.
- uint64_t Flags = SHF_ALLOC;
+ uint64_t flags = SHF_ALLOC;
- for (BaseCommand *&Cmd : SectionCommands) {
- auto *Sec = dyn_cast<OutputSection>(Cmd);
- if (!Sec)
+ for (BaseCommand *&cmd : sectionCommands) {
+ auto *sec = dyn_cast<OutputSection>(cmd);
+ if (!sec)
continue;
// Handle align (e.g. ".foo : ALIGN(16) { ... }").
- if (Sec->AlignExpr)
- Sec->Alignment =
- std::max<uint32_t>(Sec->Alignment, Sec->AlignExpr().getValue());
+ if (sec->alignExpr)
+ sec->alignment =
+ std::max<uint32_t>(sec->alignment, sec->alignExpr().getValue());
- // A live output section means that some input section was added to it. It
- // might have been removed (if it was empty synthetic section), but we at
- // least know the flags.
- if (Sec->Live)
- Flags = Sec->Flags;
+ // The input section might have been removed (if it was an empty synthetic
+ // section), but we at least know the flags.
+ if (sec->hasInputSections)
+ flags = sec->flags;
// We do not want to keep any special flags for output section
// in case it is empty.
- bool IsEmpty = getInputSections(Sec).empty();
- if (IsEmpty)
- Sec->Flags = Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR);
-
- if (IsEmpty && isDiscardable(*Sec)) {
- Sec->Live = false;
- Cmd = nullptr;
+ bool isEmpty = getInputSections(sec).empty();
+ if (isEmpty)
+ sec->flags = flags & ((sec->nonAlloc ? 0 : (uint64_t)SHF_ALLOC) |
+ SHF_WRITE | SHF_EXECINSTR);
+
+ if (isEmpty && isDiscardable(*sec)) {
+ sec->markDead();
+ cmd = nullptr;
+ } else if (!sec->isLive()) {
+ sec->markLive();
}
}
@@ -903,20 +915,20 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// clutter the output.
// We instead remove trivially empty sections. The bfd linker seems even
// more aggressive at removing them.
- llvm::erase_if(SectionCommands, [&](BaseCommand *Base) { return !Base; });
+ llvm::erase_if(sectionCommands, [&](BaseCommand *base) { return !base; });
}
void LinkerScript::adjustSectionsAfterSorting() {
// Try and find an appropriate memory region to assign offsets in.
- for (BaseCommand *Base : SectionCommands) {
- if (auto *Sec = dyn_cast<OutputSection>(Base)) {
- if (!Sec->LMARegionName.empty()) {
- if (MemoryRegion *M = MemoryRegions.lookup(Sec->LMARegionName))
- Sec->LMARegion = M;
+ for (BaseCommand *base : sectionCommands) {
+ if (auto *sec = dyn_cast<OutputSection>(base)) {
+ if (!sec->lmaRegionName.empty()) {
+ if (MemoryRegion *m = memoryRegions.lookup(sec->lmaRegionName))
+ sec->lmaRegion = m;
else
- error("memory region '" + Sec->LMARegionName + "' not declared");
+ error("memory region '" + sec->lmaRegionName + "' not declared");
}
- Sec->MemRegion = findMemoryRegion(Sec);
+ sec->memRegion = findMemoryRegion(sec);
}
}
@@ -926,38 +938,38 @@ void LinkerScript::adjustSectionsAfterSorting() {
// Below is an example of such linker script:
// PHDRS { seg PT_LOAD; }
// SECTIONS { .aaa : { *(.aaa) } }
- std::vector<StringRef> DefPhdrs;
- auto FirstPtLoad = llvm::find_if(PhdrsCommands, [](const PhdrsCommand &Cmd) {
- return Cmd.Type == PT_LOAD;
+ std::vector<StringRef> defPhdrs;
+ auto firstPtLoad = llvm::find_if(phdrsCommands, [](const PhdrsCommand &cmd) {
+ return cmd.type == PT_LOAD;
});
- if (FirstPtLoad != PhdrsCommands.end())
- DefPhdrs.push_back(FirstPtLoad->Name);
+ if (firstPtLoad != phdrsCommands.end())
+ defPhdrs.push_back(firstPtLoad->name);
// Walk the commands and propagate the program headers to commands that don't
// explicitly specify them.
- for (BaseCommand *Base : SectionCommands) {
- auto *Sec = dyn_cast<OutputSection>(Base);
- if (!Sec)
+ for (BaseCommand *base : sectionCommands) {
+ auto *sec = dyn_cast<OutputSection>(base);
+ if (!sec)
continue;
- if (Sec->Phdrs.empty()) {
+ if (sec->phdrs.empty()) {
// To match the bfd linker script behaviour, only propagate program
// headers to sections that are allocated.
- if (Sec->Flags & SHF_ALLOC)
- Sec->Phdrs = DefPhdrs;
+ if (sec->flags & SHF_ALLOC)
+ sec->phdrs = defPhdrs;
} else {
- DefPhdrs = Sec->Phdrs;
+ defPhdrs = sec->phdrs;
}
}
}
-static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) {
+static uint64_t computeBase(uint64_t min, bool allocateHeaders) {
// If there is no SECTIONS or if the linkerscript is explicit about program
// headers, do our best to allocate them.
- if (!Script->HasSectionsCommand || AllocateHeaders)
+ if (!script->hasSectionsCommand || allocateHeaders)
return 0;
// Otherwise only allocate program headers if that would not add a page.
- return alignDown(Min, Config->MaxPageSize);
+ return alignDown(min, config->maxPageSize);
}
// Try to find an address for the file and program headers output sections,
@@ -971,118 +983,120 @@ static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) {
//
// If there isn't enough space for these sections, we'll remove them from the
// PT_LOAD segment, and we'll also remove the PT_PHDR segment.
-void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) {
- uint64_t Min = std::numeric_limits<uint64_t>::max();
- for (OutputSection *Sec : OutputSections)
- if (Sec->Flags & SHF_ALLOC)
- Min = std::min<uint64_t>(Min, Sec->Addr);
-
- auto It = llvm::find_if(
- Phdrs, [](const PhdrEntry *E) { return E->p_type == PT_LOAD; });
- if (It == Phdrs.end())
+void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
+ uint64_t min = std::numeric_limits<uint64_t>::max();
+ for (OutputSection *sec : outputSections)
+ if (sec->flags & SHF_ALLOC)
+ min = std::min<uint64_t>(min, sec->addr);
+
+ auto it = llvm::find_if(
+ phdrs, [](const PhdrEntry *e) { return e->p_type == PT_LOAD; });
+ if (it == phdrs.end())
return;
- PhdrEntry *FirstPTLoad = *It;
+ PhdrEntry *firstPTLoad = *it;
- bool HasExplicitHeaders =
- llvm::any_of(PhdrsCommands, [](const PhdrsCommand &Cmd) {
- return Cmd.HasPhdrs || Cmd.HasFilehdr;
+ bool hasExplicitHeaders =
+ llvm::any_of(phdrsCommands, [](const PhdrsCommand &cmd) {
+ return cmd.hasPhdrs || cmd.hasFilehdr;
});
- uint64_t HeaderSize = getHeaderSize();
- if (HeaderSize <= Min - computeBase(Min, HasExplicitHeaders)) {
- Min = alignDown(Min - HeaderSize, Config->MaxPageSize);
- Out::ElfHeader->Addr = Min;
- Out::ProgramHeaders->Addr = Min + Out::ElfHeader->Size;
+ bool paged = !config->omagic && !config->nmagic;
+ uint64_t headerSize = getHeaderSize();
+ if ((paged || hasExplicitHeaders) &&
+ headerSize <= min - computeBase(min, hasExplicitHeaders)) {
+ min = alignDown(min - headerSize, config->maxPageSize);
+ Out::elfHeader->addr = min;
+ Out::programHeaders->addr = min + Out::elfHeader->size;
return;
}
// Error if we were explicitly asked to allocate headers.
- if (HasExplicitHeaders)
+ if (hasExplicitHeaders)
error("could not allocate headers");
- Out::ElfHeader->PtLoad = nullptr;
- Out::ProgramHeaders->PtLoad = nullptr;
- FirstPTLoad->FirstSec = findFirstSection(FirstPTLoad);
+ Out::elfHeader->ptLoad = nullptr;
+ Out::programHeaders->ptLoad = nullptr;
+ firstPTLoad->firstSec = findFirstSection(firstPTLoad);
- llvm::erase_if(Phdrs,
- [](const PhdrEntry *E) { return E->p_type == PT_PHDR; });
+ llvm::erase_if(phdrs,
+ [](const PhdrEntry *e) { return e->p_type == PT_PHDR; });
}
LinkerScript::AddressState::AddressState() {
- for (auto &MRI : Script->MemoryRegions) {
- MemoryRegion *MR = MRI.second;
- MR->CurPos = MR->Origin;
+ for (auto &mri : script->memoryRegions) {
+ MemoryRegion *mr = mri.second;
+ mr->curPos = mr->origin;
}
}
static uint64_t getInitialDot() {
// By default linker scripts use an initial value of 0 for '.',
// but prefer -image-base if set.
- if (Script->HasSectionsCommand)
- return Config->ImageBase ? *Config->ImageBase : 0;
+ if (script->hasSectionsCommand)
+ return config->imageBase ? *config->imageBase : 0;
- uint64_t StartAddr = UINT64_MAX;
- // The Sections with -T<section> have been sorted in order of ascending
- // address. We must lower StartAddr if the lowest -T<section address> as
+ uint64_t startAddr = UINT64_MAX;
+ // The sections with -T<section> have been sorted in order of ascending
+ // address. We must lower startAddr if the lowest -T<section address> as
// calls to setDot() must be monotonically increasing.
- for (auto &KV : Config->SectionStartMap)
- StartAddr = std::min(StartAddr, KV.second);
- return std::min(StartAddr, Target->getImageBase() + elf::getHeaderSize());
+ for (auto &kv : config->sectionStartMap)
+ startAddr = std::min(startAddr, kv.second);
+ return std::min(startAddr, target->getImageBase() + elf::getHeaderSize());
}
// Here we assign addresses as instructed by linker script SECTIONS
// sub-commands. Doing that allows us to use final VA values, so here
// we also handle rest commands like symbol assignments and ASSERTs.
void LinkerScript::assignAddresses() {
- Dot = getInitialDot();
-
- auto Deleter = make_unique<AddressState>();
- Ctx = Deleter.get();
- ErrorOnMissingSection = true;
- switchTo(Aether);
-
- for (BaseCommand *Base : SectionCommands) {
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- Cmd->Addr = Dot;
- assignSymbol(Cmd, false);
- Cmd->Size = Dot - Cmd->Addr;
+ dot = getInitialDot();
+
+ auto deleter = make_unique<AddressState>();
+ ctx = deleter.get();
+ errorOnMissingSection = true;
+ switchTo(aether);
+
+ for (BaseCommand *base : sectionCommands) {
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ cmd->addr = dot;
+ assignSymbol(cmd, false);
+ cmd->size = dot - cmd->addr;
continue;
}
- assignOffsets(cast<OutputSection>(Base));
+ assignOffsets(cast<OutputSection>(base));
}
- Ctx = nullptr;
+ ctx = nullptr;
}
// Creates program headers as instructed by PHDRS linker script command.
std::vector<PhdrEntry *> LinkerScript::createPhdrs() {
- std::vector<PhdrEntry *> Ret;
+ std::vector<PhdrEntry *> ret;
// Process PHDRS and FILEHDR keywords because they are not
// real output sections and cannot be added in the following loop.
- for (const PhdrsCommand &Cmd : PhdrsCommands) {
- PhdrEntry *Phdr = make<PhdrEntry>(Cmd.Type, Cmd.Flags ? *Cmd.Flags : PF_R);
+ for (const PhdrsCommand &cmd : phdrsCommands) {
+ PhdrEntry *phdr = make<PhdrEntry>(cmd.type, cmd.flags ? *cmd.flags : PF_R);
- if (Cmd.HasFilehdr)
- Phdr->add(Out::ElfHeader);
- if (Cmd.HasPhdrs)
- Phdr->add(Out::ProgramHeaders);
+ if (cmd.hasFilehdr)
+ phdr->add(Out::elfHeader);
+ if (cmd.hasPhdrs)
+ phdr->add(Out::programHeaders);
- if (Cmd.LMAExpr) {
- Phdr->p_paddr = Cmd.LMAExpr().getValue();
- Phdr->HasLMA = true;
+ if (cmd.lmaExpr) {
+ phdr->p_paddr = cmd.lmaExpr().getValue();
+ phdr->hasLMA = true;
}
- Ret.push_back(Phdr);
+ ret.push_back(phdr);
}
// Add output sections to program headers.
- for (OutputSection *Sec : OutputSections) {
+ for (OutputSection *sec : outputSections) {
// Assign headers specified by linker script
- for (size_t Id : getPhdrIndices(Sec)) {
- Ret[Id]->add(Sec);
- if (!PhdrsCommands[Id].Flags.hasValue())
- Ret[Id]->p_flags |= Sec->getPhdrFlags();
+ for (size_t id : getPhdrIndices(sec)) {
+ ret[id]->add(sec);
+ if (!phdrsCommands[id].flags.hasValue())
+ ret[id]->p_flags |= sec->getPhdrFlags();
}
}
- return Ret;
+ return ret;
}
// Returns true if we should emit an .interp section.
@@ -1091,54 +1105,54 @@ std::vector<PhdrEntry *> LinkerScript::createPhdrs() {
// no PT_INTERP is there, there's no place to emit an
// .interp, so we don't do that in that case.
bool LinkerScript::needsInterpSection() {
- if (PhdrsCommands.empty())
+ if (phdrsCommands.empty())
return true;
- for (PhdrsCommand &Cmd : PhdrsCommands)
- if (Cmd.Type == PT_INTERP)
+ for (PhdrsCommand &cmd : phdrsCommands)
+ if (cmd.type == PT_INTERP)
return true;
return false;
}
-ExprValue LinkerScript::getSymbolValue(StringRef Name, const Twine &Loc) {
- if (Name == ".") {
- if (Ctx)
- return {Ctx->OutSec, false, Dot - Ctx->OutSec->Addr, Loc};
- error(Loc + ": unable to get location counter value");
+ExprValue LinkerScript::getSymbolValue(StringRef name, const Twine &loc) {
+ if (name == ".") {
+ if (ctx)
+ return {ctx->outSec, false, dot - ctx->outSec->addr, loc};
+ error(loc + ": unable to get location counter value");
return 0;
}
- if (Symbol *Sym = Symtab->find(Name)) {
- if (auto *DS = dyn_cast<Defined>(Sym))
- return {DS->Section, false, DS->Value, Loc};
- if (isa<SharedSymbol>(Sym))
- if (!ErrorOnMissingSection)
- return {nullptr, false, 0, Loc};
+ if (Symbol *sym = symtab->find(name)) {
+ if (auto *ds = dyn_cast<Defined>(sym))
+ return {ds->section, false, ds->value, loc};
+ if (isa<SharedSymbol>(sym))
+ if (!errorOnMissingSection)
+ return {nullptr, false, 0, loc};
}
- error(Loc + ": symbol not found: " + Name);
+ error(loc + ": symbol not found: " + name);
return 0;
}
// Returns the index of the segment named Name.
-static Optional<size_t> getPhdrIndex(ArrayRef<PhdrsCommand> Vec,
- StringRef Name) {
- for (size_t I = 0; I < Vec.size(); ++I)
- if (Vec[I].Name == Name)
- return I;
+static Optional<size_t> getPhdrIndex(ArrayRef<PhdrsCommand> vec,
+ StringRef name) {
+ for (size_t i = 0; i < vec.size(); ++i)
+ if (vec[i].name == name)
+ return i;
return None;
}
// Returns indices of ELF headers containing specific section. Each index is a
// zero based number of ELF header listed within PHDRS {} script block.
-std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *Cmd) {
- std::vector<size_t> Ret;
-
- for (StringRef S : Cmd->Phdrs) {
- if (Optional<size_t> Idx = getPhdrIndex(PhdrsCommands, S))
- Ret.push_back(*Idx);
- else if (S != "NONE")
- error(Cmd->Location + ": section header '" + S +
+std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *cmd) {
+ std::vector<size_t> ret;
+
+ for (StringRef s : cmd->phdrs) {
+ if (Optional<size_t> idx = getPhdrIndex(phdrsCommands, s))
+ ret.push_back(*idx);
+ else if (s != "NONE")
+ error(cmd->location + ": section header '" + s +
"' is not listed in PHDRS");
}
- return Ret;
+ return ret;
}
diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h
index 51161981efc80..9e9c08ef10ba5 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -1,9 +1,8 @@
//===- LinkerScript.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -32,7 +31,6 @@ namespace elf {
class Defined;
class InputSection;
class InputSectionBase;
-class InputSectionBase;
class OutputSection;
class SectionBase;
class Symbol;
@@ -40,35 +38,35 @@ class ThunkSection;
// This represents an r-value in the linker script.
struct ExprValue {
- ExprValue(SectionBase *Sec, bool ForceAbsolute, uint64_t Val,
- const Twine &Loc)
- : Sec(Sec), ForceAbsolute(ForceAbsolute), Val(Val), Loc(Loc.str()) {}
+ ExprValue(SectionBase *sec, bool forceAbsolute, uint64_t val,
+ const Twine &loc)
+ : sec(sec), forceAbsolute(forceAbsolute), val(val), loc(loc.str()) {}
- ExprValue(uint64_t Val) : ExprValue(nullptr, false, Val, "") {}
+ ExprValue(uint64_t val) : ExprValue(nullptr, false, val, "") {}
- bool isAbsolute() const { return ForceAbsolute || Sec == nullptr; }
+ bool isAbsolute() const { return forceAbsolute || sec == nullptr; }
uint64_t getValue() const;
uint64_t getSecAddr() const;
uint64_t getSectionOffset() const;
// If a value is relative to a section, it has a non-null Sec.
- SectionBase *Sec;
+ SectionBase *sec;
// True if this expression is enclosed in ABSOLUTE().
// This flag affects the return value of getValue().
- bool ForceAbsolute;
+ bool forceAbsolute;
- uint64_t Val;
- uint64_t Alignment = 1;
+ uint64_t val;
+ uint64_t alignment = 1;
// Original source location. Used for error messages.
- std::string Loc;
+ std::string loc;
};
// This represents an expression in the linker script.
// ScriptParser::readExpr reads an expression and returns an Expr.
// Later, we evaluate the expression by calling the function.
-typedef std::function<ExprValue()> Expr;
+using Expr = std::function<ExprValue()>;
// This enum is used to implement linker script SECTIONS command.
// https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS
@@ -80,42 +78,42 @@ enum SectionsCommandKind {
};
struct BaseCommand {
- BaseCommand(int K) : Kind(K) {}
- int Kind;
+ BaseCommand(int k) : kind(k) {}
+ int kind;
};
// This represents ". = <expr>" or "<symbol> = <expr>".
struct SymbolAssignment : BaseCommand {
- SymbolAssignment(StringRef Name, Expr E, std::string Loc)
- : BaseCommand(AssignmentKind), Name(Name), Expression(E), Location(Loc) {}
+ SymbolAssignment(StringRef name, Expr e, std::string loc)
+ : BaseCommand(AssignmentKind), name(name), expression(e), location(loc) {}
- static bool classof(const BaseCommand *C) {
- return C->Kind == AssignmentKind;
+ static bool classof(const BaseCommand *c) {
+ return c->kind == AssignmentKind;
}
// The LHS of an expression. Name is either a symbol name or ".".
- StringRef Name;
- Defined *Sym = nullptr;
+ StringRef name;
+ Defined *sym = nullptr;
// The RHS of an expression.
- Expr Expression;
+ Expr expression;
// Command attributes for PROVIDE, HIDDEN and PROVIDE_HIDDEN.
- bool Provide = false;
- bool Hidden = false;
+ bool provide = false;
+ bool hidden = false;
// Holds file name and line number for error reporting.
- std::string Location;
+ std::string location;
// A string representation of this command. We use this for -Map.
- std::string CommandString;
+ std::string commandString;
// Address of this assignment command.
- unsigned Addr;
+ unsigned addr;
// Size of this assignment command. This is usually 0, but if
// you move '.' this may be greater than 0.
- unsigned Size;
+ unsigned size;
};
// Linker scripts allow additional constraints to be put on ouput sections.
@@ -128,83 +126,83 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
// target memory. Instances of the struct are created by parsing the
// MEMORY command.
struct MemoryRegion {
- MemoryRegion(StringRef Name, uint64_t Origin, uint64_t Length, uint32_t Flags,
- uint32_t NegFlags)
- : Name(Name), Origin(Origin), Length(Length), Flags(Flags),
- NegFlags(NegFlags) {}
-
- std::string Name;
- uint64_t Origin;
- uint64_t Length;
- uint32_t Flags;
- uint32_t NegFlags;
- uint64_t CurPos = 0;
+ MemoryRegion(StringRef name, uint64_t origin, uint64_t length, uint32_t flags,
+ uint32_t negFlags)
+ : name(name), origin(origin), length(length), flags(flags),
+ negFlags(negFlags) {}
+
+ std::string name;
+ uint64_t origin;
+ uint64_t length;
+ uint32_t flags;
+ uint32_t negFlags;
+ uint64_t curPos = 0;
};
// This struct represents one section match pattern in SECTIONS() command.
// It can optionally have negative match pattern for EXCLUDED_FILE command.
// Also it may be surrounded with SORT() command, so contains sorting rules.
struct SectionPattern {
- SectionPattern(StringMatcher &&Pat1, StringMatcher &&Pat2)
- : ExcludedFilePat(Pat1), SectionPat(Pat2),
- SortOuter(SortSectionPolicy::Default),
- SortInner(SortSectionPolicy::Default) {}
-
- StringMatcher ExcludedFilePat;
- StringMatcher SectionPat;
- SortSectionPolicy SortOuter;
- SortSectionPolicy SortInner;
+ SectionPattern(StringMatcher &&pat1, StringMatcher &&pat2)
+ : excludedFilePat(pat1), sectionPat(pat2),
+ sortOuter(SortSectionPolicy::Default),
+ sortInner(SortSectionPolicy::Default) {}
+
+ StringMatcher excludedFilePat;
+ StringMatcher sectionPat;
+ SortSectionPolicy sortOuter;
+ SortSectionPolicy sortInner;
};
struct InputSectionDescription : BaseCommand {
- InputSectionDescription(StringRef FilePattern)
- : BaseCommand(InputSectionKind), FilePat(FilePattern) {}
+ InputSectionDescription(StringRef filePattern)
+ : BaseCommand(InputSectionKind), filePat(filePattern) {}
- static bool classof(const BaseCommand *C) {
- return C->Kind == InputSectionKind;
+ static bool classof(const BaseCommand *c) {
+ return c->kind == InputSectionKind;
}
- StringMatcher FilePat;
+ StringMatcher filePat;
// Input sections that matches at least one of SectionPatterns
// will be associated with this InputSectionDescription.
- std::vector<SectionPattern> SectionPatterns;
+ std::vector<SectionPattern> sectionPatterns;
- std::vector<InputSection *> Sections;
+ std::vector<InputSection *> sections;
// Temporary record of synthetic ThunkSection instances and the pass that
// they were created in. This is used to insert newly created ThunkSections
// into Sections at the end of a createThunks() pass.
- std::vector<std::pair<ThunkSection *, uint32_t>> ThunkSections;
+ std::vector<std::pair<ThunkSection *, uint32_t>> thunkSections;
};
// Represents BYTE(), SHORT(), LONG(), or QUAD().
struct ByteCommand : BaseCommand {
- ByteCommand(Expr E, unsigned Size, std::string CommandString)
- : BaseCommand(ByteKind), CommandString(CommandString), Expression(E),
- Size(Size) {}
+ ByteCommand(Expr e, unsigned size, std::string commandString)
+ : BaseCommand(ByteKind), commandString(commandString), expression(e),
+ size(size) {}
- static bool classof(const BaseCommand *C) { return C->Kind == ByteKind; }
+ static bool classof(const BaseCommand *c) { return c->kind == ByteKind; }
// Keeps string representing the command. Used for -Map" is perhaps better.
- std::string CommandString;
+ std::string commandString;
- Expr Expression;
+ Expr expression;
// This is just an offset of this assignment command in the output section.
- unsigned Offset;
+ unsigned offset;
// Size of this data command.
- unsigned Size;
+ unsigned size;
};
struct PhdrsCommand {
- StringRef Name;
- unsigned Type = llvm::ELF::PT_NULL;
- bool HasFilehdr = false;
- bool HasPhdrs = false;
- llvm::Optional<unsigned> Flags;
- Expr LMAExpr = nullptr;
+ StringRef name;
+ unsigned type = llvm::ELF::PT_NULL;
+ bool hasFilehdr = false;
+ bool hasPhdrs = false;
+ llvm::Optional<unsigned> flags;
+ Expr lmaExpr = nullptr;
};
class LinkerScript final {
@@ -213,35 +211,35 @@ class LinkerScript final {
// not be used outside of the scope of a call to the above functions.
struct AddressState {
AddressState();
- uint64_t ThreadBssOffset = 0;
- OutputSection *OutSec = nullptr;
- MemoryRegion *MemRegion = nullptr;
- MemoryRegion *LMARegion = nullptr;
- uint64_t LMAOffset = 0;
+ uint64_t threadBssOffset = 0;
+ OutputSection *outSec = nullptr;
+ MemoryRegion *memRegion = nullptr;
+ MemoryRegion *lmaRegion = nullptr;
+ uint64_t lmaOffset = 0;
};
- llvm::DenseMap<StringRef, OutputSection *> NameToOutputSection;
+ llvm::DenseMap<StringRef, OutputSection *> nameToOutputSection;
- void addSymbol(SymbolAssignment *Cmd);
- void assignSymbol(SymbolAssignment *Cmd, bool InSec);
- void setDot(Expr E, const Twine &Loc, bool InSec);
- void expandOutputSection(uint64_t Size);
- void expandMemoryRegions(uint64_t Size);
+ void addSymbol(SymbolAssignment *cmd);
+ void assignSymbol(SymbolAssignment *cmd, bool inSec);
+ void setDot(Expr e, const Twine &loc, bool inSec);
+ void expandOutputSection(uint64_t size);
+ void expandMemoryRegions(uint64_t size);
std::vector<InputSection *>
computeInputSections(const InputSectionDescription *);
- std::vector<InputSection *> createInputSectionList(OutputSection &Cmd);
+ std::vector<InputSection *> createInputSectionList(OutputSection &cmd);
- std::vector<size_t> getPhdrIndices(OutputSection *Sec);
+ std::vector<size_t> getPhdrIndices(OutputSection *sec);
- MemoryRegion *findMemoryRegion(OutputSection *Sec);
+ MemoryRegion *findMemoryRegion(OutputSection *sec);
- void switchTo(OutputSection *Sec);
- uint64_t advance(uint64_t Size, unsigned Align);
- void output(InputSection *Sec);
+ void switchTo(OutputSection *sec);
+ uint64_t advance(uint64_t size, unsigned align);
+ void output(InputSection *sec);
- void assignOffsets(OutputSection *Sec);
+ void assignOffsets(OutputSection *sec);
// Ctx captures the local AddressState and makes it accessible
// deliberately. This is needed as there are some cases where we cannot just
@@ -249,21 +247,21 @@ class LinkerScript final {
// script parser.
// This should remain a plain pointer as its lifetime is smaller than
// LinkerScript.
- AddressState *Ctx = nullptr;
+ AddressState *ctx = nullptr;
- OutputSection *Aether;
+ OutputSection *aether;
- uint64_t Dot;
+ uint64_t dot;
public:
- OutputSection *createOutputSection(StringRef Name, StringRef Location);
- OutputSection *getOrCreateOutputSection(StringRef Name);
+ OutputSection *createOutputSection(StringRef name, StringRef location);
+ OutputSection *getOrCreateOutputSection(StringRef name);
- bool hasPhdrsCommands() { return !PhdrsCommands.empty(); }
- uint64_t getDot() { return Dot; }
- void discard(ArrayRef<InputSection *> V);
+ bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
+ uint64_t getDot() { return dot; }
+ void discard(ArrayRef<InputSection *> v);
- ExprValue getSymbolValue(StringRef Name, const Twine &Loc);
+ ExprValue getSymbolValue(StringRef name, const Twine &loc);
void addOrphanSections();
void adjustSectionsBeforeSorting();
@@ -272,9 +270,9 @@ public:
std::vector<PhdrEntry *> createPhdrs();
bool needsInterpSection();
- bool shouldKeep(InputSectionBase *S);
+ bool shouldKeep(InputSectionBase *s);
void assignAddresses();
- void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
+ void allocateHeaders(std::vector<PhdrEntry *> &phdrs);
void processSectionCommands();
void declareSymbols();
@@ -282,31 +280,31 @@ public:
void processInsertCommands();
// SECTIONS command list.
- std::vector<BaseCommand *> SectionCommands;
+ std::vector<BaseCommand *> sectionCommands;
// PHDRS command list.
- std::vector<PhdrsCommand> PhdrsCommands;
+ std::vector<PhdrsCommand> phdrsCommands;
- bool HasSectionsCommand = false;
- bool ErrorOnMissingSection = false;
+ bool hasSectionsCommand = false;
+ bool errorOnMissingSection = false;
// List of section patterns specified with KEEP commands. They will
// be kept even if they are unused and --gc-sections is specified.
- std::vector<InputSectionDescription *> KeptSections;
+ std::vector<InputSectionDescription *> keptSections;
// A map from memory region name to a memory region descriptor.
- llvm::MapVector<llvm::StringRef, MemoryRegion *> MemoryRegions;
+ llvm::MapVector<llvm::StringRef, MemoryRegion *> memoryRegions;
// A list of symbols referenced by the script.
- std::vector<llvm::StringRef> ReferencedSymbols;
+ std::vector<llvm::StringRef> referencedSymbols;
// Used to implement INSERT [AFTER|BEFORE]. Contains commands that need
// to be inserted into SECTIONS commands list.
- llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertAfterCommands;
- llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertBeforeCommands;
+ llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertAfterCommands;
+ llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertBeforeCommands;
};
-extern LinkerScript *Script;
+extern LinkerScript *script;
} // end namespace elf
} // end namespace lld
diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp
index b0dc6203008d8..a4a6238fc6557 100644
--- a/ELF/MapFile.cpp
+++ b/ELF/MapFile.cpp
@@ -1,9 +1,8 @@
//===- MapFile.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -38,69 +37,67 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-typedef DenseMap<const SectionBase *, SmallVector<Defined *, 4>> SymbolMapTy;
+using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
-static const std::string Indent8 = " "; // 8 spaces
-static const std::string Indent16 = " "; // 16 spaces
+static const std::string indent8 = " "; // 8 spaces
+static const std::string indent16 = " "; // 16 spaces
// Print out the first three columns of a line.
-static void writeHeader(raw_ostream &OS, uint64_t VMA, uint64_t LMA,
- uint64_t Size, uint64_t Align) {
- if (Config->Is64)
- OS << format("%16llx %16llx %8llx %5lld ", VMA, LMA, Size, Align);
+static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma,
+ uint64_t size, uint64_t align) {
+ if (config->is64)
+ os << format("%16llx %16llx %8llx %5lld ", vma, lma, size, align);
else
- OS << format("%8llx %8llx %8llx %5lld ", VMA, LMA, Size, Align);
+ os << format("%8llx %8llx %8llx %5lld ", vma, lma, size, align);
}
// Returns a list of all symbols that we want to print out.
static std::vector<Defined *> getSymbols() {
- std::vector<Defined *> V;
- for (InputFile *File : ObjectFiles)
- for (Symbol *B : File->getSymbols())
- if (auto *DR = dyn_cast<Defined>(B))
- if (!DR->isSection() && DR->Section && DR->Section->Live &&
- (DR->File == File || DR->NeedsPltAddr || DR->Section->Bss))
- V.push_back(DR);
- return V;
+ std::vector<Defined *> v;
+ for (InputFile *file : objectFiles)
+ for (Symbol *b : file->getSymbols())
+ if (auto *dr = dyn_cast<Defined>(b))
+ if (!dr->isSection() && dr->section && dr->section->isLive() &&
+ (dr->file == file || dr->needsPltAddr || dr->section->bss))
+ v.push_back(dr);
+ return v;
}
// Returns a map from sections to their symbols.
-static SymbolMapTy getSectionSyms(ArrayRef<Defined *> Syms) {
- SymbolMapTy Ret;
- for (Defined *DR : Syms)
- Ret[DR->Section].push_back(DR);
+static SymbolMapTy getSectionSyms(ArrayRef<Defined *> syms) {
+ SymbolMapTy ret;
+ for (Defined *dr : syms)
+ ret[dr->section].push_back(dr);
// Sort symbols by address. We want to print out symbols in the
// order in the output file rather than the order they appeared
// in the input files.
- for (auto &It : Ret) {
- SmallVectorImpl<Defined *> &V = It.second;
- std::stable_sort(V.begin(), V.end(), [](Defined *A, Defined *B) {
- return A->getVA() < B->getVA();
+ for (auto &it : ret)
+ llvm::stable_sort(it.second, [](Defined *a, Defined *b) {
+ return a->getVA() < b->getVA();
});
- }
- return Ret;
+ return ret;
}
// Construct a map from symbols to their stringified representations.
// Demangling symbols (which is what toString() does) is slow, so
// we do that in batch using parallel-for.
static DenseMap<Symbol *, std::string>
-getSymbolStrings(ArrayRef<Defined *> Syms) {
- std::vector<std::string> Str(Syms.size());
- parallelForEachN(0, Syms.size(), [&](size_t I) {
- raw_string_ostream OS(Str[I]);
- OutputSection *OSec = Syms[I]->getOutputSection();
- uint64_t VMA = Syms[I]->getVA();
- uint64_t LMA = OSec ? OSec->getLMA() + VMA - OSec->getVA(0) : 0;
- writeHeader(OS, VMA, LMA, Syms[I]->getSize(), 1);
- OS << Indent16 << toString(*Syms[I]);
+getSymbolStrings(ArrayRef<Defined *> syms) {
+ std::vector<std::string> str(syms.size());
+ parallelForEachN(0, syms.size(), [&](size_t i) {
+ raw_string_ostream os(str[i]);
+ OutputSection *osec = syms[i]->getOutputSection();
+ uint64_t vma = syms[i]->getVA();
+ uint64_t lma = osec ? osec->getLMA() + vma - osec->getVA(0) : 0;
+ writeHeader(os, vma, lma, syms[i]->getSize(), 1);
+ os << indent16 << toString(*syms[i]);
});
- DenseMap<Symbol *, std::string> Ret;
- for (size_t I = 0, E = Syms.size(); I < E; ++I)
- Ret[Syms[I]] = std::move(Str[I]);
- return Ret;
+ DenseMap<Symbol *, std::string> ret;
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ ret[syms[i]] = std::move(str[i]);
+ return ret;
}
// Print .eh_frame contents. Since the section consists of EhSectionPieces,
@@ -109,114 +106,115 @@ getSymbolStrings(ArrayRef<Defined *> Syms) {
// .eh_frame tend to contain a lot of section pieces that are contiguous
// both in input file and output file. Such pieces are squashed before
// being displayed to make output compact.
-static void printEhFrame(raw_ostream &OS, OutputSection *OSec) {
- std::vector<EhSectionPiece> Pieces;
+static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) {
+ std::vector<EhSectionPiece> pieces;
- auto Add = [&](const EhSectionPiece &P) {
+ auto add = [&](const EhSectionPiece &p) {
// If P is adjacent to Last, squash the two.
- if (!Pieces.empty()) {
- EhSectionPiece &Last = Pieces.back();
- if (Last.Sec == P.Sec && Last.InputOff + Last.Size == P.InputOff &&
- Last.OutputOff + Last.Size == P.OutputOff) {
- Last.Size += P.Size;
+ if (!pieces.empty()) {
+ EhSectionPiece &last = pieces.back();
+ if (last.sec == p.sec && last.inputOff + last.size == p.inputOff &&
+ last.outputOff + last.size == p.outputOff) {
+ last.size += p.size;
return;
}
}
- Pieces.push_back(P);
+ pieces.push_back(p);
};
// Gather section pieces.
- for (const CieRecord *Rec : In.EhFrame->getCieRecords()) {
- Add(*Rec->Cie);
- for (const EhSectionPiece *Fde : Rec->Fdes)
- Add(*Fde);
+ for (const CieRecord *rec : sec->getCieRecords()) {
+ add(*rec->cie);
+ for (const EhSectionPiece *fde : rec->fdes)
+ add(*fde);
}
// Print out section pieces.
- for (EhSectionPiece &P : Pieces) {
- writeHeader(OS, OSec->Addr + P.OutputOff, OSec->getLMA() + P.OutputOff,
- P.Size, 1);
- OS << Indent8 << toString(P.Sec->File) << ":(" << P.Sec->Name << "+0x"
- << Twine::utohexstr(P.InputOff) + ")\n";
+ const OutputSection *osec = sec->getOutputSection();
+ for (EhSectionPiece &p : pieces) {
+ writeHeader(os, osec->addr + p.outputOff, osec->getLMA() + p.outputOff,
+ p.size, 1);
+ os << indent8 << toString(p.sec->file) << ":(" << p.sec->name << "+0x"
+ << Twine::utohexstr(p.inputOff) + ")\n";
}
}
void elf::writeMapFile() {
- if (Config->MapFile.empty())
+ if (config->mapFile.empty())
return;
// Open a map file for writing.
- std::error_code EC;
- raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
- if (EC) {
- error("cannot open " + Config->MapFile + ": " + EC.message());
+ std::error_code ec;
+ raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
+ if (ec) {
+ error("cannot open " + config->mapFile + ": " + ec.message());
return;
}
// Collect symbol info that we want to print out.
- std::vector<Defined *> Syms = getSymbols();
- SymbolMapTy SectionSyms = getSectionSyms(Syms);
- DenseMap<Symbol *, std::string> SymStr = getSymbolStrings(Syms);
+ std::vector<Defined *> syms = getSymbols();
+ SymbolMapTy sectionSyms = getSectionSyms(syms);
+ DenseMap<Symbol *, std::string> symStr = getSymbolStrings(syms);
// Print out the header line.
- int W = Config->Is64 ? 16 : 8;
- OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W)
+ int w = config->is64 ? 16 : 8;
+ os << right_justify("VMA", w) << ' ' << right_justify("LMA", w)
<< " Size Align Out In Symbol\n";
- OutputSection* OSec = nullptr;
- for (BaseCommand *Base : Script->SectionCommands) {
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- if (Cmd->Provide && !Cmd->Sym)
+ OutputSection* osec = nullptr;
+ for (BaseCommand *base : script->sectionCommands) {
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ if (cmd->provide && !cmd->sym)
continue;
- uint64_t LMA = OSec ? OSec->getLMA() + Cmd->Addr - OSec->getVA(0) : 0;
- writeHeader(OS, Cmd->Addr, LMA, Cmd->Size, 1);
- OS << Cmd->CommandString << '\n';
+ uint64_t lma = osec ? osec->getLMA() + cmd->addr - osec->getVA(0) : 0;
+ writeHeader(os, cmd->addr, lma, cmd->size, 1);
+ os << cmd->commandString << '\n';
continue;
}
- OSec = cast<OutputSection>(Base);
- writeHeader(OS, OSec->Addr, OSec->getLMA(), OSec->Size, OSec->Alignment);
- OS << OSec->Name << '\n';
+ osec = cast<OutputSection>(base);
+ writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->alignment);
+ os << osec->name << '\n';
// Dump symbols for each input section.
- for (BaseCommand *Base : OSec->SectionCommands) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
- for (InputSection *IS : ISD->Sections) {
- if (IS == In.EhFrame) {
- printEhFrame(OS, OSec);
+ for (BaseCommand *base : osec->sectionCommands) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
+ for (InputSection *isec : isd->sections) {
+ if (auto *ehSec = dyn_cast<EhFrameSection>(isec)) {
+ printEhFrame(os, ehSec);
continue;
}
- writeHeader(OS, IS->getVA(0), OSec->getLMA() + IS->getOffset(0),
- IS->getSize(), IS->Alignment);
- OS << Indent8 << toString(IS) << '\n';
- for (Symbol *Sym : SectionSyms[IS])
- OS << SymStr[Sym] << '\n';
+ writeHeader(os, isec->getVA(0), osec->getLMA() + isec->getOffset(0),
+ isec->getSize(), isec->alignment);
+ os << indent8 << toString(isec) << '\n';
+ for (Symbol *sym : sectionSyms[isec])
+ os << symStr[sym] << '\n';
}
continue;
}
- if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
- writeHeader(OS, OSec->Addr + Cmd->Offset, OSec->getLMA() + Cmd->Offset,
- Cmd->Size, 1);
- OS << Indent8 << Cmd->CommandString << '\n';
+ if (auto *cmd = dyn_cast<ByteCommand>(base)) {
+ writeHeader(os, osec->addr + cmd->offset, osec->getLMA() + cmd->offset,
+ cmd->size, 1);
+ os << indent8 << cmd->commandString << '\n';
continue;
}
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- if (Cmd->Provide && !Cmd->Sym)
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ if (cmd->provide && !cmd->sym)
continue;
- writeHeader(OS, Cmd->Addr, OSec->getLMA() + Cmd->Addr - OSec->getVA(0),
- Cmd->Size, 1);
- OS << Indent8 << Cmd->CommandString << '\n';
+ writeHeader(os, cmd->addr, osec->getLMA() + cmd->addr - osec->getVA(0),
+ cmd->size, 1);
+ os << indent8 << cmd->commandString << '\n';
continue;
}
}
}
}
-static void print(StringRef A, StringRef B) {
- outs() << left_justify(A, 49) << " " << B << "\n";
+static void print(StringRef a, StringRef b) {
+ outs() << left_justify(a, 49) << " " << b << "\n";
}
// Output a cross reference table to stdout. This is for --cref.
@@ -231,18 +229,18 @@ static void print(StringRef A, StringRef B) {
// In this case, strlen is defined by libc.so.6 and used by other two
// files.
void elf::writeCrossReferenceTable() {
- if (!Config->Cref)
+ if (!config->cref)
return;
// Collect symbols and files.
- MapVector<Symbol *, SetVector<InputFile *>> Map;
- for (InputFile *File : ObjectFiles) {
- for (Symbol *Sym : File->getSymbols()) {
- if (isa<SharedSymbol>(Sym))
- Map[Sym].insert(File);
- if (auto *D = dyn_cast<Defined>(Sym))
- if (!D->isLocal() && (!D->Section || D->Section->Live))
- Map[D].insert(File);
+ MapVector<Symbol *, SetVector<InputFile *>> map;
+ for (InputFile *file : objectFiles) {
+ for (Symbol *sym : file->getSymbols()) {
+ if (isa<SharedSymbol>(sym))
+ map[sym].insert(file);
+ if (auto *d = dyn_cast<Defined>(sym))
+ if (!d->isLocal() && (!d->section || d->section->isLive()))
+ map[d].insert(file);
}
}
@@ -251,13 +249,13 @@ void elf::writeCrossReferenceTable() {
print("Symbol", "File");
// Print out a table.
- for (auto KV : Map) {
- Symbol *Sym = KV.first;
- SetVector<InputFile *> &Files = KV.second;
+ for (auto kv : map) {
+ Symbol *sym = kv.first;
+ SetVector<InputFile *> &files = kv.second;
- print(toString(*Sym), toString(Sym->File));
- for (InputFile *File : Files)
- if (File != Sym->File)
- print("", toString(File));
+ print(toString(*sym), toString(sym->file));
+ for (InputFile *file : files)
+ if (file != sym->file)
+ print("", toString(file));
}
}
diff --git a/ELF/MapFile.h b/ELF/MapFile.h
index 0282425888b72..7e7938919edfb 100644
--- a/ELF/MapFile.h
+++ b/ELF/MapFile.h
@@ -1,9 +1,8 @@
//===- MapFile.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp
index 8d0ec091c3278..36b847f725b87 100644
--- a/ELF/MarkLive.cpp
+++ b/ELF/MarkLive.cpp
@@ -1,9 +1,8 @@
//===- MarkLive.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -26,6 +25,7 @@
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
@@ -42,66 +42,79 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
+namespace {
+template <class ELFT> class MarkLive {
+public:
+ MarkLive(unsigned partition) : partition(partition) {}
+
+ void run();
+ void moveToMain();
+
+private:
+ void enqueue(InputSectionBase *sec, uint64_t offset);
+ void markSymbol(Symbol *sym);
+ void mark();
+
+ template <class RelTy>
+ void resolveReloc(InputSectionBase &sec, RelTy &rel, bool isLSDA);
+
+ template <class RelTy>
+ void scanEhFrameSection(EhInputSection &eh, ArrayRef<RelTy> rels);
+
+ // The index of the partition that we are currently processing.
+ unsigned partition;
+
+ // A list of sections to visit.
+ SmallVector<InputSection *, 256> queue;
+
+ // There are normally few input sections whose names are valid C
+ // identifiers, so we just store a std::vector instead of a multimap.
+ DenseMap<StringRef, std::vector<InputSectionBase *>> cNamedSections;
+};
+} // namespace
+
template <class ELFT>
-static typename ELFT::uint getAddend(InputSectionBase &Sec,
- const typename ELFT::Rel &Rel) {
- return Target->getImplicitAddend(Sec.data().begin() + Rel.r_offset,
- Rel.getType(Config->IsMips64EL));
+static uint64_t getAddend(InputSectionBase &sec,
+ const typename ELFT::Rel &rel) {
+ return target->getImplicitAddend(sec.data().begin() + rel.r_offset,
+ rel.getType(config->isMips64EL));
}
template <class ELFT>
-static typename ELFT::uint getAddend(InputSectionBase &Sec,
- const typename ELFT::Rela &Rel) {
- return Rel.r_addend;
+static uint64_t getAddend(InputSectionBase &sec,
+ const typename ELFT::Rela &rel) {
+ return rel.r_addend;
}
-// There are normally few input sections whose names are valid C
-// identifiers, so we just store a std::vector instead of a multimap.
-static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
-
-template <class ELFT, class RelT>
-static void
-resolveReloc(InputSectionBase &Sec, RelT &Rel,
- llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
- Symbol &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
+template <class ELFT>
+template <class RelTy>
+void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
+ bool isLSDA) {
+ Symbol &sym = sec.getFile<ELFT>()->getRelocTargetSym(rel);
// If a symbol is referenced in a live section, it is used.
- B.Used = true;
- if (auto *SS = dyn_cast<SharedSymbol>(&B))
- if (!SS->isWeak())
- SS->getFile<ELFT>().IsNeeded = true;
-
- if (auto *D = dyn_cast<Defined>(&B)) {
- auto *RelSec = dyn_cast_or_null<InputSectionBase>(D->Section);
- if (!RelSec)
+ sym.used = true;
+
+ if (auto *d = dyn_cast<Defined>(&sym)) {
+ auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section);
+ if (!relSec)
return;
- uint64_t Offset = D->Value;
- if (D->isSection())
- Offset += getAddend<ELFT>(Sec, Rel);
- Fn(RelSec, Offset);
- return;
- }
- if (!B.isDefined())
- for (InputSectionBase *Sec : CNamedSections.lookup(B.getName()))
- Fn(Sec, 0);
-}
+ uint64_t offset = d->value;
+ if (d->isSection())
+ offset += getAddend<ELFT>(sec, rel);
-// Calls Fn for each section that Sec refers to via relocations.
-template <class ELFT>
-static void
-forEachSuccessor(InputSection &Sec,
- llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
- if (Sec.AreRelocsRela) {
- for (const typename ELFT::Rela &Rel : Sec.template relas<ELFT>())
- resolveReloc<ELFT>(Sec, Rel, Fn);
- } else {
- for (const typename ELFT::Rel &Rel : Sec.template rels<ELFT>())
- resolveReloc<ELFT>(Sec, Rel, Fn);
+ if (!isLSDA || !(relSec->flags & SHF_EXECINSTR))
+ enqueue(relSec, offset);
+ return;
}
- for (InputSectionBase *IS : Sec.DependentSections)
- Fn(IS, 0);
+ if (auto *ss = dyn_cast<SharedSymbol>(&sym))
+ if (!ss->isWeak())
+ ss->getFile().isNeeded = true;
+
+ for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
+ enqueue(sec, 0);
}
// The .eh_frame section is an unfortunate special case.
@@ -118,175 +131,203 @@ forEachSuccessor(InputSection &Sec,
// A possible improvement would be to fully process .eh_frame in the middle of
// the gc pass. With that we would be able to also gc some sections holding
// LSDAs and personality functions if we found that they were unused.
-template <class ELFT, class RelTy>
-static void
-scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
- llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
- const endianness E = ELFT::TargetEndianness;
-
- for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) {
- EhSectionPiece &Piece = EH.Pieces[I];
- unsigned FirstRelI = Piece.FirstRelocation;
- if (FirstRelI == (unsigned)-1)
+template <class ELFT>
+template <class RelTy>
+void MarkLive<ELFT>::scanEhFrameSection(EhInputSection &eh,
+ ArrayRef<RelTy> rels) {
+ for (size_t i = 0, end = eh.pieces.size(); i < end; ++i) {
+ EhSectionPiece &piece = eh.pieces[i];
+ size_t firstRelI = piece.firstRelocation;
+ if (firstRelI == (unsigned)-1)
continue;
- if (read32<E>(Piece.data().data() + 4) == 0) {
+
+ if (read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
// This is a CIE, we only need to worry about the first relocation. It is
// known to point to the personality function.
- resolveReloc<ELFT>(EH, Rels[FirstRelI], Fn);
+ resolveReloc(eh, rels[firstRelI], false);
continue;
}
+
// This is a FDE. The relocations point to the described function or to
// a LSDA. We only need to keep the LSDA alive, so ignore anything that
// points to executable sections.
- typename ELFT::uint PieceEnd = Piece.InputOff + Piece.Size;
- for (unsigned I2 = FirstRelI, N2 = Rels.size(); I2 < N2; ++I2) {
- const RelTy &Rel = Rels[I2];
- if (Rel.r_offset >= PieceEnd)
- break;
- resolveReloc<ELFT>(EH, Rels[I2],
- [&](InputSectionBase *Sec, uint64_t Offset) {
- if (Sec && Sec != &InputSection::Discarded &&
- !(Sec->Flags & SHF_EXECINSTR))
- Fn(Sec, 0);
- });
- }
+ uint64_t pieceEnd = piece.inputOff + piece.size;
+ for (size_t j = firstRelI, end2 = rels.size(); j < end2; ++j)
+ if (rels[j].r_offset < pieceEnd)
+ resolveReloc(eh, rels[j], true);
}
}
-template <class ELFT>
-static void
-scanEhFrameSection(EhInputSection &EH,
- llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
- if (!EH.NumRelocations)
- return;
-
- if (EH.AreRelocsRela)
- scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Fn);
- else
- scanEhFrameSection<ELFT>(EH, EH.template rels<ELFT>(), Fn);
-}
-
// Some sections are used directly by the loader, so they should never be
// garbage-collected. This function returns true if a given section is such
// section.
-template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
- switch (Sec->Type) {
+static bool isReserved(InputSectionBase *sec) {
+ switch (sec->type) {
case SHT_FINI_ARRAY:
case SHT_INIT_ARRAY:
case SHT_NOTE:
case SHT_PREINIT_ARRAY:
return true;
default:
- StringRef S = Sec->Name;
- return S.startswith(".ctors") || S.startswith(".dtors") ||
- S.startswith(".init") || S.startswith(".fini") ||
- S.startswith(".jcr");
+ StringRef s = sec->name;
+ return s.startswith(".ctors") || s.startswith(".dtors") ||
+ s.startswith(".init") || s.startswith(".fini") ||
+ s.startswith(".jcr");
}
}
-// This is the main function of the garbage collector.
-// Starting from GC-root sections, this function visits all reachable
-// sections to set their "Live" bits.
-template <class ELFT> static void doGcSections() {
- SmallVector<InputSection *, 256> Q;
- CNamedSections.clear();
-
- auto Enqueue = [&](InputSectionBase *Sec, uint64_t Offset) {
- // Skip over discarded sections. This in theory shouldn't happen, because
- // the ELF spec doesn't allow a relocation to point to a deduplicated
- // COMDAT section directly. Unfortunately this happens in practice (e.g.
- // .eh_frame) so we need to add a check.
- if (Sec == &InputSection::Discarded)
- return;
-
+template <class ELFT>
+void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset) {
+ // Skip over discarded sections. This in theory shouldn't happen, because
+ // the ELF spec doesn't allow a relocation to point to a deduplicated
+ // COMDAT section directly. Unfortunately this happens in practice (e.g.
+ // .eh_frame) so we need to add a check.
+ if (sec == &InputSection::discarded)
+ return;
- // Usually, a whole section is marked as live or dead, but in mergeable
- // (splittable) sections, each piece of data has independent liveness bit.
- // So we explicitly tell it which offset is in use.
- if (auto *MS = dyn_cast<MergeInputSection>(Sec))
- MS->getSectionPiece(Offset)->Live = true;
+ // Usually, a whole section is marked as live or dead, but in mergeable
+ // (splittable) sections, each piece of data has independent liveness bit.
+ // So we explicitly tell it which offset is in use.
+ if (auto *ms = dyn_cast<MergeInputSection>(sec))
+ ms->getSectionPiece(offset)->live = true;
- if (Sec->Live)
- return;
- Sec->Live = true;
+ // Set Sec->Partition to the meet (i.e. the "minimum") of Partition and
+ // Sec->Partition in the following lattice: 1 < other < 0. If Sec->Partition
+ // doesn't change, we don't need to do anything.
+ if (sec->partition == 1 || sec->partition == partition)
+ return;
+ sec->partition = sec->partition ? 1 : partition;
- // Add input section to the queue.
- if (InputSection *S = dyn_cast<InputSection>(Sec))
- Q.push_back(S);
- };
+ // Add input section to the queue.
+ if (InputSection *s = dyn_cast<InputSection>(sec))
+ queue.push_back(s);
+}
- auto MarkSymbol = [&](Symbol *Sym) {
- if (auto *D = dyn_cast_or_null<Defined>(Sym))
- if (auto *IS = dyn_cast_or_null<InputSectionBase>(D->Section))
- Enqueue(IS, D->Value);
- };
+template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {
+ if (auto *d = dyn_cast_or_null<Defined>(sym))
+ if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section))
+ enqueue(isec, d->value);
+}
+// This is the main function of the garbage collector.
+// Starting from GC-root sections, this function visits all reachable
+// sections to set their "Live" bits.
+template <class ELFT> void MarkLive<ELFT>::run() {
// Add GC root symbols.
- MarkSymbol(Symtab->find(Config->Entry));
- MarkSymbol(Symtab->find(Config->Init));
- MarkSymbol(Symtab->find(Config->Fini));
- for (StringRef S : Config->Undefined)
- MarkSymbol(Symtab->find(S));
- for (StringRef S : Script->ReferencedSymbols)
- MarkSymbol(Symtab->find(S));
// Preserve externally-visible symbols if the symbols defined by this
// file can interrupt other ELF file's symbols at runtime.
- for (Symbol *S : Symtab->getSymbols())
- if (S->includeInDynsym())
- MarkSymbol(S);
+ symtab->forEachSymbol([&](Symbol *sym) {
+ if (sym->includeInDynsym() && sym->partition == partition)
+ markSymbol(sym);
+ });
+
+ // If this isn't the main partition, that's all that we need to preserve.
+ if (partition != 1) {
+ mark();
+ return;
+ }
+
+ markSymbol(symtab->find(config->entry));
+ markSymbol(symtab->find(config->init));
+ markSymbol(symtab->find(config->fini));
+ for (StringRef s : config->undefined)
+ markSymbol(symtab->find(s));
+ for (StringRef s : script->referencedSymbols)
+ markSymbol(symtab->find(s));
// Preserve special sections and those which are specified in linker
// script KEEP command.
- for (InputSectionBase *Sec : InputSections) {
+ for (InputSectionBase *sec : inputSections) {
// Mark .eh_frame sections as live because there are usually no relocations
// that point to .eh_frames. Otherwise, the garbage collector would drop
// all of them. We also want to preserve personality routines and LSDA
// referenced by .eh_frame sections, so we scan them for that here.
- if (auto *EH = dyn_cast<EhInputSection>(Sec)) {
- EH->Live = true;
- scanEhFrameSection<ELFT>(*EH, Enqueue);
+ if (auto *eh = dyn_cast<EhInputSection>(sec)) {
+ eh->markLive();
+ if (!eh->numRelocations)
+ continue;
+
+ if (eh->areRelocsRela)
+ scanEhFrameSection(*eh, eh->template relas<ELFT>());
+ else
+ scanEhFrameSection(*eh, eh->template rels<ELFT>());
}
- if (Sec->Flags & SHF_LINK_ORDER)
+ if (sec->flags & SHF_LINK_ORDER)
continue;
- if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec)) {
- Enqueue(Sec, 0);
- } else if (isValidCIdentifier(Sec->Name)) {
- CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
- CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec);
+ if (isReserved(sec) || script->shouldKeep(sec)) {
+ enqueue(sec, 0);
+ } else if (isValidCIdentifier(sec->name)) {
+ cNamedSections[saver.save("__start_" + sec->name)].push_back(sec);
+ cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec);
}
}
+ mark();
+}
+
+template <class ELFT> void MarkLive<ELFT>::mark() {
// Mark all reachable sections.
- while (!Q.empty())
- forEachSuccessor<ELFT>(*Q.pop_back_val(), Enqueue);
+ while (!queue.empty()) {
+ InputSectionBase &sec = *queue.pop_back_val();
+
+ if (sec.areRelocsRela) {
+ for (const typename ELFT::Rela &rel : sec.template relas<ELFT>())
+ resolveReloc(sec, rel, false);
+ } else {
+ for (const typename ELFT::Rel &rel : sec.template rels<ELFT>())
+ resolveReloc(sec, rel, false);
+ }
+
+ for (InputSectionBase *isec : sec.dependentSections)
+ enqueue(isec, 0);
+ }
+}
+
+// Move the sections for some symbols to the main partition, specifically ifuncs
+// (because they can result in an IRELATIVE being added to the main partition's
+// GOT, which means that the ifunc must be available when the main partition is
+// loaded) and TLS symbols (because we only know how to correctly process TLS
+// relocations for the main partition).
+template <class ELFT> void MarkLive<ELFT>::moveToMain() {
+ for (InputFile *file : objectFiles)
+ for (Symbol *s : file->getSymbols())
+ if (auto *d = dyn_cast<Defined>(s))
+ if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section &&
+ d->section->isLive())
+ markSymbol(s);
+
+ mark();
}
// Before calling this function, Live bits are off for all
// input sections. This function make some or all of them on
// so that they are emitted to the output file.
template <class ELFT> void elf::markLive() {
- if (!Config->GcSections) {
- // If -gc-sections is missing, no sections are removed.
- for (InputSectionBase *Sec : InputSections)
- Sec->Live = true;
+ // If -gc-sections is not given, no sections are removed.
+ if (!config->gcSections) {
+ for (InputSectionBase *sec : inputSections)
+ sec->markLive();
// If a DSO defines a symbol referenced in a regular object, it is needed.
- for (Symbol *Sym : Symtab->getSymbols())
- if (auto *S = dyn_cast<SharedSymbol>(Sym))
- if (S->IsUsedInRegularObj && !S->isWeak())
- S->getFile<ELFT>().IsNeeded = true;
+ symtab->forEachSymbol([](Symbol *sym) {
+ if (auto *s = dyn_cast<SharedSymbol>(sym))
+ if (s->isUsedInRegularObj && !s->isWeak())
+ s->getFile().isNeeded = true;
+ });
return;
}
+ // Otheriwse, do mark-sweep GC.
+ //
// The -gc-sections option works only for SHF_ALLOC sections
// (sections that are memory-mapped at runtime). So we can
// unconditionally make non-SHF_ALLOC sections alive except
// SHF_LINK_ORDER and SHT_REL/SHT_RELA sections.
//
- // Usually, SHF_ALLOC sections are not removed even if they are
+ // Usually, non-SHF_ALLOC sections are not removed even if they are
// unreachable through relocations because reachability is not
// a good signal whether they are garbage or not (e.g. there is
// usually no section referring to a .comment section, but we
@@ -300,22 +341,30 @@ template <class ELFT> void elf::markLive() {
// or -emit-reloc were given. And they are subject of garbage
// collection because, if we remove a text section, we also
// remove its relocation section.
- for (InputSectionBase *Sec : InputSections) {
- bool IsAlloc = (Sec->Flags & SHF_ALLOC);
- bool IsLinkOrder = (Sec->Flags & SHF_LINK_ORDER);
- bool IsRel = (Sec->Type == SHT_REL || Sec->Type == SHT_RELA);
- if (!IsAlloc && !IsLinkOrder && !IsRel)
- Sec->Live = true;
+ for (InputSectionBase *sec : inputSections) {
+ bool isAlloc = (sec->flags & SHF_ALLOC);
+ bool isLinkOrder = (sec->flags & SHF_LINK_ORDER);
+ bool isRel = (sec->type == SHT_REL || sec->type == SHT_RELA);
+
+ if (!isAlloc && !isLinkOrder && !isRel)
+ sec->markLive();
}
// Follow the graph to mark all live sections.
- doGcSections<ELFT>();
+ for (unsigned curPart = 1; curPart <= partitions.size(); ++curPart)
+ MarkLive<ELFT>(curPart).run();
+
+ // If we have multiple partitions, some sections need to live in the main
+ // partition even if they were allocated to a loadable partition. Move them
+ // there now.
+ if (partitions.size() != 1)
+ MarkLive<ELFT>(1).moveToMain();
// Report garbage-collected sections.
- if (Config->PrintGcSections)
- for (InputSectionBase *Sec : InputSections)
- if (!Sec->Live)
- message("removing unused section " + toString(Sec));
+ if (config->printGcSections)
+ for (InputSectionBase *sec : inputSections)
+ if (!sec->isLive())
+ message("removing unused section " + toString(sec));
}
template void elf::markLive<ELF32LE>();
diff --git a/ELF/MarkLive.h b/ELF/MarkLive.h
index c9b99add34de0..63b5b26691468 100644
--- a/ELF/MarkLive.h
+++ b/ELF/MarkLive.h
@@ -1,9 +1,8 @@
//===- MarkLive.h -----------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/ELF/Options.td b/ELF/Options.td
index e43a21b923d33..3ebb46f2e1b2d 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -30,7 +30,7 @@ def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
def build_id: F<"build-id">, HelpText<"Alias for --build-id=fast">;
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">,
- MetaVarName<"[fast,md5,sha,uuid,0x<hexstring>]">;
+ MetaVarName<"[fast,md5,sha1,uuid,0x<hexstring>]">;
defm check_sections: B<"check-sections",
"Check section addresses for overlaps (default)",
@@ -63,10 +63,18 @@ defm allow_multiple_definition: B<"allow-multiple-definition",
"Allow multiple definitions",
"Do not allow multiple definitions (default)">;
+defm allow_shlib_undefined: B<"allow-shlib-undefined",
+ "Allow unresolved references in shared libraries (default when linking a shared library)",
+ "Do not allow unresolved references in shared libraries (default when linking an executable)">;
+
defm apply_dynamic_relocs: B<"apply-dynamic-relocs",
"Apply link-time values for dynamic relocations",
"Do not apply link-time values for dynamic relocations (default)">;
+defm dependent_libraries: B<"dependent-libraries",
+ "Process dependent library specifiers from input files (default)",
+ "Ignore dependent library specifiers from input files">;
+
defm as_needed: B<"as-needed",
"Only set DT_NEEDED for shared libraries if used",
"Always set DT_NEEDED for shared libraries (default)">;
@@ -163,6 +171,13 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
+// This option is intentionally hidden from the user as the implementation
+// is not complete.
+def require_cet: F<"require-cet">;
+
+def force_bti: F<"force-bti">,
+ HelpText<"Force enable AArch64 BTI in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property">;
+
defm format: Eq<"format", "Change the input format of the inputs following this option">,
MetaVarName<"[default,elf,binary]">;
@@ -214,6 +229,9 @@ defm merge_exidx_entries: B<"merge-exidx-entries",
"Enable merging .ARM.exidx entries (default)",
"Disable merging .ARM.exidx entries">;
+def nmagic: F<"nmagic">, MetaVarName<"<magic>">,
+ HelpText<"Do not page align sections, link against static libraries.">;
+
def nostdlib: F<"nostdlib">,
HelpText<"Only search directories specified on the command line">;
@@ -226,8 +244,11 @@ def no_dynamic_linker: F<"no-dynamic-linker">,
def noinhibit_exec: F<"noinhibit-exec">,
HelpText<"Retain the executable output file whenever it is still usable">;
+def no_nmagic: F<"no-nmagic">, MetaVarName<"<magic>">,
+ HelpText<"Page align sections (default)">;
+
def no_omagic: F<"no-omagic">, MetaVarName<"<magic>">,
- HelpText<"Do not set the text data sections to be writable">;
+ HelpText<"Do not set the text data sections to be writable, page align sections (default)">;
def no_rosegment: F<"no-rosegment">,
HelpText<"Do not put read-only non-executable sections in their own segment">;
@@ -242,7 +263,7 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
HelpText<"Specify the binary format for the output object file">;
def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
- HelpText<"Set the text and data sections to be readable and writable">;
+ HelpText<"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries">;
defm orphan_handling:
Eq<"orphan-handling", "Control how orphan sections are handled when linker script used">;
@@ -251,10 +272,16 @@ defm pack_dyn_relocs:
Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
MetaVarName<"[none,android,relr,android+relr]">;
+def pac_plt: F<"pac-plt">,
+ HelpText<"AArch64 only, use pointer authentication in PLT">;
+
defm use_android_relr_tags: B<"use-android-relr-tags",
"Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*",
"Use SHT_RELR / DT_RELR* tags (default)">;
+def pic_veneer: F<"pic-veneer">,
+ HelpText<"Always generate position independent thunks (veneers)">;
+
defm pie: B<"pie",
"Create a position independent executable",
"Do not create a position independent executable (default)">;
@@ -267,6 +294,9 @@ defm print_icf_sections: B<"print-icf-sections",
"List identical folded sections",
"Do not list identical folded sections (default)">;
+defm print_symbol_order: Eq<"print-symbol-order",
+ "Print a symbol order specified by --call-graph-ordering-file into the speficied file">;
+
def pop_state: F<"pop-state">,
HelpText<"Undo the effect of -push-state">;
@@ -336,6 +366,9 @@ defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">;
defm undefined: Eq<"undefined", "Force undefined symbol during linking">,
MetaVarName<"<symbol>">;
+defm undefined_glob: Eq<"undefined-glob", "Force undefined symbol during linking">,
+ MetaVarName<"<pattern>">;
+
defm unresolved_symbols:
Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">;
@@ -383,6 +416,9 @@ defm wrap: Eq<"wrap", "Use wrapper functions for symbol">,
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
+def visual_studio_diagnostics_format : F<"vs-diagnostics">,
+HelpText<"Format diagnostics for Visual Studio compatiblity">;
+
// Aliases
def: Separate<["-"], "f">, Alias<auxiliary>, HelpText<"Alias for --auxiliary">;
def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
@@ -404,6 +440,7 @@ def: Separate<["-"], "b">, Alias<format>, HelpText<"Alias for --format">;
def: JoinedOrSeparate<["-"], "l">, Alias<library>, HelpText<"Alias for --library">;
def: JoinedOrSeparate<["-"], "L">, Alias<library_path>, HelpText<"Alias for --library-path">;
def: F<"no-pic-executable">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
+def: Flag<["-"], "n">, Alias<nmagic>, HelpText<"Alias for --nmagic">;
def: Flag<["-"], "N">, Alias<omagic>, HelpText<"Alias for --omagic">;
def: Joined<["--"], "output=">, Alias<o>, HelpText<"Alias for -o">;
def: Separate<["--"], "output">, Alias<o>, HelpText<"Alias for -o">;
@@ -437,14 +474,22 @@ def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
def lto_partitions: J<"lto-partitions=">,
HelpText<"Number of LTO codegen partitions">;
+def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
+ HelpText<"Perform context senstive PGO instrumentation">;
+def lto_cs_profile_file: J<"lto-cs-profile-file=">,
+ HelpText<"Context sensitive profile file path">;
def lto_sample_profile: J<"lto-sample-profile=">,
HelpText<"Sample profile file path">;
def disable_verify: F<"disable-verify">;
defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">;
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
HelpText<"YAML output file for optimization remarks">;
+def opt_remarks_passes: Separate<["--"], "opt-remarks-passes">,
+ HelpText<"Regex for the passes that need to be serialized to the output file">;
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
HelpText<"Include hotness information in the optimization remarks file">;
+def opt_remarks_format: Separate<["--"], "opt-remarks-format">,
+ HelpText<"The format used for serializing remarks (default: YAML)">;
defm plugin_opt: Eq<"plugin-opt", "specifies LTO options for compatibility with GNU linkers">;
def save_temps: F<"save-temps">;
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
@@ -465,6 +510,10 @@ def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
def: F<"plugin-opt=new-pass-manager">,
Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
def plugin_opt_obj_path_eq: J<"plugin-opt=obj-path=">;
+def: F<"plugin-opt=cs-profile-generate">,
+ Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">;
+def: J<"plugin-opt=cs-profile-path=">,
+ Alias<lto_cs_profile_file>, HelpText<"Alias for -lto-cs-profile-file">;
def: J<"plugin-opt=sample-profile=">,
Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
@@ -489,19 +538,20 @@ def plugin_opt_thinlto: J<"plugin-opt=thinlto">;
def plugin_opt_slash: J<"plugin-opt=/">;
// Options listed below are silently ignored for now for compatibility.
-def: F<"allow-shlib-undefined">;
def: F<"detect-odr-violations">;
def: Flag<["-"], "g">;
def: F<"long-plt">;
def: F<"no-add-needed">;
-def: F<"no-allow-shlib-undefined">;
def: F<"no-copy-dt-needed-entries">;
def: F<"no-ctors-in-init-array">;
def: F<"no-keep-memory">;
def: F<"no-mmap-output-file">;
+def: F<"no-pipeline-knowledge">;
def: F<"no-warn-mismatch">;
+def: Flag<["-"], "p">;
def: Separate<["--", "-"], "rpath-link">;
def: J<"rpath-link=">;
+def: F<"secure-plt">;
def: F<"sort-common">;
def: F<"stats">;
def: F<"warn-execstack">;
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index c1442c0787361..a89bd509bc96d 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -1,9 +1,8 @@
//===- OutputSections.cpp -------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -31,47 +30,46 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-uint8_t Out::First;
-PhdrEntry *Out::TlsPhdr;
-OutputSection *Out::ElfHeader;
-OutputSection *Out::ProgramHeaders;
-OutputSection *Out::PreinitArray;
-OutputSection *Out::InitArray;
-OutputSection *Out::FiniArray;
+uint8_t *Out::bufferStart;
+uint8_t Out::first;
+PhdrEntry *Out::tlsPhdr;
+OutputSection *Out::elfHeader;
+OutputSection *Out::programHeaders;
+OutputSection *Out::preinitArray;
+OutputSection *Out::initArray;
+OutputSection *Out::finiArray;
-std::vector<OutputSection *> elf::OutputSections;
+std::vector<OutputSection *> elf::outputSections;
uint32_t OutputSection::getPhdrFlags() const {
- uint32_t Ret = 0;
- if (Config->EMachine != EM_ARM || !(Flags & SHF_ARM_PURECODE))
- Ret |= PF_R;
- if (Flags & SHF_WRITE)
- Ret |= PF_W;
- if (Flags & SHF_EXECINSTR)
- Ret |= PF_X;
- return Ret;
+ uint32_t ret = 0;
+ if (config->emachine != EM_ARM || !(flags & SHF_ARM_PURECODE))
+ ret |= PF_R;
+ if (flags & SHF_WRITE)
+ ret |= PF_W;
+ if (flags & SHF_EXECINSTR)
+ ret |= PF_X;
+ return ret;
}
template <class ELFT>
-void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) {
- Shdr->sh_entsize = Entsize;
- Shdr->sh_addralign = Alignment;
- Shdr->sh_type = Type;
- Shdr->sh_offset = Offset;
- Shdr->sh_flags = Flags;
- Shdr->sh_info = Info;
- Shdr->sh_link = Link;
- Shdr->sh_addr = Addr;
- Shdr->sh_size = Size;
- Shdr->sh_name = ShName;
+void OutputSection::writeHeaderTo(typename ELFT::Shdr *shdr) {
+ shdr->sh_entsize = entsize;
+ shdr->sh_addralign = alignment;
+ shdr->sh_type = type;
+ shdr->sh_offset = offset;
+ shdr->sh_flags = flags;
+ shdr->sh_info = info;
+ shdr->sh_link = link;
+ shdr->sh_addr = addr;
+ shdr->sh_size = size;
+ shdr->sh_name = shName;
}
-OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
+OutputSection::OutputSection(StringRef name, uint32_t type, uint64_t flags)
: BaseCommand(OutputSectionKind),
- SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
- /*Info*/ 0, /*Link*/ 0) {
- Live = false;
-}
+ SectionBase(Output, name, flags, /*Entsize*/ 0, /*Alignment*/ 1, type,
+ /*Info*/ 0, /*Link*/ 0) {}
// We allow sections of types listed below to merged into a
// single progbits section. This is typically done by linker
@@ -79,257 +77,248 @@ OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
// to be allocated for nobits sections. Other ones don't require
// any special treatment on top of progbits, so there doesn't
// seem to be a harm in merging them.
-static bool canMergeToProgbits(unsigned Type) {
- return Type == SHT_NOBITS || Type == SHT_PROGBITS || Type == SHT_INIT_ARRAY ||
- Type == SHT_PREINIT_ARRAY || Type == SHT_FINI_ARRAY ||
- Type == SHT_NOTE;
+static bool canMergeToProgbits(unsigned type) {
+ return type == SHT_NOBITS || type == SHT_PROGBITS || type == SHT_INIT_ARRAY ||
+ type == SHT_PREINIT_ARRAY || type == SHT_FINI_ARRAY ||
+ type == SHT_NOTE;
}
-void OutputSection::addSection(InputSection *IS) {
- if (!Live) {
+void OutputSection::addSection(InputSection *isec) {
+ if (!hasInputSections) {
// If IS is the first section to be added to this section,
- // initialize Type, Entsize and flags from IS.
- Live = true;
- Type = IS->Type;
- Entsize = IS->Entsize;
- Flags = IS->Flags;
+ // initialize Partition, Type, Entsize and flags from IS.
+ hasInputSections = true;
+ partition = isec->partition;
+ type = isec->type;
+ entsize = isec->entsize;
+ flags = isec->flags;
} else {
// Otherwise, check if new type or flags are compatible with existing ones.
- unsigned Mask = SHF_TLS | SHF_LINK_ORDER;
- if ((Flags & Mask) != (IS->Flags & Mask))
- error("incompatible section flags for " + Name + "\n>>> " + toString(IS) +
- ": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name +
- ": 0x" + utohexstr(Flags));
-
- if (Type != IS->Type) {
- if (!canMergeToProgbits(Type) || !canMergeToProgbits(IS->Type))
- error("section type mismatch for " + IS->Name + "\n>>> " +
- toString(IS) + ": " +
- getELFSectionTypeName(Config->EMachine, IS->Type) +
- "\n>>> output section " + Name + ": " +
- getELFSectionTypeName(Config->EMachine, Type));
- Type = SHT_PROGBITS;
+ unsigned mask = SHF_TLS | SHF_LINK_ORDER;
+ if ((flags & mask) != (isec->flags & mask))
+ error("incompatible section flags for " + name + "\n>>> " + toString(isec) +
+ ": 0x" + utohexstr(isec->flags) + "\n>>> output section " + name +
+ ": 0x" + utohexstr(flags));
+
+ if (type != isec->type) {
+ if (!canMergeToProgbits(type) || !canMergeToProgbits(isec->type))
+ error("section type mismatch for " + isec->name + "\n>>> " +
+ toString(isec) + ": " +
+ getELFSectionTypeName(config->emachine, isec->type) +
+ "\n>>> output section " + name + ": " +
+ getELFSectionTypeName(config->emachine, type));
+ type = SHT_PROGBITS;
}
}
- IS->Parent = this;
- uint64_t AndMask =
- Config->EMachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0;
- uint64_t OrMask = ~AndMask;
- uint64_t AndFlags = (Flags & IS->Flags) & AndMask;
- uint64_t OrFlags = (Flags | IS->Flags) & OrMask;
- Flags = AndFlags | OrFlags;
+ isec->parent = this;
+ uint64_t andMask =
+ config->emachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0;
+ uint64_t orMask = ~andMask;
+ uint64_t andFlags = (flags & isec->flags) & andMask;
+ uint64_t orFlags = (flags | isec->flags) & orMask;
+ flags = andFlags | orFlags;
- Alignment = std::max(Alignment, IS->Alignment);
+ alignment = std::max(alignment, isec->alignment);
// If this section contains a table of fixed-size entries, sh_entsize
// holds the element size. If it contains elements of different size we
// set sh_entsize to 0.
- if (Entsize != IS->Entsize)
- Entsize = 0;
-
- if (!IS->Assigned) {
- IS->Assigned = true;
- if (SectionCommands.empty() ||
- !isa<InputSectionDescription>(SectionCommands.back()))
- SectionCommands.push_back(make<InputSectionDescription>(""));
- auto *ISD = cast<InputSectionDescription>(SectionCommands.back());
- ISD->Sections.push_back(IS);
+ if (entsize != isec->entsize)
+ entsize = 0;
+
+ if (!isec->assigned) {
+ isec->assigned = true;
+ if (sectionCommands.empty() ||
+ !isa<InputSectionDescription>(sectionCommands.back()))
+ sectionCommands.push_back(make<InputSectionDescription>(""));
+ auto *isd = cast<InputSectionDescription>(sectionCommands.back());
+ isd->sections.push_back(isec);
}
}
-static void sortByOrder(MutableArrayRef<InputSection *> In,
- llvm::function_ref<int(InputSectionBase *S)> Order) {
- typedef std::pair<int, InputSection *> Pair;
- auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
+static void sortByOrder(MutableArrayRef<InputSection *> in,
+ llvm::function_ref<int(InputSectionBase *s)> order) {
+ std::vector<std::pair<int, InputSection *>> v;
+ for (InputSection *s : in)
+ v.push_back({order(s), s});
+ llvm::stable_sort(v, less_first());
- std::vector<Pair> V;
- for (InputSection *S : In)
- V.push_back({Order(S), S});
- std::stable_sort(V.begin(), V.end(), Comp);
-
- for (size_t I = 0; I < V.size(); ++I)
- In[I] = V[I].second;
+ for (size_t i = 0; i < v.size(); ++i)
+ in[i] = v[i].second;
}
uint64_t elf::getHeaderSize() {
- if (Config->OFormatBinary)
+ if (config->oFormatBinary)
return 0;
- return Out::ElfHeader->Size + Out::ProgramHeaders->Size;
+ return Out::elfHeader->size + Out::programHeaders->size;
}
-bool OutputSection::classof(const BaseCommand *C) {
- return C->Kind == OutputSectionKind;
+bool OutputSection::classof(const BaseCommand *c) {
+ return c->kind == OutputSectionKind;
}
-void OutputSection::sort(llvm::function_ref<int(InputSectionBase *S)> Order) {
- assert(Live);
- for (BaseCommand *B : SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(B))
- sortByOrder(ISD->Sections, Order);
+void OutputSection::sort(llvm::function_ref<int(InputSectionBase *s)> order) {
+ assert(isLive());
+ for (BaseCommand *b : sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(b))
+ sortByOrder(isd->sections, order);
}
// Fill [Buf, Buf + Size) with Filler.
// This is used for linker script "=fillexp" command.
-static void fill(uint8_t *Buf, size_t Size,
- const std::array<uint8_t, 4> &Filler) {
- size_t I = 0;
- for (; I + 4 < Size; I += 4)
- memcpy(Buf + I, Filler.data(), 4);
- memcpy(Buf + I, Filler.data(), Size - I);
+static void fill(uint8_t *buf, size_t size,
+ const std::array<uint8_t, 4> &filler) {
+ size_t i = 0;
+ for (; i + 4 < size; i += 4)
+ memcpy(buf + i, filler.data(), 4);
+ memcpy(buf + i, filler.data(), size - i);
}
// Compress section contents if this section contains debug info.
template <class ELFT> void OutputSection::maybeCompress() {
- typedef typename ELFT::Chdr Elf_Chdr;
+ using Elf_Chdr = typename ELFT::Chdr;
// Compress only DWARF debug sections.
- if (!Config->CompressDebugSections || (Flags & SHF_ALLOC) ||
- !Name.startswith(".debug_"))
+ if (!config->compressDebugSections || (flags & SHF_ALLOC) ||
+ !name.startswith(".debug_"))
return;
// Create a section header.
- ZDebugHeader.resize(sizeof(Elf_Chdr));
- auto *Hdr = reinterpret_cast<Elf_Chdr *>(ZDebugHeader.data());
- Hdr->ch_type = ELFCOMPRESS_ZLIB;
- Hdr->ch_size = Size;
- Hdr->ch_addralign = Alignment;
+ zDebugHeader.resize(sizeof(Elf_Chdr));
+ auto *hdr = reinterpret_cast<Elf_Chdr *>(zDebugHeader.data());
+ hdr->ch_type = ELFCOMPRESS_ZLIB;
+ hdr->ch_size = size;
+ hdr->ch_addralign = alignment;
// Write section contents to a temporary buffer and compress it.
- std::vector<uint8_t> Buf(Size);
- writeTo<ELFT>(Buf.data());
- if (Error E = zlib::compress(toStringRef(Buf), CompressedData))
- fatal("compress failed: " + llvm::toString(std::move(E)));
+ std::vector<uint8_t> buf(size);
+ writeTo<ELFT>(buf.data());
+ if (Error e = zlib::compress(toStringRef(buf), compressedData))
+ fatal("compress failed: " + llvm::toString(std::move(e)));
// Update section headers.
- Size = sizeof(Elf_Chdr) + CompressedData.size();
- Flags |= SHF_COMPRESSED;
+ size = sizeof(Elf_Chdr) + compressedData.size();
+ flags |= SHF_COMPRESSED;
}
-static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
- if (Size == 1)
- *Buf = Data;
- else if (Size == 2)
- write16(Buf, Data);
- else if (Size == 4)
- write32(Buf, Data);
- else if (Size == 8)
- write64(Buf, Data);
+static void writeInt(uint8_t *buf, uint64_t data, uint64_t size) {
+ if (size == 1)
+ *buf = data;
+ else if (size == 2)
+ write16(buf, data);
+ else if (size == 4)
+ write32(buf, data);
+ else if (size == 8)
+ write64(buf, data);
else
llvm_unreachable("unsupported Size argument");
}
-template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
- if (Type == SHT_NOBITS)
+template <class ELFT> void OutputSection::writeTo(uint8_t *buf) {
+ if (type == SHT_NOBITS)
return;
- Loc = Buf;
-
// If -compress-debug-section is specified and if this is a debug seciton,
// we've already compressed section contents. If that's the case,
// just write it down.
- if (!CompressedData.empty()) {
- memcpy(Buf, ZDebugHeader.data(), ZDebugHeader.size());
- memcpy(Buf + ZDebugHeader.size(), CompressedData.data(),
- CompressedData.size());
+ if (!compressedData.empty()) {
+ memcpy(buf, zDebugHeader.data(), zDebugHeader.size());
+ memcpy(buf + zDebugHeader.size(), compressedData.data(),
+ compressedData.size());
return;
}
// Write leading padding.
- std::vector<InputSection *> Sections = getInputSections(this);
- std::array<uint8_t, 4> Filler = getFiller();
- bool NonZeroFiller = read32(Filler.data()) != 0;
- if (NonZeroFiller)
- fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
+ std::vector<InputSection *> sections = getInputSections(this);
+ std::array<uint8_t, 4> filler = getFiller();
+ bool nonZeroFiller = read32(filler.data()) != 0;
+ if (nonZeroFiller)
+ fill(buf, sections.empty() ? size : sections[0]->outSecOff, filler);
- parallelForEachN(0, Sections.size(), [&](size_t I) {
- InputSection *IS = Sections[I];
- IS->writeTo<ELFT>(Buf);
+ parallelForEachN(0, sections.size(), [&](size_t i) {
+ InputSection *isec = sections[i];
+ isec->writeTo<ELFT>(buf);
// Fill gaps between sections.
- if (NonZeroFiller) {
- uint8_t *Start = Buf + IS->OutSecOff + IS->getSize();
- uint8_t *End;
- if (I + 1 == Sections.size())
- End = Buf + Size;
+ if (nonZeroFiller) {
+ uint8_t *start = buf + isec->outSecOff + isec->getSize();
+ uint8_t *end;
+ if (i + 1 == sections.size())
+ end = buf + size;
else
- End = Buf + Sections[I + 1]->OutSecOff;
- fill(Start, End - Start, Filler);
+ end = buf + sections[i + 1]->outSecOff;
+ fill(start, end - start, filler);
}
});
// Linker scripts may have BYTE()-family commands with which you
// can write arbitrary bytes to the output. Process them if any.
- for (BaseCommand *Base : SectionCommands)
- if (auto *Data = dyn_cast<ByteCommand>(Base))
- writeInt(Buf + Data->Offset, Data->Expression().getValue(), Data->Size);
+ for (BaseCommand *base : sectionCommands)
+ if (auto *data = dyn_cast<ByteCommand>(base))
+ writeInt(buf + data->offset, data->expression().getValue(), data->size);
}
-template <class ELFT>
-static void finalizeShtGroup(OutputSection *OS,
- InputSection *Section) {
- assert(Config->Relocatable);
+static void finalizeShtGroup(OutputSection *os,
+ InputSection *section) {
+ assert(config->relocatable);
// sh_link field for SHT_GROUP sections should contain the section index of
// the symbol table.
- OS->Link = In.SymTab->getParent()->SectionIndex;
+ os->link = in.symTab->getParent()->sectionIndex;
// sh_info then contain index of an entry in symbol table section which
// provides signature of the section group.
- ObjFile<ELFT> *Obj = Section->getFile<ELFT>();
- ArrayRef<Symbol *> Symbols = Obj->getSymbols();
- OS->Info = In.SymTab->getSymbolIndex(Symbols[Section->Info]);
+ ArrayRef<Symbol *> symbols = section->file->getSymbols();
+ os->info = in.symTab->getSymbolIndex(symbols[section->info]);
}
-template <class ELFT> void OutputSection::finalize() {
- if (Type == SHT_NOBITS)
- for (BaseCommand *Base : SectionCommands)
- if (isa<ByteCommand>(Base))
- Type = SHT_PROGBITS;
-
- std::vector<InputSection *> V = getInputSections(this);
- InputSection *First = V.empty() ? nullptr : V[0];
+void OutputSection::finalize() {
+ std::vector<InputSection *> v = getInputSections(this);
+ InputSection *first = v.empty() ? nullptr : v[0];
- if (Flags & SHF_LINK_ORDER) {
+ if (flags & SHF_LINK_ORDER) {
// We must preserve the link order dependency of sections with the
// SHF_LINK_ORDER flag. The dependency is indicated by the sh_link field. We
// need to translate the InputSection sh_link to the OutputSection sh_link,
// all InputSections in the OutputSection have the same dependency.
- if (auto *D = First->getLinkOrderDep())
- Link = D->getParent()->SectionIndex;
+ if (auto *ex = dyn_cast<ARMExidxSyntheticSection>(first))
+ link = ex->getLinkOrderDep()->getParent()->sectionIndex;
+ else if (auto *d = first->getLinkOrderDep())
+ link = d->getParent()->sectionIndex;
}
- if (Type == SHT_GROUP) {
- finalizeShtGroup<ELFT>(this, First);
+ if (type == SHT_GROUP) {
+ finalizeShtGroup(this, first);
return;
}
- if (!Config->CopyRelocs || (Type != SHT_RELA && Type != SHT_REL))
+ if (!config->copyRelocs || (type != SHT_RELA && type != SHT_REL))
return;
- if (isa<SyntheticSection>(First))
+ if (isa<SyntheticSection>(first))
return;
- Link = In.SymTab->getParent()->SectionIndex;
+ link = in.symTab->getParent()->sectionIndex;
// sh_info for SHT_REL[A] sections should contain the section header index of
// the section to which the relocation applies.
- InputSectionBase *S = First->getRelocatedSection();
- Info = S->getOutputSection()->SectionIndex;
- Flags |= SHF_INFO_LINK;
+ InputSectionBase *s = first->getRelocatedSection();
+ info = s->getOutputSection()->sectionIndex;
+ flags |= SHF_INFO_LINK;
}
// Returns true if S matches /Filename.?\.o$/.
-static bool isCrtBeginEnd(StringRef S, StringRef Filename) {
- if (!S.endswith(".o"))
+static bool isCrtBeginEnd(StringRef s, StringRef filename) {
+ if (!s.endswith(".o"))
return false;
- S = S.drop_back(2);
- if (S.endswith(Filename))
+ s = s.drop_back(2);
+ if (s.endswith(filename))
return true;
- return !S.empty() && S.drop_back().endswith(Filename);
+ return !s.empty() && s.drop_back().endswith(filename);
}
-static bool isCrtbegin(StringRef S) { return isCrtBeginEnd(S, "crtbegin"); }
-static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
+static bool isCrtbegin(StringRef s) { return isCrtBeginEnd(s, "crtbegin"); }
+static bool isCrtend(StringRef s) { return isCrtBeginEnd(s, "crtend"); }
// .ctors and .dtors are sorted by this priority from highest to lowest.
//
@@ -349,52 +338,52 @@ static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
// .ctors are duplicate features (and .init_array is newer.) However, there
// are too many real-world use cases of .ctors, so we had no choice to
// support that with this rather ad-hoc semantics.
-static bool compCtors(const InputSection *A, const InputSection *B) {
- bool BeginA = isCrtbegin(A->File->getName());
- bool BeginB = isCrtbegin(B->File->getName());
- if (BeginA != BeginB)
- return BeginA;
- bool EndA = isCrtend(A->File->getName());
- bool EndB = isCrtend(B->File->getName());
- if (EndA != EndB)
- return EndB;
- StringRef X = A->Name;
- StringRef Y = B->Name;
- assert(X.startswith(".ctors") || X.startswith(".dtors"));
- assert(Y.startswith(".ctors") || Y.startswith(".dtors"));
- X = X.substr(6);
- Y = Y.substr(6);
- return X < Y;
+static bool compCtors(const InputSection *a, const InputSection *b) {
+ bool beginA = isCrtbegin(a->file->getName());
+ bool beginB = isCrtbegin(b->file->getName());
+ if (beginA != beginB)
+ return beginA;
+ bool endA = isCrtend(a->file->getName());
+ bool endB = isCrtend(b->file->getName());
+ if (endA != endB)
+ return endB;
+ StringRef x = a->name;
+ StringRef y = b->name;
+ assert(x.startswith(".ctors") || x.startswith(".dtors"));
+ assert(y.startswith(".ctors") || y.startswith(".dtors"));
+ x = x.substr(6);
+ y = y.substr(6);
+ return x < y;
}
// Sorts input sections by the special rules for .ctors and .dtors.
// Unfortunately, the rules are different from the one for .{init,fini}_array.
// Read the comment above.
void OutputSection::sortCtorsDtors() {
- assert(SectionCommands.size() == 1);
- auto *ISD = cast<InputSectionDescription>(SectionCommands[0]);
- std::stable_sort(ISD->Sections.begin(), ISD->Sections.end(), compCtors);
+ assert(sectionCommands.size() == 1);
+ auto *isd = cast<InputSectionDescription>(sectionCommands[0]);
+ llvm::stable_sort(isd->sections, compCtors);
}
// If an input string is in the form of "foo.N" where N is a number,
// return N. Otherwise, returns 65536, which is one greater than the
// lowest priority.
-int elf::getPriority(StringRef S) {
- size_t Pos = S.rfind('.');
- if (Pos == StringRef::npos)
+int elf::getPriority(StringRef s) {
+ size_t pos = s.rfind('.');
+ if (pos == StringRef::npos)
return 65536;
- int V;
- if (!to_integer(S.substr(Pos + 1), V, 10))
+ int v;
+ if (!to_integer(s.substr(pos + 1), v, 10))
return 65536;
- return V;
+ return v;
}
-std::vector<InputSection *> elf::getInputSections(OutputSection *OS) {
- std::vector<InputSection *> Ret;
- for (BaseCommand *Base : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
- Ret.insert(Ret.end(), ISD->Sections.begin(), ISD->Sections.end());
- return Ret;
+std::vector<InputSection *> elf::getInputSections(OutputSection *os) {
+ std::vector<InputSection *> ret;
+ for (BaseCommand *base : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ ret.insert(ret.end(), isd->sections.begin(), isd->sections.end());
+ return ret;
}
// Sorts input sections by section name suffixes, so that .foo.N comes
@@ -405,14 +394,14 @@ std::vector<InputSection *> elf::getInputSections(OutputSection *OS) {
// For more detail, read the section of the GCC's manual about init_priority.
void OutputSection::sortInitFini() {
// Sort sections by priority.
- sort([](InputSectionBase *S) { return getPriority(S->Name); });
+ sort([](InputSectionBase *s) { return getPriority(s->name); });
}
std::array<uint8_t, 4> OutputSection::getFiller() {
- if (Filler)
- return *Filler;
- if (Flags & SHF_EXECINSTR)
- return Target->TrapInstr;
+ if (filler)
+ return *filler;
+ if (flags & SHF_EXECINSTR)
+ return target->trapInstr;
return {0, 0, 0, 0};
}
@@ -430,8 +419,3 @@ template void OutputSection::maybeCompress<ELF32LE>();
template void OutputSection::maybeCompress<ELF32BE>();
template void OutputSection::maybeCompress<ELF64LE>();
template void OutputSection::maybeCompress<ELF64BE>();
-
-template void OutputSection::finalize<ELF32LE>();
-template void OutputSection::finalize<ELF32BE>();
-template void OutputSection::finalize<ELF64LE>();
-template void OutputSection::finalize<ELF64BE>();
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index 113bf6836926d..fff8327ea376c 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -1,9 +1,8 @@
//===- OutputSections.h -----------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -23,17 +22,8 @@ namespace lld {
namespace elf {
struct PhdrEntry;
-class Symbol;
-struct EhSectionPiece;
-class EhInputSection;
class InputSection;
class InputSectionBase;
-class MergeInputSection;
-class OutputSection;
-template <class ELFT> class ObjFile;
-template <class ELFT> class SharedFile;
-class SharedSymbol;
-class Defined;
// This represents a section in an output file.
// It is composed of multiple InputSections.
@@ -41,19 +31,19 @@ class Defined;
// non-overlapping file offsets and VAs.
class OutputSection final : public BaseCommand, public SectionBase {
public:
- OutputSection(StringRef Name, uint32_t Type, uint64_t Flags);
+ OutputSection(StringRef name, uint32_t type, uint64_t flags);
- static bool classof(const SectionBase *S) {
- return S->kind() == SectionBase::Output;
+ static bool classof(const SectionBase *s) {
+ return s->kind() == SectionBase::Output;
}
- static bool classof(const BaseCommand *C);
+ static bool classof(const BaseCommand *c);
- uint64_t getLMA() const { return PtLoad ? Addr + PtLoad->LMAOffset : Addr; }
- template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
+ uint64_t getLMA() const { return ptLoad ? addr + ptLoad->lmaOffset : addr; }
+ template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *sHdr);
- uint32_t SectionIndex = UINT32_MAX;
- unsigned SortRank;
+ uint32_t sectionIndex = UINT32_MAX;
+ unsigned sortRank;
uint32_t getPhdrFlags() const;
@@ -64,78 +54,82 @@ public:
// section offset we use the following formula: Off = Off_first + VA -
// VA_first, where Off_first and VA_first is file offset and VA of first
// section in PT_LOAD.
- PhdrEntry *PtLoad = nullptr;
+ PhdrEntry *ptLoad = nullptr;
// Pointer to a relocation section for this section. Usually nullptr because
// we consume relocations, but if --emit-relocs is specified (which is rare),
// it may have a non-null value.
- OutputSection *RelocationSection = nullptr;
+ OutputSection *relocationSection = nullptr;
// Initially this field is the number of InputSections that have been added to
// the OutputSection so far. Later on, after a call to assignAddresses, it
// corresponds to the Elf_Shdr member.
- uint64_t Size = 0;
+ uint64_t size = 0;
// The following fields correspond to Elf_Shdr members.
- uint64_t Offset = 0;
- uint64_t Addr = 0;
- uint32_t ShName = 0;
-
- void addSection(InputSection *IS);
+ uint64_t offset = 0;
+ uint64_t addr = 0;
+ uint32_t shName = 0;
- // Location in the output buffer.
- uint8_t *Loc = nullptr;
+ void addSection(InputSection *isec);
// The following members are normally only used in linker scripts.
- MemoryRegion *MemRegion = nullptr;
- MemoryRegion *LMARegion = nullptr;
- Expr AddrExpr;
- Expr AlignExpr;
- Expr LMAExpr;
- Expr SubalignExpr;
- std::vector<BaseCommand *> SectionCommands;
- std::vector<StringRef> Phdrs;
- llvm::Optional<std::array<uint8_t, 4>> Filler;
- ConstraintKind Constraint = ConstraintKind::NoConstraint;
- std::string Location;
- std::string MemoryRegionName;
- std::string LMARegionName;
- bool NonAlloc = false;
- bool Noload = false;
- bool ExpressionsUseSymbols = false;
- bool InOverlay = false;
-
- template <class ELFT> void finalize();
- template <class ELFT> void writeTo(uint8_t *Buf);
+ MemoryRegion *memRegion = nullptr;
+ MemoryRegion *lmaRegion = nullptr;
+ Expr addrExpr;
+ Expr alignExpr;
+ Expr lmaExpr;
+ Expr subalignExpr;
+ std::vector<BaseCommand *> sectionCommands;
+ std::vector<StringRef> phdrs;
+ llvm::Optional<std::array<uint8_t, 4>> filler;
+ ConstraintKind constraint = ConstraintKind::NoConstraint;
+ std::string location;
+ std::string memoryRegionName;
+ std::string lmaRegionName;
+ bool nonAlloc = false;
+ bool noload = false;
+ bool expressionsUseSymbols = false;
+ bool usedInExpression = false;
+ bool inOverlay = false;
+
+ // Tracks whether the section has ever had an input section added to it, even
+ // if the section was later removed (e.g. because it is a synthetic section
+ // that wasn't needed). This is needed for orphan placement.
+ bool hasInputSections = false;
+
+ void finalize();
+ template <class ELFT> void writeTo(uint8_t *buf);
template <class ELFT> void maybeCompress();
- void sort(llvm::function_ref<int(InputSectionBase *S)> Order);
+ void sort(llvm::function_ref<int(InputSectionBase *s)> order);
void sortInitFini();
void sortCtorsDtors();
private:
// Used for implementation of --compress-debug-sections option.
- std::vector<uint8_t> ZDebugHeader;
- llvm::SmallVector<char, 1> CompressedData;
+ std::vector<uint8_t> zDebugHeader;
+ llvm::SmallVector<char, 1> compressedData;
std::array<uint8_t, 4> getFiller();
};
-int getPriority(StringRef S);
+int getPriority(StringRef s);
-std::vector<InputSection *> getInputSections(OutputSection* OS);
+std::vector<InputSection *> getInputSections(OutputSection* os);
// All output sections that are handled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
// until Writer is initialized.
struct Out {
- static uint8_t First;
- static PhdrEntry *TlsPhdr;
- static OutputSection *ElfHeader;
- static OutputSection *ProgramHeaders;
- static OutputSection *PreinitArray;
- static OutputSection *InitArray;
- static OutputSection *FiniArray;
+ static uint8_t *bufferStart;
+ static uint8_t first;
+ static PhdrEntry *tlsPhdr;
+ static OutputSection *elfHeader;
+ static OutputSection *programHeaders;
+ static OutputSection *preinitArray;
+ static OutputSection *initArray;
+ static OutputSection *finiArray;
};
} // namespace elf
@@ -146,7 +140,7 @@ namespace elf {
uint64_t getHeaderSize();
-extern std::vector<OutputSection *> OutputSections;
+extern std::vector<OutputSection *> outputSections;
} // namespace elf
} // namespace lld
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index 812468896f0d0..ee48f48081360 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -1,9 +1,8 @@
//===- Relocations.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -66,11 +65,11 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-static Optional<std::string> getLinkerScriptLocation(const Symbol &Sym) {
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
- if (Cmd->Sym == &Sym)
- return Cmd->Location;
+static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base))
+ if (cmd->sym == &sym)
+ return cmd->location;
return None;
}
@@ -79,19 +78,51 @@ static Optional<std::string> getLinkerScriptLocation(const Symbol &Sym) {
// >>> defined in /home/alice/src/foo.o
// >>> referenced by bar.c:12 (/home/alice/src/bar.c:12)
// >>> /home/alice/src/bar.o:(.text+0x1)
-static std::string getLocation(InputSectionBase &S, const Symbol &Sym,
- uint64_t Off) {
- std::string Msg = "\n>>> defined in ";
- if (Sym.File)
- Msg += toString(Sym.File);
- else if (Optional<std::string> Loc = getLinkerScriptLocation(Sym))
- Msg += *Loc;
-
- Msg += "\n>>> referenced by ";
- std::string Src = S.getSrcMsg(Sym, Off);
- if (!Src.empty())
- Msg += Src + "\n>>> ";
- return Msg + S.getObjMsg(Off);
+static std::string getLocation(InputSectionBase &s, const Symbol &sym,
+ uint64_t off) {
+ std::string msg = "\n>>> defined in ";
+ if (sym.file)
+ msg += toString(sym.file);
+ else if (Optional<std::string> loc = getLinkerScriptLocation(sym))
+ msg += *loc;
+
+ msg += "\n>>> referenced by ";
+ std::string src = s.getSrcMsg(sym, off);
+ if (!src.empty())
+ msg += src + "\n>>> ";
+ return msg + s.getObjMsg(off);
+}
+
+namespace {
+// Build a bitmask with one bit set for each RelExpr.
+//
+// Constexpr function arguments can't be used in static asserts, so we
+// use template arguments to build the mask.
+// But function template partial specializations don't exist (needed
+// for base case of the recursion), so we need a dummy struct.
+template <RelExpr... Exprs> struct RelExprMaskBuilder {
+ static inline uint64_t build() { return 0; }
+};
+
+// Specialization for recursive case.
+template <RelExpr Head, RelExpr... Tail>
+struct RelExprMaskBuilder<Head, Tail...> {
+ static inline uint64_t build() {
+ static_assert(0 <= Head && Head < 64,
+ "RelExpr is too large for 64-bit mask!");
+ return (uint64_t(1) << Head) | RelExprMaskBuilder<Tail...>::build();
+ }
+};
+} // namespace
+
+// Return true if `Expr` is one of `Exprs`.
+// There are fewer than 64 RelExpr's, so we can represent any set of
+// RelExpr's as a constant bit mask and test for membership with a
+// couple cheap bitwise operations.
+template <RelExpr... Exprs> bool oneof(RelExpr expr) {
+ assert(0 <= expr && (int)expr < 64 &&
+ "RelExpr is too large for 64-bit mask!");
+ return (uint64_t(1) << expr) & RelExprMaskBuilder<Exprs...>::build();
}
// This function is similar to the `handleTlsRelocation`. MIPS does not
@@ -100,204 +131,173 @@ static std::string getLocation(InputSectionBase &S, const Symbol &Sym,
// pollute other `handleTlsRelocation` by MIPS `ifs` statements.
// Mips has a custom MipsGotSection that handles the writing of GOT entries
// without dynamic relocations.
-static unsigned handleMipsTlsRelocation(RelType Type, Symbol &Sym,
- InputSectionBase &C, uint64_t Offset,
- int64_t Addend, RelExpr Expr) {
- if (Expr == R_MIPS_TLSLD) {
- In.MipsGot->addTlsIndex(*C.File);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+static unsigned handleMipsTlsRelocation(RelType type, Symbol &sym,
+ InputSectionBase &c, uint64_t offset,
+ int64_t addend, RelExpr expr) {
+ if (expr == R_MIPS_TLSLD) {
+ in.mipsGot->addTlsIndex(*c.file);
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
- if (Expr == R_MIPS_TLSGD) {
- In.MipsGot->addDynTlsEntry(*C.File, Sym);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ if (expr == R_MIPS_TLSGD) {
+ in.mipsGot->addDynTlsEntry(*c.file, sym);
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
return 0;
}
-// This function is similar to the `handleMipsTlsRelocation`. ARM also does not
-// support any relaxations for TLS relocations. ARM is logically similar to Mips
-// in how it handles TLS, but Mips uses its own custom GOT which handles some
-// of the cases that ARM uses GOT relocations for.
-//
-// We look for TLS global dynamic and local dynamic relocations, these may
-// require the generation of a pair of GOT entries that have associated
-// dynamic relocations. When the results of the dynamic relocations can be
-// resolved at static link time we do so. This is necessary for static linking
-// as there will be no dynamic loader to resolve them at load-time.
+// Notes about General Dynamic and Local Dynamic TLS models below. They may
+// require the generation of a pair of GOT entries that have associated dynamic
+// relocations. The pair of GOT entries created are of the form GOT[e0] Module
+// Index (Used to find pointer to TLS block at run-time) GOT[e1] Offset of
+// symbol in TLS block.
//
-// The pair of GOT entries created are of the form
-// GOT[e0] Module Index (Used to find pointer to TLS block at run-time)
-// GOT[e1] Offset of symbol in TLS block
-template <class ELFT>
-static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym,
- InputSectionBase &C, uint64_t Offset,
- int64_t Addend, RelExpr Expr) {
- // The Dynamic TLS Module Index Relocation for a symbol defined in an
- // executable is always 1. If the target Symbol is not preemptible then
- // we know the offset into the TLS block at static link time.
- bool NeedDynId = Sym.IsPreemptible || Config->Shared;
- bool NeedDynOff = Sym.IsPreemptible;
-
- auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) {
- if (Dyn)
- In.RelaDyn->addReloc(Type, In.Got, Off, Dest);
- else
- In.Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest});
- };
-
- // Local Dynamic is for access to module local TLS variables, while still
- // being suitable for being dynamically loaded via dlopen.
- // GOT[e0] is the module index, with a special value of 0 for the current
- // module. GOT[e1] is unused. There only needs to be one module index entry.
- if (Expr == R_TLSLD_PC && In.Got->addTlsIndex()) {
- AddTlsReloc(In.Got->getTlsIndexOff(), Target->TlsModuleIndexRel,
- NeedDynId ? nullptr : &Sym, NeedDynId);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
- return 1;
- }
-
- // Global Dynamic is the most general purpose access model. When we know
- // the module index and offset of symbol in TLS block we can fill these in
- // using static GOT relocations.
- if (Expr == R_TLSGD_PC) {
- if (In.Got->addDynTlsEntry(Sym)) {
- uint64_t Off = In.Got->getGlobalDynOffset(Sym);
- AddTlsReloc(Off, Target->TlsModuleIndexRel, &Sym, NeedDynId);
- AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Sym,
- NeedDynOff);
- }
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
- return 1;
- }
- return 0;
-}
-
// Returns the number of relocations processed.
template <class ELFT>
static unsigned
-handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
- typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) {
- if (!Sym.isTls())
+handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
+ typename ELFT::uint offset, int64_t addend, RelExpr expr) {
+ if (!sym.isTls())
return 0;
- if (Config->EMachine == EM_ARM)
- return handleARMTlsRelocation<ELFT>(Type, Sym, C, Offset, Addend, Expr);
- if (Config->EMachine == EM_MIPS)
- return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr);
-
- if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
- Config->Shared) {
- if (In.Got->addDynTlsEntry(Sym)) {
- uint64_t Off = In.Got->getGlobalDynOffset(Sym);
- In.RelaDyn->addReloc(
- {Target->TlsDescRel, In.Got, Off, !Sym.IsPreemptible, &Sym, 0});
+ if (config->emachine == EM_MIPS)
+ return handleMipsTlsRelocation(type, sym, c, offset, addend, expr);
+
+ if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC>(
+ expr) &&
+ config->shared) {
+ if (in.got->addDynTlsEntry(sym)) {
+ uint64_t off = in.got->getGlobalDynOffset(sym);
+ mainPart->relaDyn->addReloc(
+ {target->tlsDescRel, in.got, off, !sym.isPreemptible, &sym, 0});
}
- if (Expr != R_TLSDESC_CALL)
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ if (expr != R_TLSDESC_CALL)
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
- if (isRelExprOneOf<R_TLSLD_GOT, R_TLSLD_GOT_FROM_END, R_TLSLD_PC,
- R_TLSLD_HINT>(Expr)) {
+ bool canRelax = config->emachine != EM_ARM && config->emachine != EM_RISCV;
+
+ // If we are producing an executable and the symbol is non-preemptable, it
+ // must be defined and the code sequence can be relaxed to use Local-Exec.
+ //
+ // ARM and RISC-V do not support any relaxations for TLS relocations, however,
+ // we can omit the DTPMOD dynamic relocations and resolve them at link time
+ // because them are always 1. This may be necessary for static linking as
+ // DTPMOD may not be expected at load time.
+ bool isLocalInExecutable = !sym.isPreemptible && !config->shared;
+
+ // Local Dynamic is for access to module local TLS variables, while still
+ // being suitable for being dynamically loaded via dlopen. GOT[e0] is the
+ // module index, with a special value of 0 for the current module. GOT[e1] is
+ // unused. There only needs to be one module index entry.
+ if (oneof<R_TLSLD_GOT, R_TLSLD_GOTPLT, R_TLSLD_PC, R_TLSLD_HINT>(
+ expr)) {
// Local-Dynamic relocs can be relaxed to Local-Exec.
- if (!Config->Shared) {
- C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type,
- Offset, Addend, &Sym});
- return Target->TlsGdRelaxSkip;
+ if (canRelax && !config->shared) {
+ c.relocations.push_back(
+ {target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_LD_TO_LE), type,
+ offset, addend, &sym});
+ return target->getTlsGdRelaxSkip(type);
}
- if (Expr == R_TLSLD_HINT)
+ if (expr == R_TLSLD_HINT)
return 1;
- if (In.Got->addTlsIndex())
- In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got,
- In.Got->getTlsIndexOff(), nullptr);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ if (in.got->addTlsIndex()) {
+ if (isLocalInExecutable)
+ in.got->relocations.push_back(
+ {R_ADDEND, target->symbolicRel, in.got->getTlsIndexOff(), 1, &sym});
+ else
+ mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, in.got,
+ in.got->getTlsIndexOff(), nullptr);
+ }
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
// Local-Dynamic relocs can be relaxed to Local-Exec.
- if (Expr == R_ABS && !Config->Shared) {
- C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type,
- Offset, Addend, &Sym});
+ if (expr == R_DTPREL && !config->shared) {
+ c.relocations.push_back(
+ {target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_LD_TO_LE), type,
+ offset, addend, &sym});
return 1;
}
// Local-Dynamic sequence where offset of tls variable relative to dynamic
- // thread pointer is stored in the got.
- if (Expr == R_TLSLD_GOT_OFF) {
- // Local-Dynamic relocs can be relaxed to local-exec
- if (!Config->Shared) {
- C.Relocations.push_back({R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym});
- return 1;
+ // thread pointer is stored in the got. This cannot be relaxed to Local-Exec.
+ if (expr == R_TLSLD_GOT_OFF) {
+ if (!sym.isInGot()) {
+ in.got->addEntry(sym);
+ uint64_t off = sym.getGotOffset();
+ in.got->relocations.push_back(
+ {R_ABS, target->tlsOffsetRel, off, 0, &sym});
}
- if (!Sym.isInGot()) {
- In.Got->addEntry(Sym);
- uint64_t Off = Sym.getGotOffset();
- In.Got->Relocations.push_back(
- {R_ABS, Target->TlsOffsetRel, Off, 0, &Sym});
- }
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
- if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL,
- R_TLSGD_GOT, R_TLSGD_GOT_FROM_END, R_TLSGD_PC>(Expr)) {
- if (Config->Shared) {
- if (In.Got->addDynTlsEntry(Sym)) {
- uint64_t Off = In.Got->getGlobalDynOffset(Sym);
- In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got, Off, &Sym);
+ if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
+ R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
+ if (!canRelax || config->shared) {
+ if (in.got->addDynTlsEntry(sym)) {
+ uint64_t off = in.got->getGlobalDynOffset(sym);
+
+ if (isLocalInExecutable)
+ // Write one to the GOT slot.
+ in.got->relocations.push_back(
+ {R_ADDEND, target->symbolicRel, off, 1, &sym});
+ else
+ mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, in.got, off, &sym);
// If the symbol is preemptible we need the dynamic linker to write
// the offset too.
- uint64_t OffsetOff = Off + Config->Wordsize;
- if (Sym.IsPreemptible)
- In.RelaDyn->addReloc(Target->TlsOffsetRel, In.Got, OffsetOff, &Sym);
+ uint64_t offsetOff = off + config->wordsize;
+ if (sym.isPreemptible)
+ mainPart->relaDyn->addReloc(target->tlsOffsetRel, in.got, offsetOff,
+ &sym);
else
- In.Got->Relocations.push_back(
- {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym});
+ in.got->relocations.push_back(
+ {R_ABS, target->tlsOffsetRel, offsetOff, 0, &sym});
}
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
// Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
// depending on the symbol being locally defined or not.
- if (Sym.IsPreemptible) {
- C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type,
- Offset, Addend, &Sym});
- if (!Sym.isInGot()) {
- In.Got->addEntry(Sym);
- In.RelaDyn->addReloc(Target->TlsGotRel, In.Got, Sym.getGotOffset(),
- &Sym);
+ if (sym.isPreemptible) {
+ c.relocations.push_back(
+ {target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_GD_TO_IE), type,
+ offset, addend, &sym});
+ if (!sym.isInGot()) {
+ in.got->addEntry(sym);
+ mainPart->relaDyn->addReloc(target->tlsGotRel, in.got, sym.getGotOffset(),
+ &sym);
}
} else {
- C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type,
- Offset, Addend, &Sym});
+ c.relocations.push_back(
+ {target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_GD_TO_LE), type,
+ offset, addend, &sym});
}
- return Target->TlsGdRelaxSkip;
+ return target->getTlsGdRelaxSkip(type);
}
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined.
- if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_AARCH64_GOT_PAGE_PC,
- R_GOT_OFF, R_TLSIE_HINT>(Expr) &&
- !Config->Shared && !Sym.IsPreemptible) {
- C.Relocations.push_back({R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Sym});
+ if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_OFF,
+ R_TLSIE_HINT>(expr) &&
+ canRelax && isLocalInExecutable) {
+ c.relocations.push_back({R_RELAX_TLS_IE_TO_LE, type, offset, addend, &sym});
return 1;
}
- if (Expr == R_TLSIE_HINT)
+ if (expr == R_TLSIE_HINT)
return 1;
return 0;
}
-static RelType getMipsPairType(RelType Type, bool IsLocal) {
- switch (Type) {
+static RelType getMipsPairType(RelType type, bool isLocal) {
+ switch (type) {
case R_MIPS_HI16:
return R_MIPS_LO16;
case R_MIPS_GOT16:
@@ -309,9 +309,9 @@ static RelType getMipsPairType(RelType Type, bool IsLocal) {
// the high 16 bits of the symbol's value. A paired R_MIPS_LO16
// relocations handle low 16 bits of the address. That allows
// to allocate only one GOT entry for every 64 KBytes of local data.
- return IsLocal ? R_MIPS_LO16 : R_MIPS_NONE;
+ return isLocal ? R_MIPS_LO16 : R_MIPS_NONE;
case R_MICROMIPS_GOT16:
- return IsLocal ? R_MICROMIPS_LO16 : R_MIPS_NONE;
+ return isLocal ? R_MICROMIPS_LO16 : R_MIPS_NONE;
case R_MIPS_PCHI16:
return R_MIPS_PCLO16;
case R_MICROMIPS_HI16:
@@ -323,40 +323,38 @@ static RelType getMipsPairType(RelType Type, bool IsLocal) {
// True if non-preemptable symbol always has the same value regardless of where
// the DSO is loaded.
-static bool isAbsolute(const Symbol &Sym) {
- if (Sym.isUndefWeak())
+static bool isAbsolute(const Symbol &sym) {
+ if (sym.isUndefWeak())
return true;
- if (const auto *DR = dyn_cast<Defined>(&Sym))
- return DR->Section == nullptr; // Absolute symbol.
+ if (const auto *dr = dyn_cast<Defined>(&sym))
+ return dr->section == nullptr; // Absolute symbol.
return false;
}
-static bool isAbsoluteValue(const Symbol &Sym) {
- return isAbsolute(Sym) || Sym.isTls();
+static bool isAbsoluteValue(const Symbol &sym) {
+ return isAbsolute(sym) || sym.isTls();
}
// Returns true if Expr refers a PLT entry.
-static bool needsPlt(RelExpr Expr) {
- return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_AARCH64_PLT_PAGE_PC,
- R_GOT_PLT, R_AARCH64_GOT_PAGE_PC_PLT>(Expr);
+static bool needsPlt(RelExpr expr) {
+ return oneof<R_PLT_PC, R_PPC32_PLTREL, R_PPC64_CALL_PLT, R_PLT>(expr);
}
// Returns true if Expr refers a GOT entry. Note that this function
// returns false for TLS variables even though they need GOT, because
// TLS variables uses GOT differently than the regular variables.
-static bool needsGot(RelExpr Expr) {
- return isRelExprOneOf<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
- R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
- R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, R_GOT_FROM_END,
- R_GOT_PLT>(Expr);
+static bool needsGot(RelExpr expr) {
+ return oneof<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
+ R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
+ R_GOT_PC, R_GOTPLT>(expr);
}
// True if this expression is of the form Sym - X, where X is a position in the
// file (PC, or GOT for example).
-static bool isRelExpr(RelExpr Expr) {
- return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL,
- R_PPC_CALL, R_PPC_CALL_PLT, R_AARCH64_PAGE_PC,
- R_RELAX_GOT_PC>(Expr);
+static bool isRelExpr(RelExpr expr) {
+ return oneof<R_PC, R_GOTREL, R_GOTPLTREL, R_MIPS_GOTREL, R_PPC64_CALL,
+ R_PPC64_RELAX_TOC, R_AARCH64_PAGE_PC, R_RELAX_GOT_PC,
+ R_RISCV_PC_INDIRECT>(expr);
}
// Returns true if a given relocation can be computed at link-time.
@@ -368,43 +366,43 @@ static bool isRelExpr(RelExpr Expr) {
//
// If this function returns false, that means we need to emit a
// dynamic relocation so that the relocation will be fixed at load-time.
-static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
- InputSectionBase &S, uint64_t RelOff) {
+static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
+ InputSectionBase &s, uint64_t relOff) {
// These expressions always compute a constant
- if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
- R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
- R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
- R_AARCH64_GOT_PAGE_PC, R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC,
- R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT,
- R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT,
- R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT,
- R_TLSLD_HINT, R_TLSIE_HINT>(E))
+ if (oneof<R_DTPREL, R_GOTPLT, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
+ R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
+ R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
+ R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
+ R_PLT_PC, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC, R_PPC32_PLTREL,
+ R_PPC64_CALL_PLT, R_PPC64_RELAX_TOC, R_RISCV_ADD, R_TLSDESC_CALL,
+ R_TLSDESC_PC, R_AARCH64_TLSDESC_PAGE, R_HINT, R_TLSLD_HINT,
+ R_TLSIE_HINT>(e))
return true;
// These never do, except if the entire file is position dependent or if
// only the low bits are used.
- if (E == R_GOT || E == R_GOT_PLT || E == R_PLT || E == R_TLSDESC)
- return Target->usesOnlyLowPageBits(Type) || !Config->Pic;
+ if (e == R_GOT || e == R_PLT || e == R_TLSDESC)
+ return target->usesOnlyLowPageBits(type) || !config->isPic;
- if (Sym.IsPreemptible)
+ if (sym.isPreemptible)
return false;
- if (!Config->Pic)
+ if (!config->isPic)
return true;
// The size of a non preemptible symbol is a constant.
- if (E == R_SIZE)
+ if (e == R_SIZE)
return true;
// For the target and the relocation, we want to know if they are
// absolute or relative.
- bool AbsVal = isAbsoluteValue(Sym);
- bool RelE = isRelExpr(E);
- if (AbsVal && !RelE)
+ bool absVal = isAbsoluteValue(sym);
+ bool relE = isRelExpr(e);
+ if (absVal && !relE)
return true;
- if (!AbsVal && RelE)
+ if (!absVal && relE)
return true;
- if (!AbsVal && !RelE)
- return Target->usesOnlyLowPageBits(Type);
+ if (!absVal && !relE)
+ return target->usesOnlyLowPageBits(type);
// Relative relocation to an absolute value. This is normally unrepresentable,
// but if the relocation refers to a weak undefined symbol, we allow it to
@@ -414,59 +412,60 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
// Another special case is MIPS _gp_disp symbol which represents offset
// between start of a function and '_gp' value and defined as absolute just
// to simplify the code.
- assert(AbsVal && RelE);
- if (Sym.isUndefWeak())
+ assert(absVal && relE);
+ if (sym.isUndefWeak())
return true;
- error("relocation " + toString(Type) + " cannot refer to absolute symbol: " +
- toString(Sym) + getLocation(S, Sym, RelOff));
+ // We set the final symbols values for linker script defined symbols later.
+ // They always can be computed as a link time constant.
+ if (sym.scriptDefined)
+ return true;
+
+ error("relocation " + toString(type) + " cannot refer to absolute symbol: " +
+ toString(sym) + getLocation(s, sym, relOff));
return true;
}
-static RelExpr toPlt(RelExpr Expr) {
- switch (Expr) {
- case R_PPC_CALL:
- return R_PPC_CALL_PLT;
+static RelExpr toPlt(RelExpr expr) {
+ switch (expr) {
+ case R_PPC64_CALL:
+ return R_PPC64_CALL_PLT;
case R_PC:
return R_PLT_PC;
- case R_AARCH64_PAGE_PC:
- return R_AARCH64_PLT_PAGE_PC;
- case R_AARCH64_GOT_PAGE_PC:
- return R_AARCH64_GOT_PAGE_PC_PLT;
case R_ABS:
return R_PLT;
- case R_GOT:
- return R_GOT_PLT;
default:
- return Expr;
+ return expr;
}
}
-static RelExpr fromPlt(RelExpr Expr) {
+static RelExpr fromPlt(RelExpr expr) {
// We decided not to use a plt. Optimize a reference to the plt to a
// reference to the symbol itself.
- switch (Expr) {
+ switch (expr) {
case R_PLT_PC:
+ case R_PPC32_PLTREL:
return R_PC;
- case R_PPC_CALL_PLT:
- return R_PPC_CALL;
+ case R_PPC64_CALL_PLT:
+ return R_PPC64_CALL;
case R_PLT:
return R_ABS;
default:
- return Expr;
+ return expr;
}
}
// Returns true if a given shared symbol is in a read-only segment in a DSO.
-template <class ELFT> static bool isReadOnly(SharedSymbol &SS) {
- typedef typename ELFT::Phdr Elf_Phdr;
+template <class ELFT> static bool isReadOnly(SharedSymbol &ss) {
+ using Elf_Phdr = typename ELFT::Phdr;
// Determine if the symbol is read-only by scanning the DSO's program headers.
- const SharedFile<ELFT> &File = SS.getFile<ELFT>();
- for (const Elf_Phdr &Phdr : check(File.getObj().program_headers()))
- if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) &&
- !(Phdr.p_flags & ELF::PF_W) && SS.Value >= Phdr.p_vaddr &&
- SS.Value < Phdr.p_vaddr + Phdr.p_memsz)
+ const SharedFile &file = ss.getFile();
+ for (const Elf_Phdr &phdr :
+ check(file.template getObj<ELFT>().program_headers()))
+ if ((phdr.p_type == ELF::PT_LOAD || phdr.p_type == ELF::PT_GNU_RELRO) &&
+ !(phdr.p_flags & ELF::PF_W) && ss.value >= phdr.p_vaddr &&
+ ss.value < phdr.p_vaddr + phdr.p_memsz)
return true;
return false;
}
@@ -477,22 +476,22 @@ template <class ELFT> static bool isReadOnly(SharedSymbol &SS) {
// them are copied by a copy relocation, all of them need to be copied.
// Otherwise, they would refer to different places at runtime.
template <class ELFT>
-static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) {
- typedef typename ELFT::Sym Elf_Sym;
+static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &ss) {
+ using Elf_Sym = typename ELFT::Sym;
- SharedFile<ELFT> &File = SS.getFile<ELFT>();
+ SharedFile &file = ss.getFile();
- SmallSet<SharedSymbol *, 4> Ret;
- for (const Elf_Sym &S : File.getGlobalELFSyms()) {
- if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS ||
- S.getType() == STT_TLS || S.st_value != SS.Value)
+ SmallSet<SharedSymbol *, 4> ret;
+ for (const Elf_Sym &s : file.template getGlobalELFSyms<ELFT>()) {
+ if (s.st_shndx == SHN_UNDEF || s.st_shndx == SHN_ABS ||
+ s.getType() == STT_TLS || s.st_value != ss.value)
continue;
- StringRef Name = check(S.getName(File.getStringTable()));
- Symbol *Sym = Symtab->find(Name);
- if (auto *Alias = dyn_cast_or_null<SharedSymbol>(Sym))
- Ret.insert(Alias);
+ StringRef name = check(s.getName(file.getStringTable()));
+ Symbol *sym = symtab->find(name);
+ if (auto *alias = dyn_cast_or_null<SharedSymbol>(sym))
+ ret.insert(alias);
}
- return Ret;
+ return ret;
}
// When a symbol is copy relocated or we create a canonical plt entry, it is
@@ -500,19 +499,21 @@ static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) {
// in .bss and in the case of a canonical plt entry it is in .plt. This function
// replaces the existing symbol with a Defined pointing to the appropriate
// location.
-static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value,
- uint64_t Size) {
- Symbol Old = Sym;
- replaceSymbol<Defined>(&Sym, Sym.File, Sym.getName(), Sym.Binding,
- Sym.StOther, Sym.Type, Value, Size, Sec);
- Sym.PltIndex = Old.PltIndex;
- Sym.GotIndex = Old.GotIndex;
- Sym.VerdefIndex = Old.VerdefIndex;
- Sym.PPC64BranchltIndex = Old.PPC64BranchltIndex;
- Sym.IsPreemptible = true;
- Sym.ExportDynamic = true;
- Sym.IsUsedInRegularObj = true;
- Sym.Used = true;
+static void replaceWithDefined(Symbol &sym, SectionBase *sec, uint64_t value,
+ uint64_t size) {
+ Symbol old = sym;
+
+ sym.replace(Defined{sym.file, sym.getName(), sym.binding, sym.stOther,
+ sym.type, value, size, sec});
+
+ sym.pltIndex = old.pltIndex;
+ sym.gotIndex = old.gotIndex;
+ sym.verdefIndex = old.verdefIndex;
+ sym.ppc64BranchltIndex = old.ppc64BranchltIndex;
+ sym.isPreemptible = true;
+ sym.exportDynamic = true;
+ sym.isUsedInRegularObj = true;
+ sym.used = true;
}
// Reserve space in .bss or .bss.rel.ro for copy relocation.
@@ -557,29 +558,29 @@ static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value,
// to the variable in .bss. This kind of issue is sometimes very hard to
// debug. What's a solution? Instead of exporting a varaible V from a DSO,
// define an accessor getV().
-template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) {
+template <class ELFT> static void addCopyRelSymbol(SharedSymbol &ss) {
// Copy relocation against zero-sized symbol doesn't make sense.
- uint64_t SymSize = SS.getSize();
- if (SymSize == 0 || SS.Alignment == 0)
- fatal("cannot create a copy relocation for symbol " + toString(SS));
+ uint64_t symSize = ss.getSize();
+ if (symSize == 0 || ss.alignment == 0)
+ fatal("cannot create a copy relocation for symbol " + toString(ss));
// See if this symbol is in a read-only segment. If so, preserve the symbol's
// memory protection by reserving space in the .bss.rel.ro section.
- bool IsReadOnly = isReadOnly<ELFT>(SS);
- BssSection *Sec = make<BssSection>(IsReadOnly ? ".bss.rel.ro" : ".bss",
- SymSize, SS.Alignment);
- if (IsReadOnly)
- In.BssRelRo->getParent()->addSection(Sec);
+ bool isRO = isReadOnly<ELFT>(ss);
+ BssSection *sec =
+ make<BssSection>(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment);
+ if (isRO)
+ in.bssRelRo->getParent()->addSection(sec);
else
- In.Bss->getParent()->addSection(Sec);
+ in.bss->getParent()->addSection(sec);
// Look through the DSO's dynamic symbol table for aliases and create a
// dynamic symbol for each one. This causes the copy relocation to correctly
// interpose any aliases.
- for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS))
- replaceWithDefined(*Sym, Sec, 0, Sym->Size);
+ for (SharedSymbol *sym : getSymbolsAt<ELFT>(ss))
+ replaceWithDefined(*sym, sec, 0, sym->size);
- In.RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS);
+ mainPart->relaDyn->addReloc(target->copyRel, sec, 0, &ss);
}
// MIPS has an odd notion of "paired" relocations to calculate addends.
@@ -587,34 +588,34 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) {
// R_MIPS_LO16 relocation after that, and an addend is calculated using
// the two relocations.
template <class ELFT, class RelTy>
-static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End,
- InputSectionBase &Sec, RelExpr Expr,
- bool IsLocal) {
- if (Expr == R_MIPS_GOTREL && IsLocal)
- return Sec.getFile<ELFT>()->MipsGp0;
+static int64_t computeMipsAddend(const RelTy &rel, const RelTy *end,
+ InputSectionBase &sec, RelExpr expr,
+ bool isLocal) {
+ if (expr == R_MIPS_GOTREL && isLocal)
+ return sec.getFile<ELFT>()->mipsGp0;
// The ABI says that the paired relocation is used only for REL.
// See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (RelTy::IsRela)
return 0;
- RelType Type = Rel.getType(Config->IsMips64EL);
- uint32_t PairTy = getMipsPairType(Type, IsLocal);
- if (PairTy == R_MIPS_NONE)
+ RelType type = rel.getType(config->isMips64EL);
+ uint32_t pairTy = getMipsPairType(type, isLocal);
+ if (pairTy == R_MIPS_NONE)
return 0;
- const uint8_t *Buf = Sec.data().data();
- uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
+ const uint8_t *buf = sec.data().data();
+ uint32_t symIndex = rel.getSymbol(config->isMips64EL);
// To make things worse, paired relocations might not be contiguous in
// the relocation table, so we need to do linear search. *sigh*
- for (const RelTy *RI = &Rel; RI != End; ++RI)
- if (RI->getType(Config->IsMips64EL) == PairTy &&
- RI->getSymbol(Config->IsMips64EL) == SymIndex)
- return Target->getImplicitAddend(Buf + RI->r_offset, PairTy);
+ for (const RelTy *ri = &rel; ri != end; ++ri)
+ if (ri->getType(config->isMips64EL) == pairTy &&
+ ri->getSymbol(config->isMips64EL) == symIndex)
+ return target->getImplicitAddend(buf + ri->r_offset, pairTy);
- warn("can't find matching " + toString(PairTy) + " relocation for " +
- toString(Type));
+ warn("can't find matching " + toString(pairTy) + " relocation for " +
+ toString(type));
return 0;
}
@@ -622,59 +623,174 @@ static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End,
// is in a relocation itself. If it is REL, we need to read it from an
// input section.
template <class ELFT, class RelTy>
-static int64_t computeAddend(const RelTy &Rel, const RelTy *End,
- InputSectionBase &Sec, RelExpr Expr,
- bool IsLocal) {
- int64_t Addend;
- RelType Type = Rel.getType(Config->IsMips64EL);
+static int64_t computeAddend(const RelTy &rel, const RelTy *end,
+ InputSectionBase &sec, RelExpr expr,
+ bool isLocal) {
+ int64_t addend;
+ RelType type = rel.getType(config->isMips64EL);
if (RelTy::IsRela) {
- Addend = getAddend<ELFT>(Rel);
+ addend = getAddend<ELFT>(rel);
} else {
- const uint8_t *Buf = Sec.data().data();
- Addend = Target->getImplicitAddend(Buf + Rel.r_offset, Type);
+ const uint8_t *buf = sec.data().data();
+ addend = target->getImplicitAddend(buf + rel.r_offset, type);
}
- if (Config->EMachine == EM_PPC64 && Config->Pic && Type == R_PPC64_TOC)
- Addend += getPPC64TocBase();
- if (Config->EMachine == EM_MIPS)
- Addend += computeMipsAddend<ELFT>(Rel, End, Sec, Expr, IsLocal);
+ if (config->emachine == EM_PPC64 && config->isPic && type == R_PPC64_TOC)
+ addend += getPPC64TocBase();
+ if (config->emachine == EM_MIPS)
+ addend += computeMipsAddend<ELFT>(rel, end, sec, expr, isLocal);
- return Addend;
+ return addend;
}
-// Report an undefined symbol if necessary.
-// Returns true if this function printed out an error message.
-static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
- uint64_t Offset) {
- if (Sym.isLocal() || !Sym.isUndefined() || Sym.isWeak())
- return false;
+// Custom error message if Sym is defined in a discarded section.
+template <class ELFT>
+static std::string maybeReportDiscarded(Undefined &sym) {
+ auto *file = dyn_cast_or_null<ObjFile<ELFT>>(sym.file);
+ if (!file || !sym.discardedSecIdx ||
+ file->getSections()[sym.discardedSecIdx] != &InputSection::discarded)
+ return "";
+ ArrayRef<Elf_Shdr_Impl<ELFT>> objSections =
+ CHECK(file->getObj().sections(), file);
+
+ std::string msg;
+ if (sym.type == ELF::STT_SECTION) {
+ msg = "relocation refers to a discarded section: ";
+ msg += CHECK(
+ file->getObj().getSectionName(&objSections[sym.discardedSecIdx]), file);
+ } else {
+ msg = "relocation refers to a symbol in a discarded section: " +
+ toString(sym);
+ }
+ msg += "\n>>> defined in " + toString(file);
+
+ Elf_Shdr_Impl<ELFT> elfSec = objSections[sym.discardedSecIdx - 1];
+ if (elfSec.sh_type != SHT_GROUP)
+ return msg;
+
+ // If the discarded section is a COMDAT.
+ StringRef signature = file->getShtGroupSignature(objSections, elfSec);
+ if (const InputFile *prevailing =
+ symtab->comdatGroups.lookup(CachedHashStringRef(signature)))
+ msg += "\n>>> section group signature: " + signature.str() +
+ "\n>>> prevailing definition is in " + toString(prevailing);
+ return msg;
+}
- bool CanBeExternal =
- Sym.computeBinding() != STB_LOCAL && Sym.Visibility == STV_DEFAULT;
- if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal)
- return false;
+// Undefined diagnostics are collected in a vector and emitted once all of
+// them are known, so that some postprocessing on the list of undefined symbols
+// can happen before lld emits diagnostics.
+struct UndefinedDiag {
+ Symbol *sym;
+ struct Loc {
+ InputSectionBase *sec;
+ uint64_t offset;
+ };
+ std::vector<Loc> locs;
+ bool isWarning;
+};
+
+static std::vector<UndefinedDiag> undefs;
+
+template <class ELFT>
+static void reportUndefinedSymbol(const UndefinedDiag &undef) {
+ Symbol &sym = *undef.sym;
+
+ auto visibility = [&]() -> std::string {
+ switch (sym.visibility) {
+ case STV_INTERNAL:
+ return "internal ";
+ case STV_HIDDEN:
+ return "hidden ";
+ case STV_PROTECTED:
+ return "protected ";
+ default:
+ return "";
+ }
+ };
- std::string Msg =
- "undefined symbol: " + toString(Sym) + "\n>>> referenced by ";
+ std::string msg = maybeReportDiscarded<ELFT>(cast<Undefined>(sym));
+ if (msg.empty())
+ msg = "undefined " + visibility() + "symbol: " + toString(sym);
+
+ const size_t maxUndefReferences = 10;
+ size_t i = 0;
+ for (UndefinedDiag::Loc l : undef.locs) {
+ if (i >= maxUndefReferences)
+ break;
+ InputSectionBase &sec = *l.sec;
+ uint64_t offset = l.offset;
+
+ msg += "\n>>> referenced by ";
+ std::string src = sec.getSrcMsg(sym, offset);
+ if (!src.empty())
+ msg += src + "\n>>> ";
+ msg += sec.getObjMsg(offset);
+ i++;
+ }
- std::string Src = Sec.getSrcMsg(Sym, Offset);
- if (!Src.empty())
- Msg += Src + "\n>>> ";
- Msg += Sec.getObjMsg(Offset);
+ if (i < undef.locs.size())
+ msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times")
+ .str();
- if (Sym.getName().startswith("_ZTV"))
- Msg += "\nthe vtable symbol may be undefined because the class is missing "
+ if (sym.getName().startswith("_ZTV"))
+ msg += "\nthe vtable symbol may be undefined because the class is missing "
"its key function (see https://lld.llvm.org/missingkeyfunction)";
- if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) ||
- Config->NoinhibitExec) {
- warn(Msg);
- return false;
+ if (undef.isWarning)
+ warn(msg);
+ else
+ error(msg);
+}
+
+template <class ELFT> void elf::reportUndefinedSymbols() {
+ // Find the first "undefined symbol" diagnostic for each diagnostic, and
+ // collect all "referenced from" lines at the first diagnostic.
+ DenseMap<Symbol *, UndefinedDiag *> firstRef;
+ for (UndefinedDiag &undef : undefs) {
+ assert(undef.locs.size() == 1);
+ if (UndefinedDiag *canon = firstRef.lookup(undef.sym)) {
+ canon->locs.push_back(undef.locs[0]);
+ undef.locs.clear();
+ } else
+ firstRef[undef.sym] = &undef;
}
- error(Msg);
- return true;
+ for (const UndefinedDiag &undef : undefs) {
+ if (!undef.locs.empty())
+ reportUndefinedSymbol<ELFT>(undef);
+ }
+ undefs.clear();
+}
+
+// Report an undefined symbol if necessary.
+// Returns true if the undefined symbol will produce an error message.
+template <class ELFT>
+static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
+ uint64_t offset) {
+ if (!sym.isUndefined() || sym.isWeak())
+ return false;
+
+ bool canBeExternal = !sym.isLocal() && sym.computeBinding() != STB_LOCAL &&
+ sym.visibility == STV_DEFAULT;
+ if (config->unresolvedSymbols == UnresolvedPolicy::Ignore && canBeExternal)
+ return false;
+
+ // clang (as of 2019-06-12) / gcc (as of 8.2.1) PPC64 may emit a .rela.toc
+ // which references a switch table in a discarded .rodata/.text section. The
+ // .toc and the .rela.toc are incorrectly not placed in the comdat. The ELF
+ // spec says references from outside the group to a STB_LOCAL symbol are not
+ // allowed. Work around the bug.
+ if (config->emachine == EM_PPC64 &&
+ cast<Undefined>(sym).discardedSecIdx != 0 && sec.name == ".toc")
+ return false;
+
+ bool isWarning =
+ (config->unresolvedSymbols == UnresolvedPolicy::Warn && canBeExternal) ||
+ config->noinhibitExec;
+ undefs.push_back({&sym, {{&sec, offset}}, isWarning});
+ return !isWarning;
}
// MIPS N32 ABI treats series of successive relocations with the same offset
@@ -682,14 +798,14 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
// packs all relocations into the single relocation record. Here we emulate
// this for the N32 ABI. Iterate over relocation with the same offset and put
// theirs types into the single bit-set.
-template <class RelTy> static RelType getMipsN32RelType(RelTy *&Rel, RelTy *End) {
- RelType Type = 0;
- uint64_t Offset = Rel->r_offset;
-
- int N = 0;
- while (Rel != End && Rel->r_offset == Offset)
- Type |= (Rel++)->getType(Config->IsMips64EL) << (8 * N++);
- return Type;
+template <class RelTy> static RelType getMipsN32RelType(RelTy *&rel, RelTy *end) {
+ RelType type = 0;
+ uint64_t offset = rel->r_offset;
+
+ int n = 0;
+ while (rel != end && rel->r_offset == offset)
+ type |= (rel++)->getType(config->isMips64EL) << (8 * n++);
+ return type;
}
// .eh_frame sections are mergeable input sections, so their input
@@ -706,77 +822,72 @@ template <class RelTy> static RelType getMipsN32RelType(RelTy *&Rel, RelTy *End)
namespace {
class OffsetGetter {
public:
- explicit OffsetGetter(InputSectionBase &Sec) {
- if (auto *Eh = dyn_cast<EhInputSection>(&Sec))
- Pieces = Eh->Pieces;
+ explicit OffsetGetter(InputSectionBase &sec) {
+ if (auto *eh = dyn_cast<EhInputSection>(&sec))
+ pieces = eh->pieces;
}
// Translates offsets in input sections to offsets in output sections.
// Given offset must increase monotonically. We assume that Piece is
- // sorted by InputOff.
- uint64_t get(uint64_t Off) {
- if (Pieces.empty())
- return Off;
-
- while (I != Pieces.size() && Pieces[I].InputOff + Pieces[I].Size <= Off)
- ++I;
- if (I == Pieces.size())
+ // sorted by inputOff.
+ uint64_t get(uint64_t off) {
+ if (pieces.empty())
+ return off;
+
+ while (i != pieces.size() && pieces[i].inputOff + pieces[i].size <= off)
+ ++i;
+ if (i == pieces.size())
fatal(".eh_frame: relocation is not in any piece");
// Pieces must be contiguous, so there must be no holes in between.
- assert(Pieces[I].InputOff <= Off && "Relocation not in any piece");
+ assert(pieces[i].inputOff <= off && "Relocation not in any piece");
// Offset -1 means that the piece is dead (i.e. garbage collected).
- if (Pieces[I].OutputOff == -1)
+ if (pieces[i].outputOff == -1)
return -1;
- return Pieces[I].OutputOff + Off - Pieces[I].InputOff;
+ return pieces[i].outputOff + off - pieces[i].inputOff;
}
private:
- ArrayRef<EhSectionPiece> Pieces;
- size_t I = 0;
+ ArrayRef<EhSectionPiece> pieces;
+ size_t i = 0;
};
} // namespace
-static void addRelativeReloc(InputSectionBase *IS, uint64_t OffsetInSec,
- Symbol *Sym, int64_t Addend, RelExpr Expr,
- RelType Type) {
- // Add a relative relocation. If RelrDyn section is enabled, and the
+static void addRelativeReloc(InputSectionBase *isec, uint64_t offsetInSec,
+ Symbol *sym, int64_t addend, RelExpr expr,
+ RelType type) {
+ Partition &part = isec->getPartition();
+
+ // Add a relative relocation. If relrDyn section is enabled, and the
// relocation offset is guaranteed to be even, add the relocation to
- // the RelrDyn section, otherwise add it to the RelaDyn section.
- // RelrDyn sections don't support odd offsets. Also, RelrDyn sections
+ // the relrDyn section, otherwise add it to the relaDyn section.
+ // relrDyn sections don't support odd offsets. Also, relrDyn sections
// don't store the addend values, so we must write it to the relocated
// address.
- if (In.RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) {
- IS->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym});
- In.RelrDyn->Relocs.push_back({IS, OffsetInSec});
+ if (part.relrDyn && isec->alignment >= 2 && offsetInSec % 2 == 0) {
+ isec->relocations.push_back({expr, type, offsetInSec, addend, sym});
+ part.relrDyn->relocs.push_back({isec, offsetInSec});
return;
}
- In.RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend, Expr,
- Type);
+ part.relaDyn->addReloc(target->relativeRel, isec, offsetInSec, sym, addend,
+ expr, type);
}
template <class ELFT, class GotPltSection>
-static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt,
- RelocationBaseSection *Rel, RelType Type, Symbol &Sym) {
- Plt->addEntry<ELFT>(Sym);
- GotPlt->addEntry(Sym);
- Rel->addReloc(
- {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0});
+static void addPltEntry(PltSection *plt, GotPltSection *gotPlt,
+ RelocationBaseSection *rel, RelType type, Symbol &sym) {
+ plt->addEntry<ELFT>(sym);
+ gotPlt->addEntry(sym);
+ rel->addReloc(
+ {type, gotPlt, sym.getGotPltOffset(), !sym.isPreemptible, &sym, 0});
}
-template <class ELFT> static void addGotEntry(Symbol &Sym) {
- In.Got->addEntry(Sym);
-
- RelExpr Expr;
- if (Sym.isTls())
- Expr = R_TLS;
- else if (Sym.isGnuIFunc())
- Expr = R_PLT;
- else
- Expr = R_ABS;
+static void addGotEntry(Symbol &sym) {
+ in.got->addEntry(sym);
- uint64_t Off = Sym.getGotOffset();
+ RelExpr expr = sym.isTls() ? R_TLS : R_ABS;
+ uint64_t off = sym.getGotOffset();
// If a GOT slot value can be calculated at link-time, which is now,
// we can just fill that out.
@@ -785,42 +896,42 @@ template <class ELFT> static void addGotEntry(Symbol &Sym) {
// add a static relocation to a Relocations vector so that
// InputSection::relocate will do the work for us. We may be able
// to just write a value now, but it is a TODO.)
- bool IsLinkTimeConstant =
- !Sym.IsPreemptible && (!Config->Pic || isAbsolute(Sym));
- if (IsLinkTimeConstant) {
- In.Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
+ bool isLinkTimeConstant =
+ !sym.isPreemptible && (!config->isPic || isAbsolute(sym));
+ if (isLinkTimeConstant) {
+ in.got->relocations.push_back({expr, target->symbolicRel, off, 0, &sym});
return;
}
// Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
// the GOT slot will be fixed at load-time.
- if (!Sym.isTls() && !Sym.IsPreemptible && Config->Pic && !isAbsolute(Sym)) {
- addRelativeReloc(In.Got, Off, &Sym, 0, R_ABS, Target->GotRel);
+ if (!sym.isTls() && !sym.isPreemptible && config->isPic && !isAbsolute(sym)) {
+ addRelativeReloc(in.got, off, &sym, 0, R_ABS, target->symbolicRel);
return;
}
- In.RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel, In.Got,
- Off, &Sym, 0, Sym.IsPreemptible ? R_ADDEND : R_ABS,
- Target->GotRel);
+ mainPart->relaDyn->addReloc(
+ sym.isTls() ? target->tlsGotRel : target->gotRel, in.got, off, &sym, 0,
+ sym.isPreemptible ? R_ADDEND : R_ABS, target->symbolicRel);
}
// Return true if we can define a symbol in the executable that
// contains the value/function of a symbol defined in a shared
// library.
-static bool canDefineSymbolInExecutable(Symbol &Sym) {
+static bool canDefineSymbolInExecutable(Symbol &sym) {
// If the symbol has default visibility the symbol defined in the
// executable will preempt it.
// Note that we want the visibility of the shared symbol itself, not
// the visibility of the symbol in the output file we are producing. That is
- // why we use Sym.StOther.
- if ((Sym.StOther & 0x3) == STV_DEFAULT)
+ // why we use Sym.stOther.
+ if ((sym.stOther & 0x3) == STV_DEFAULT)
return true;
// If we are allowed to break address equality of functions, defining
// a plt entry will allow the program to call the function in the
// .so, but the .so and the executable will no agree on the address
// of the function. Similar logic for objects.
- return ((Sym.isFunc() && Config->IgnoreFunctionAddressEquality) ||
- (Sym.isObject() && Config->IgnoreDataAddressEquality));
+ return ((sym.isFunc() && config->ignoreFunctionAddressEquality) ||
+ (sym.isObject() && config->ignoreDataAddressEquality));
}
// The reason we have to do this early scan is as follows
@@ -837,23 +948,33 @@ static bool canDefineSymbolInExecutable(Symbol &Sym) {
// complicates things for the dynamic linker and means we would have to reserve
// space for the extra PT_LOAD even if we end up not using it.
template <class ELFT, class RelTy>
-static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
- uint64_t Offset, Symbol &Sym, const RelTy &Rel,
- int64_t Addend) {
- if (isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Offset)) {
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
+ uint64_t offset, Symbol &sym, const RelTy &rel,
+ int64_t addend) {
+ // If the relocation is known to be a link-time constant, we know no dynamic
+ // relocation will be created, pass the control to relocateAlloc() or
+ // relocateNonAlloc() to resolve it.
+ //
+ // The behavior of an undefined weak reference is implementation defined. If
+ // the relocation is to a weak undef, and we are producing an executable, let
+ // relocate{,Non}Alloc() resolve it.
+ if (isStaticLinkTimeConstant(expr, type, sym, sec, offset) ||
+ (!config->shared && sym.isUndefWeak())) {
+ sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
}
- bool CanWrite = (Sec.Flags & SHF_WRITE) || !Config->ZText;
- if (CanWrite) {
- // R_GOT refers to a position in the got, even if the symbol is preemptible.
- bool IsPreemptibleValue = Sym.IsPreemptible && Expr != R_GOT;
- if (!IsPreemptibleValue) {
- addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type);
+ bool canWrite = (sec.flags & SHF_WRITE) || !config->zText;
+ if (canWrite) {
+ RelType rel = target->getDynRel(type);
+ if (expr == R_GOT || (rel == target->symbolicRel && !sym.isPreemptible)) {
+ addRelativeReloc(&sec, offset, &sym, addend, expr, type);
return;
- } else if (RelType Rel = Target->getDynRel(Type)) {
- In.RelaDyn->addReloc(Rel, &Sec, Offset, &Sym, Addend, R_ADDEND, Type);
+ } else if (rel != 0) {
+ if (config->emachine == EM_MIPS && rel == target->symbolicRel)
+ rel = target->relativeRel;
+ sec.getPartition().relaDyn->addReloc(rel, &sec, offset, &sym, addend,
+ R_ADDEND, type);
// MIPS ABI turns using of GOT and dynamic relocations inside out.
// While regular ABI uses dynamic relocations to fill up GOT entries
@@ -870,62 +991,62 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
// to the GOT entry and reads the GOT entry when it needs to perform
// a dynamic relocation.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
- if (Config->EMachine == EM_MIPS)
- In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
+ if (config->emachine == EM_MIPS)
+ in.mipsGot->addEntry(*sec.file, sym, addend, expr);
return;
}
}
- // If the relocation is to a weak undef, and we are producing
- // executable, give up on it and produce a non preemptible 0.
- if (!Config->Shared && Sym.isUndefWeak()) {
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
- return;
- }
-
- if (!CanWrite && (Config->Pic && !isRelExpr(Expr))) {
+ if (!canWrite && (config->isPic && !isRelExpr(expr))) {
error(
- "can't create dynamic relocation " + toString(Type) + " against " +
- (Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) +
+ "can't create dynamic relocation " + toString(type) + " against " +
+ (sym.getName().empty() ? "local symbol" : "symbol: " + toString(sym)) +
" in readonly segment; recompile object files with -fPIC "
"or pass '-Wl,-z,notext' to allow text relocations in the output" +
- getLocation(Sec, Sym, Offset));
+ getLocation(sec, sym, offset));
return;
}
- // Copy relocations are only possible if we are creating an executable.
- if (Config->Shared) {
- errorOrWarn("relocation " + toString(Type) +
- " cannot be used against symbol " + toString(Sym) +
- "; recompile with -fPIC" + getLocation(Sec, Sym, Offset));
+ // Copy relocations (for STT_OBJECT) and canonical PLT (for STT_FUNC) are only
+ // possible in an executable.
+ //
+ // Among R_ABS relocatoin types, symbolicRel has the same size as the word
+ // size. Others have fewer bits and may cause runtime overflow in -pie/-shared
+ // mode. Disallow them.
+ if (config->shared ||
+ (config->pie && expr == R_ABS && type != target->symbolicRel)) {
+ errorOrWarn(
+ "relocation " + toString(type) + " cannot be used against " +
+ (sym.getName().empty() ? "local symbol" : "symbol " + toString(sym)) +
+ "; recompile with -fPIC" + getLocation(sec, sym, offset));
return;
}
// If the symbol is undefined we already reported any relevant errors.
- if (Sym.isUndefined())
+ if (sym.isUndefined())
return;
- if (!canDefineSymbolInExecutable(Sym)) {
- error("cannot preempt symbol: " + toString(Sym) +
- getLocation(Sec, Sym, Offset));
+ if (!canDefineSymbolInExecutable(sym)) {
+ error("cannot preempt symbol: " + toString(sym) +
+ getLocation(sec, sym, offset));
return;
}
- if (Sym.isObject()) {
+ if (sym.isObject()) {
// Produce a copy relocation.
- if (auto *SS = dyn_cast<SharedSymbol>(&Sym)) {
- if (!Config->ZCopyreloc)
- error("unresolvable relocation " + toString(Type) +
- " against symbol '" + toString(*SS) +
+ if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
+ if (!config->zCopyreloc)
+ error("unresolvable relocation " + toString(type) +
+ " against symbol '" + toString(*ss) +
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
- getLocation(Sec, Sym, Offset));
- addCopyRelSymbol<ELFT>(*SS);
+ getLocation(sec, sym, offset));
+ addCopyRelSymbol<ELFT>(*ss);
}
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
}
- if (Sym.isFunc()) {
+ if (sym.isFunc()) {
// This handles a non PIC program call to function in a shared library. In
// an ideal world, we could just report an error saying the relocation can
// overflow at runtime. In the real world with glibc, crt1.o has a
@@ -953,167 +1074,333 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
// compiled without -fPIE/-fPIC and doesn't maintain ebx.
// * If a library definition gets preempted to the executable, it will have
// the wrong ebx value.
- if (Config->Pie && Config->EMachine == EM_386)
- errorOrWarn("symbol '" + toString(Sym) +
+ if (config->pie && config->emachine == EM_386)
+ errorOrWarn("symbol '" + toString(sym) +
"' cannot be preempted; recompile with -fPIE" +
- getLocation(Sec, Sym, Offset));
- if (!Sym.isInPlt())
- addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
- if (!Sym.isDefined())
- replaceWithDefined(Sym, In.Plt, getPltEntryOffset(Sym.PltIndex), 0);
- Sym.NeedsPltAddr = true;
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ getLocation(sec, sym, offset));
+ if (!sym.isInPlt())
+ addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
+ if (!sym.isDefined())
+ replaceWithDefined(
+ sym, in.plt,
+ target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
+ sym.needsPltAddr = true;
+ sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
}
- errorOrWarn("symbol '" + toString(Sym) + "' has no type" +
- getLocation(Sec, Sym, Offset));
+ errorOrWarn("symbol '" + toString(sym) + "' has no type" +
+ getLocation(sec, sym, offset));
}
+struct IRelativeReloc {
+ RelType type;
+ InputSectionBase *sec;
+ uint64_t offset;
+ Symbol *sym;
+};
+
+static std::vector<IRelativeReloc> iRelativeRelocs;
+
template <class ELFT, class RelTy>
-static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
- RelTy *End) {
- const RelTy &Rel = *I;
- Symbol &Sym = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
- RelType Type;
+static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
+ RelTy *end) {
+ const RelTy &rel = *i;
+ uint32_t symIndex = rel.getSymbol(config->isMips64EL);
+ Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIndex);
+ RelType type;
// Deal with MIPS oddity.
- if (Config->MipsN32Abi) {
- Type = getMipsN32RelType(I, End);
+ if (config->mipsN32Abi) {
+ type = getMipsN32RelType(i, end);
} else {
- Type = Rel.getType(Config->IsMips64EL);
- ++I;
+ type = rel.getType(config->isMips64EL);
+ ++i;
}
// Get an offset in an output section this relocation is applied to.
- uint64_t Offset = GetOffset.get(Rel.r_offset);
- if (Offset == uint64_t(-1))
+ uint64_t offset = getOffset.get(rel.r_offset);
+ if (offset == uint64_t(-1))
return;
- // Skip if the target symbol is an erroneous undefined symbol.
- if (maybeReportUndefined(Sym, Sec, Rel.r_offset))
+ // Error if the target symbol is undefined. Symbol index 0 may be used by
+ // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
+ if (symIndex != 0 && maybeReportUndefined<ELFT>(sym, sec, rel.r_offset))
return;
- const uint8_t *RelocatedAddr = Sec.data().begin() + Rel.r_offset;
- RelExpr Expr = Target->getRelExpr(Type, Sym, RelocatedAddr);
+ const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset;
+ RelExpr expr = target->getRelExpr(type, sym, relocatedAddr);
// Ignore "hint" relocations because they are only markers for relaxation.
- if (isRelExprOneOf<R_HINT, R_NONE>(Expr))
+ if (oneof<R_HINT, R_NONE>(expr))
return;
- // Strenghten or relax relocations.
- //
- // GNU ifunc symbols must be accessed via PLT because their addresses
- // are determined by runtime.
+ // We can separate the small code model relocations into 2 categories:
+ // 1) Those that access the compiler generated .toc sections.
+ // 2) Those that access the linker allocated got entries.
+ // lld allocates got entries to symbols on demand. Since we don't try to sort
+ // the got entries in any way, we don't have to track which objects have
+ // got-based small code model relocs. The .toc sections get placed after the
+ // end of the linker allocated .got section and we do sort those so sections
+ // addressed with small code model relocations come first.
+ if (config->emachine == EM_PPC64 && isPPC64SmallCodeModelTocReloc(type))
+ sec.file->ppc64SmallCodeModelTocRelocs = true;
+
+ if (sym.isGnuIFunc() && !config->zText && config->warnIfuncTextrel) {
+ warn("using ifunc symbols when text relocations are allowed may produce "
+ "a binary that will segfault, if the object file is linked with "
+ "old version of glibc (glibc 2.28 and earlier). If this applies to "
+ "you, consider recompiling the object files without -fPIC and "
+ "without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
+ "turn off this warning." +
+ getLocation(sec, sym, offset));
+ }
+
+ // Read an addend.
+ int64_t addend = computeAddend<ELFT>(rel, end, sec, expr, sym.isLocal());
+
+ // Relax relocations.
//
- // On the other hand, if we know that a PLT entry will be resolved within
- // the same ELF module, we can skip PLT access and directly jump to the
- // destination function. For example, if we are linking a main exectuable,
- // all dynamic symbols that can be resolved within the executable will
- // actually be resolved that way at runtime, because the main exectuable
- // is always at the beginning of a search list. We can leverage that fact.
- if (Sym.isGnuIFunc()) {
- if (!Config->ZText && Config->WarnIfuncTextrel) {
- warn("using ifunc symbols when text relocations are allowed may produce "
- "a binary that will segfault, if the object file is linked with "
- "old version of glibc (glibc 2.28 and earlier). If this applies to "
- "you, consider recompiling the object files without -fPIC and "
- "without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
- "turn off this warning." +
- getLocation(Sec, Sym, Offset));
+ // If we know that a PLT entry will be resolved within the same ELF module, we
+ // can skip PLT access and directly jump to the destination function. For
+ // example, if we are linking a main exectuable, all dynamic symbols that can
+ // be resolved within the executable will actually be resolved that way at
+ // runtime, because the main exectuable is always at the beginning of a search
+ // list. We can leverage that fact.
+ if (!sym.isPreemptible && (!sym.isGnuIFunc() || config->zIfuncNoplt)) {
+ if (expr == R_GOT_PC && !isAbsoluteValue(sym)) {
+ expr = target->adjustRelaxExpr(type, relocatedAddr, expr);
+ } else {
+ // Addend of R_PPC_PLTREL24 is used to choose call stub type. It should be
+ // ignored if optimized to R_PC.
+ if (config->emachine == EM_PPC && expr == R_PPC32_PLTREL)
+ addend = 0;
+ expr = fromPlt(expr);
}
- Expr = toPlt(Expr);
- } else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) {
- Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr);
- } else if (!Sym.IsPreemptible) {
- Expr = fromPlt(Expr);
}
- // This relocation does not require got entry, but it is relative to got and
- // needs it to be created. Here we request for that.
- if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL,
- R_GOTREL_FROM_END, R_PPC_TOC>(Expr))
- In.Got->HasGotOffRel = true;
-
- // Read an addend.
- int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal());
+ // If the relocation does not emit a GOT or GOTPLT entry but its computation
+ // uses their addresses, we need GOT or GOTPLT to be created.
+ //
+ // The 4 types that relative GOTPLT are all x86 and x86-64 specific.
+ if (oneof<R_GOTPLTONLY_PC, R_GOTPLTREL, R_GOTPLT, R_TLSGD_GOTPLT>(expr)) {
+ in.gotPlt->hasGotPltOffRel = true;
+ } else if (oneof<R_GOTONLY_PC, R_GOTREL, R_PPC64_TOCBASE, R_PPC64_RELAX_TOC>(
+ expr)) {
+ in.got->hasGotOffRel = true;
+ }
// Process some TLS relocations, including relaxing TLS relocations.
// Note that this function does not handle all TLS relocations.
- if (unsigned Processed =
- handleTlsRelocation<ELFT>(Type, Sym, Sec, Offset, Addend, Expr)) {
- I += (Processed - 1);
+ if (unsigned processed =
+ handleTlsRelocation<ELFT>(type, sym, sec, offset, addend, expr)) {
+ i += (processed - 1);
return;
}
- // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
- if (needsPlt(Expr) && !Sym.isInPlt()) {
- if (Sym.isGnuIFunc() && !Sym.IsPreemptible)
- addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel,
- Sym);
- else
- addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
+ // We were asked not to generate PLT entries for ifuncs. Instead, pass the
+ // direct relocation on through.
+ if (sym.isGnuIFunc() && config->zIfuncNoplt) {
+ sym.exportDynamic = true;
+ mainPart->relaDyn->addReloc(type, &sec, offset, &sym, addend, R_ADDEND, type);
+ return;
}
- // Create a GOT slot if a relocation needs GOT.
- if (needsGot(Expr)) {
- if (Config->EMachine == EM_MIPS) {
- // MIPS ABI has special rules to process GOT entries and doesn't
- // require relocation entries for them. A special case is TLS
- // relocations. In that case dynamic loader applies dynamic
- // relocations to initialize TLS GOT entries.
- // See "Global Offset Table" in Chapter 5 in the following document
- // for detailed description:
- // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
- } else if (!Sym.isInGot()) {
- addGotEntry<ELFT>(Sym);
+ // Non-preemptible ifuncs require special handling. First, handle the usual
+ // case where the symbol isn't one of these.
+ if (!sym.isGnuIFunc() || sym.isPreemptible) {
+ // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
+ if (needsPlt(expr) && !sym.isInPlt())
+ addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
+
+ // Create a GOT slot if a relocation needs GOT.
+ if (needsGot(expr)) {
+ if (config->emachine == EM_MIPS) {
+ // MIPS ABI has special rules to process GOT entries and doesn't
+ // require relocation entries for them. A special case is TLS
+ // relocations. In that case dynamic loader applies dynamic
+ // relocations to initialize TLS GOT entries.
+ // See "Global Offset Table" in Chapter 5 in the following document
+ // for detailed description:
+ // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+ in.mipsGot->addEntry(*sec.file, sym, addend, expr);
+ } else if (!sym.isInGot()) {
+ addGotEntry(sym);
+ }
+ }
+ } else {
+ // Handle a reference to a non-preemptible ifunc. These are special in a
+ // few ways:
+ //
+ // - Unlike most non-preemptible symbols, non-preemptible ifuncs do not have
+ // a fixed value. But assuming that all references to the ifunc are
+ // GOT-generating or PLT-generating, the handling of an ifunc is
+ // relatively straightforward. We create a PLT entry in Iplt, which is
+ // usually at the end of .plt, which makes an indirect call using a
+ // matching GOT entry in igotPlt, which is usually at the end of .got.plt.
+ // The GOT entry is relocated using an IRELATIVE relocation in relaIplt,
+ // which is usually at the end of .rela.plt. Unlike most relocations in
+ // .rela.plt, which may be evaluated lazily without -z now, dynamic
+ // loaders evaluate IRELATIVE relocs eagerly, which means that for
+ // IRELATIVE relocs only, GOT-generating relocations can point directly to
+ // .got.plt without requiring a separate GOT entry.
+ //
+ // - Despite the fact that an ifunc does not have a fixed value, compilers
+ // that are not passed -fPIC will assume that they do, and will emit
+ // direct (non-GOT-generating, non-PLT-generating) relocations to the
+ // symbol. This means that if a direct relocation to the symbol is
+ // seen, the linker must set a value for the symbol, and this value must
+ // be consistent no matter what type of reference is made to the symbol.
+ // This can be done by creating a PLT entry for the symbol in the way
+ // described above and making it canonical, that is, making all references
+ // point to the PLT entry instead of the resolver. In lld we also store
+ // the address of the PLT entry in the dynamic symbol table, which means
+ // that the symbol will also have the same value in other modules.
+ // Because the value loaded from the GOT needs to be consistent with
+ // the value computed using a direct relocation, a non-preemptible ifunc
+ // may end up with two GOT entries, one in .got.plt that points to the
+ // address returned by the resolver and is used only by the PLT entry,
+ // and another in .got that points to the PLT entry and is used by
+ // GOT-generating relocations.
+ //
+ // - The fact that these symbols do not have a fixed value makes them an
+ // exception to the general rule that a statically linked executable does
+ // not require any form of dynamic relocation. To handle these relocations
+ // correctly, the IRELATIVE relocations are stored in an array which a
+ // statically linked executable's startup code must enumerate using the
+ // linker-defined symbols __rela?_iplt_{start,end}.
+ //
+ // - An absolute relocation to a non-preemptible ifunc (such as a global
+ // variable containing a pointer to the ifunc) needs to be relocated in
+ // the exact same way as a GOT entry, so we can avoid needing to make the
+ // PLT entry canonical by translating such relocations into IRELATIVE
+ // relocations in the relaIplt.
+ if (!sym.isInPlt()) {
+ // Create PLT and GOTPLT slots for the symbol.
+ sym.isInIplt = true;
+
+ // Create a copy of the symbol to use as the target of the IRELATIVE
+ // relocation in the igotPlt. This is in case we make the PLT canonical
+ // later, which would overwrite the original symbol.
+ //
+ // FIXME: Creating a copy of the symbol here is a bit of a hack. All
+ // that's really needed to create the IRELATIVE is the section and value,
+ // so ideally we should just need to copy those.
+ auto *directSym = make<Defined>(cast<Defined>(sym));
+ addPltEntry<ELFT>(in.iplt, in.igotPlt, in.relaIplt, target->iRelativeRel,
+ *directSym);
+ sym.pltIndex = directSym->pltIndex;
+ }
+ if (expr == R_ABS && addend == 0 && (sec.flags & SHF_WRITE)) {
+ // We might be able to represent this as an IRELATIVE. But we don't know
+ // yet whether some later relocation will make the symbol point to a
+ // canonical PLT, which would make this either a dynamic RELATIVE (PIC) or
+ // static (non-PIC) relocation. So we keep a record of the information
+ // required to process the relocation, and after scanRelocs() has been
+ // called on all relocations, the relocation is resolved by
+ // addIRelativeRelocs().
+ iRelativeRelocs.push_back({type, &sec, offset, &sym});
+ return;
+ }
+ if (needsGot(expr)) {
+ // Redirect GOT accesses to point to the Igot.
+ //
+ // This field is also used to keep track of whether we ever needed a GOT
+ // entry. If we did and we make the PLT canonical later, we'll need to
+ // create a GOT entry pointing to the PLT entry for Sym.
+ sym.gotInIgot = true;
+ } else if (!needsPlt(expr)) {
+ // Make the ifunc's PLT entry canonical by changing the value of its
+ // symbol to redirect all references to point to it.
+ unsigned entryOffset = sym.pltIndex * target->pltEntrySize;
+ if (config->zRetpolineplt)
+ entryOffset += target->pltHeaderSize;
+
+ auto &d = cast<Defined>(sym);
+ d.section = in.iplt;
+ d.value = entryOffset;
+ d.size = 0;
+ // It's important to set the symbol type here so that dynamic loaders
+ // don't try to call the PLT as if it were an ifunc resolver.
+ d.type = STT_FUNC;
+
+ if (sym.gotInIgot) {
+ // We previously encountered a GOT generating reference that we
+ // redirected to the Igot. Now that the PLT entry is canonical we must
+ // clear the redirection to the Igot and add a GOT entry. As we've
+ // changed the symbol type to STT_FUNC future GOT generating references
+ // will naturally use this GOT entry.
+ //
+ // We don't need to worry about creating a MIPS GOT here because ifuncs
+ // aren't a thing on MIPS.
+ sym.gotInIgot = false;
+ addGotEntry(sym);
+ }
}
}
- processRelocAux<ELFT>(Sec, Expr, Type, Offset, Sym, Rel, Addend);
+ processRelocAux<ELFT>(sec, expr, type, offset, sym, rel, addend);
}
template <class ELFT, class RelTy>
-static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
- OffsetGetter GetOffset(Sec);
+static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
+ OffsetGetter getOffset(sec);
// Not all relocations end up in Sec.Relocations, but a lot do.
- Sec.Relocations.reserve(Rels.size());
-
- for (auto I = Rels.begin(), End = Rels.end(); I != End;)
- scanReloc<ELFT>(Sec, GetOffset, I, End);
-
- // Sort relocations by offset to binary search for R_RISCV_PCREL_HI20
- if (Config->EMachine == EM_RISCV)
- std::stable_sort(Sec.Relocations.begin(), Sec.Relocations.end(),
- RelocationOffsetComparator{});
+ sec.relocations.reserve(rels.size());
+
+ for (auto i = rels.begin(), end = rels.end(); i != end;)
+ scanReloc<ELFT>(sec, getOffset, i, end);
+
+ // Sort relocations by offset for more efficient searching for
+ // R_RISCV_PCREL_HI20 and R_PPC64_ADDR64.
+ if (config->emachine == EM_RISCV ||
+ (config->emachine == EM_PPC64 && sec.name == ".toc"))
+ llvm::stable_sort(sec.relocations,
+ [](const Relocation &lhs, const Relocation &rhs) {
+ return lhs.offset < rhs.offset;
+ });
}
-template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
- if (S.AreRelocsRela)
- scanRelocs<ELFT>(S, S.relas<ELFT>());
+template <class ELFT> void elf::scanRelocations(InputSectionBase &s) {
+ if (s.areRelocsRela)
+ scanRelocs<ELFT>(s, s.relas<ELFT>());
else
- scanRelocs<ELFT>(S, S.rels<ELFT>());
+ scanRelocs<ELFT>(s, s.rels<ELFT>());
+}
+
+// Figure out which representation to use for any absolute relocs to
+// non-preemptible ifuncs that we visited during scanRelocs().
+void elf::addIRelativeRelocs() {
+ for (IRelativeReloc &r : iRelativeRelocs) {
+ if (r.sym->type == STT_GNU_IFUNC)
+ in.relaIplt->addReloc(
+ {target->iRelativeRel, r.sec, r.offset, true, r.sym, 0});
+ else if (config->isPic)
+ addRelativeReloc(r.sec, r.offset, r.sym, 0, R_ABS, r.type);
+ else
+ r.sec->relocations.push_back({R_ABS, r.type, r.offset, 0, r.sym});
+ }
+ iRelativeRelocs.clear();
}
-static bool mergeCmp(const InputSection *A, const InputSection *B) {
+static bool mergeCmp(const InputSection *a, const InputSection *b) {
// std::merge requires a strict weak ordering.
- if (A->OutSecOff < B->OutSecOff)
+ if (a->outSecOff < b->outSecOff)
return true;
- if (A->OutSecOff == B->OutSecOff) {
- auto *TA = dyn_cast<ThunkSection>(A);
- auto *TB = dyn_cast<ThunkSection>(B);
+ if (a->outSecOff == b->outSecOff) {
+ auto *ta = dyn_cast<ThunkSection>(a);
+ auto *tb = dyn_cast<ThunkSection>(b);
// Check if Thunk is immediately before any specific Target
// InputSection for example Mips LA25 Thunks.
- if (TA && TA->getTargetInputSection() == B)
+ if (ta && ta->getTargetInputSection() == b)
return true;
// Place Thunk Sections without specific targets before
// non-Thunk Sections.
- if (TA && !TB && !TA->getTargetInputSection())
+ if (ta && !tb && !ta->getTargetInputSection())
return true;
}
@@ -1123,14 +1410,14 @@ static bool mergeCmp(const InputSection *A, const InputSection *B) {
// Call Fn on every executable InputSection accessed via the linker script
// InputSectionDescription::Sections.
static void forEachInputSectionDescription(
- ArrayRef<OutputSection *> OutputSections,
- llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) {
- for (OutputSection *OS : OutputSections) {
- if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
+ ArrayRef<OutputSection *> outputSections,
+ llvm::function_ref<void(OutputSection *, InputSectionDescription *)> fn) {
+ for (OutputSection *os : outputSections) {
+ if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR))
continue;
- for (BaseCommand *BC : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(BC))
- Fn(OS, ISD);
+ for (BaseCommand *bc : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(bc))
+ fn(os, isd);
}
}
@@ -1225,54 +1512,54 @@ static void forEachInputSectionDescription(
// in the Sections vector, and recalculate the InputSection output section
// offsets.
// This may invalidate any output section offsets stored outside of InputSection
-void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
+void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> outputSections) {
forEachInputSectionDescription(
- OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
- if (ISD->ThunkSections.empty())
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ if (isd->thunkSections.empty())
return;
// Remove any zero sized precreated Thunks.
- llvm::erase_if(ISD->ThunkSections,
- [](const std::pair<ThunkSection *, uint32_t> &TS) {
- return TS.first->getSize() == 0;
+ llvm::erase_if(isd->thunkSections,
+ [](const std::pair<ThunkSection *, uint32_t> &ts) {
+ return ts.first->getSize() == 0;
});
// ISD->ThunkSections contains all created ThunkSections, including
// those inserted in previous passes. Extract the Thunks created this
- // pass and order them in ascending OutSecOff.
- std::vector<ThunkSection *> NewThunks;
- for (const std::pair<ThunkSection *, uint32_t> TS : ISD->ThunkSections)
- if (TS.second == Pass)
- NewThunks.push_back(TS.first);
- std::stable_sort(NewThunks.begin(), NewThunks.end(),
- [](const ThunkSection *A, const ThunkSection *B) {
- return A->OutSecOff < B->OutSecOff;
- });
-
- // Merge sorted vectors of Thunks and InputSections by OutSecOff
- std::vector<InputSection *> Tmp;
- Tmp.reserve(ISD->Sections.size() + NewThunks.size());
-
- std::merge(ISD->Sections.begin(), ISD->Sections.end(),
- NewThunks.begin(), NewThunks.end(), std::back_inserter(Tmp),
+ // pass and order them in ascending outSecOff.
+ std::vector<ThunkSection *> newThunks;
+ for (const std::pair<ThunkSection *, uint32_t> ts : isd->thunkSections)
+ if (ts.second == pass)
+ newThunks.push_back(ts.first);
+ llvm::stable_sort(newThunks,
+ [](const ThunkSection *a, const ThunkSection *b) {
+ return a->outSecOff < b->outSecOff;
+ });
+
+ // Merge sorted vectors of Thunks and InputSections by outSecOff
+ std::vector<InputSection *> tmp;
+ tmp.reserve(isd->sections.size() + newThunks.size());
+
+ std::merge(isd->sections.begin(), isd->sections.end(),
+ newThunks.begin(), newThunks.end(), std::back_inserter(tmp),
mergeCmp);
- ISD->Sections = std::move(Tmp);
+ isd->sections = std::move(tmp);
});
}
// Find or create a ThunkSection within the InputSectionDescription (ISD) that
// is in range of Src. An ISD maps to a range of InputSections described by a
// linker script section pattern such as { .text .text.* }.
-ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *OS, InputSection *IS,
- InputSectionDescription *ISD,
- uint32_t Type, uint64_t Src) {
- for (std::pair<ThunkSection *, uint32_t> TP : ISD->ThunkSections) {
- ThunkSection *TS = TP.first;
- uint64_t TSBase = OS->Addr + TS->OutSecOff;
- uint64_t TSLimit = TSBase + TS->getSize();
- if (Target->inBranchRange(Type, Src, (Src > TSLimit) ? TSBase : TSLimit))
- return TS;
+ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *os, InputSection *isec,
+ InputSectionDescription *isd,
+ uint32_t type, uint64_t src) {
+ for (std::pair<ThunkSection *, uint32_t> tp : isd->thunkSections) {
+ ThunkSection *ts = tp.first;
+ uint64_t tsBase = os->addr + ts->outSecOff;
+ uint64_t tsLimit = tsBase + ts->getSize();
+ if (target->inBranchRange(type, src, (src > tsLimit) ? tsBase : tsLimit))
+ return ts;
}
// No suitable ThunkSection exists. This can happen when there is a branch
@@ -1280,40 +1567,40 @@ ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *OS, InputSection *IS,
// many Thunks. Create a new ThunkSection as close to the InputSection as
// possible. Error if InputSection is so large we cannot place ThunkSection
// anywhere in Range.
- uint64_t ThunkSecOff = IS->OutSecOff;
- if (!Target->inBranchRange(Type, Src, OS->Addr + ThunkSecOff)) {
- ThunkSecOff = IS->OutSecOff + IS->getSize();
- if (!Target->inBranchRange(Type, Src, OS->Addr + ThunkSecOff))
+ uint64_t thunkSecOff = isec->outSecOff;
+ if (!target->inBranchRange(type, src, os->addr + thunkSecOff)) {
+ thunkSecOff = isec->outSecOff + isec->getSize();
+ if (!target->inBranchRange(type, src, os->addr + thunkSecOff))
fatal("InputSection too large for range extension thunk " +
- IS->getObjMsg(Src - (OS->Addr + IS->OutSecOff)));
+ isec->getObjMsg(src - (os->addr + isec->outSecOff)));
}
- return addThunkSection(OS, ISD, ThunkSecOff);
+ return addThunkSection(os, isd, thunkSecOff);
}
// Add a Thunk that needs to be placed in a ThunkSection that immediately
// precedes its Target.
-ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
- ThunkSection *TS = ThunkedSections.lookup(IS);
- if (TS)
- return TS;
+ThunkSection *ThunkCreator::getISThunkSec(InputSection *isec) {
+ ThunkSection *ts = thunkedSections.lookup(isec);
+ if (ts)
+ return ts;
// Find InputSectionRange within Target Output Section (TOS) that the
// InputSection (IS) that we need to precede is in.
- OutputSection *TOS = IS->getParent();
- for (BaseCommand *BC : TOS->SectionCommands) {
- auto *ISD = dyn_cast<InputSectionDescription>(BC);
- if (!ISD || ISD->Sections.empty())
+ OutputSection *tos = isec->getParent();
+ for (BaseCommand *bc : tos->sectionCommands) {
+ auto *isd = dyn_cast<InputSectionDescription>(bc);
+ if (!isd || isd->sections.empty())
continue;
- InputSection *First = ISD->Sections.front();
- InputSection *Last = ISD->Sections.back();
+ InputSection *first = isd->sections.front();
+ InputSection *last = isd->sections.back();
- if (IS->OutSecOff < First->OutSecOff || Last->OutSecOff < IS->OutSecOff)
+ if (isec->outSecOff < first->outSecOff || last->outSecOff < isec->outSecOff)
continue;
- TS = addThunkSection(TOS, ISD, IS->OutSecOff);
- ThunkedSections[IS] = TS;
- return TS;
+ ts = addThunkSection(tos, isd, isec->outSecOff);
+ thunkedSections[isec] = ts;
+ return ts;
}
return nullptr;
@@ -1336,82 +1623,93 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
// distance from a thunk to its target will be sufficiently small to
// allow for the creation of a short thunk.
void ThunkCreator::createInitialThunkSections(
- ArrayRef<OutputSection *> OutputSections) {
- uint32_t ThunkSectionSpacing = Target->getThunkSectionSpacing();
+ ArrayRef<OutputSection *> outputSections) {
+ uint32_t thunkSectionSpacing = target->getThunkSectionSpacing();
forEachInputSectionDescription(
- OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
- if (ISD->Sections.empty())
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ if (isd->sections.empty())
return;
- uint32_t ISDBegin = ISD->Sections.front()->OutSecOff;
- uint32_t ISDEnd =
- ISD->Sections.back()->OutSecOff + ISD->Sections.back()->getSize();
- uint32_t LastThunkLowerBound = -1;
- if (ISDEnd - ISDBegin > ThunkSectionSpacing * 2)
- LastThunkLowerBound = ISDEnd - ThunkSectionSpacing;
-
- uint32_t ISLimit;
- uint32_t PrevISLimit = ISDBegin;
- uint32_t ThunkUpperBound = ISDBegin + ThunkSectionSpacing;
-
- for (const InputSection *IS : ISD->Sections) {
- ISLimit = IS->OutSecOff + IS->getSize();
- if (ISLimit > ThunkUpperBound) {
- addThunkSection(OS, ISD, PrevISLimit);
- ThunkUpperBound = PrevISLimit + ThunkSectionSpacing;
+ uint32_t isdBegin = isd->sections.front()->outSecOff;
+ uint32_t isdEnd =
+ isd->sections.back()->outSecOff + isd->sections.back()->getSize();
+ uint32_t lastThunkLowerBound = -1;
+ if (isdEnd - isdBegin > thunkSectionSpacing * 2)
+ lastThunkLowerBound = isdEnd - thunkSectionSpacing;
+
+ uint32_t isecLimit;
+ uint32_t prevIsecLimit = isdBegin;
+ uint32_t thunkUpperBound = isdBegin + thunkSectionSpacing;
+
+ for (const InputSection *isec : isd->sections) {
+ isecLimit = isec->outSecOff + isec->getSize();
+ if (isecLimit > thunkUpperBound) {
+ addThunkSection(os, isd, prevIsecLimit);
+ thunkUpperBound = prevIsecLimit + thunkSectionSpacing;
}
- if (ISLimit > LastThunkLowerBound)
+ if (isecLimit > lastThunkLowerBound)
break;
- PrevISLimit = ISLimit;
+ prevIsecLimit = isecLimit;
}
- addThunkSection(OS, ISD, ISLimit);
+ addThunkSection(os, isd, isecLimit);
});
}
-ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS,
- InputSectionDescription *ISD,
- uint64_t Off) {
- auto *TS = make<ThunkSection>(OS, Off);
- ISD->ThunkSections.push_back({TS, Pass});
- return TS;
+ThunkSection *ThunkCreator::addThunkSection(OutputSection *os,
+ InputSectionDescription *isd,
+ uint64_t off) {
+ auto *ts = make<ThunkSection>(os, off);
+ ts->partition = os->partition;
+ isd->thunkSections.push_back({ts, pass});
+ return ts;
}
-std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
- uint64_t Src) {
- std::vector<Thunk *> *ThunkVec = nullptr;
+static bool isThunkSectionCompatible(InputSection *source,
+ SectionBase *target) {
+ // We can't reuse thunks in different loadable partitions because they might
+ // not be loaded. But partition 1 (the main partition) will always be loaded.
+ if (source->partition != target->partition)
+ return target->partition == 1;
+ return true;
+}
+
+std::pair<Thunk *, bool> ThunkCreator::getThunk(InputSection *isec,
+ Relocation &rel, uint64_t src) {
+ std::vector<Thunk *> *thunkVec = nullptr;
// We use (section, offset) pair to find the thunk position if possible so
// that we create only one thunk for aliased symbols or ICFed sections.
- if (auto *D = dyn_cast<Defined>(&Sym))
- if (!D->isInPlt() && D->Section)
- ThunkVec = &ThunkedSymbolsBySection[{D->Section->Repl, D->Value}];
- if (!ThunkVec)
- ThunkVec = &ThunkedSymbols[&Sym];
+ if (auto *d = dyn_cast<Defined>(rel.sym))
+ if (!d->isInPlt() && d->section)
+ thunkVec = &thunkedSymbolsBySection[{d->section->repl, d->value}];
+ if (!thunkVec)
+ thunkVec = &thunkedSymbols[rel.sym];
// Check existing Thunks for Sym to see if they can be reused
- for (Thunk *T : *ThunkVec)
- if (T->isCompatibleWith(Type) &&
- Target->inBranchRange(Type, Src, T->getThunkTargetSym()->getVA()))
- return std::make_pair(T, false);
+ for (Thunk *t : *thunkVec)
+ if (isThunkSectionCompatible(isec, t->getThunkTargetSym()->section) &&
+ t->isCompatibleWith(*isec, rel) &&
+ target->inBranchRange(rel.type, src, t->getThunkTargetSym()->getVA()))
+ return std::make_pair(t, false);
// No existing compatible Thunk in range, create a new one
- Thunk *T = addThunk(Type, Sym);
- ThunkVec->push_back(T);
- return std::make_pair(T, true);
+ Thunk *t = addThunk(*isec, rel);
+ thunkVec->push_back(t);
+ return std::make_pair(t, true);
}
// Return true if the relocation target is an in range Thunk.
// Return false if the relocation is not to a Thunk. If the relocation target
// was originally to a Thunk, but is no longer in range we revert the
// relocation back to its original non-Thunk target.
-bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) {
- if (Thunk *T = Thunks.lookup(Rel.Sym)) {
- if (Target->inBranchRange(Rel.Type, Src, Rel.Sym->getVA()))
+bool ThunkCreator::normalizeExistingThunk(Relocation &rel, uint64_t src) {
+ if (Thunk *t = thunks.lookup(rel.sym)) {
+ if (target->inBranchRange(rel.type, src, rel.sym->getVA()))
return true;
- Rel.Sym = &T->Destination;
- if (Rel.Sym->isInPlt())
- Rel.Expr = toPlt(Rel.Expr);
+ rel.sym = &t->destination;
+ if (rel.sym->isInPlt())
+ rel.expr = toPlt(rel.expr);
}
return false;
}
@@ -1441,15 +1739,15 @@ bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) {
// made no changes. If the target requires range extension thunks, currently
// ARM, then any future change in offset between caller and callee risks a
// relocation out of range error.
-bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
- bool AddressesChanged = false;
+bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
+ bool addressesChanged = false;
- if (Pass == 0 && Target->getThunkSectionSpacing())
- createInitialThunkSections(OutputSections);
+ if (pass == 0 && target->getThunkSectionSpacing())
+ createInitialThunkSections(outputSections);
// With Thunk Size much smaller than branch range we expect to
// converge quickly; if we get to 10 something has gone wrong.
- if (Pass == 10)
+ if (pass == 10)
fatal("thunk creation not converged");
// Create all the Thunks and insert them into synthetic ThunkSections. The
@@ -1458,55 +1756,64 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
// ThunkSections as ThunkSections are not always inserted into the same
// InputSectionDescription as the caller.
forEachInputSectionDescription(
- OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
- for (InputSection *IS : ISD->Sections)
- for (Relocation &Rel : IS->Relocations) {
- uint64_t Src = IS->getVA(Rel.Offset);
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ for (InputSection *isec : isd->sections)
+ for (Relocation &rel : isec->relocations) {
+ uint64_t src = isec->getVA(rel.offset);
// If we are a relocation to an existing Thunk, check if it is
// still in range. If not then Rel will be altered to point to its
// original target so another Thunk can be generated.
- if (Pass > 0 && normalizeExistingThunk(Rel, Src))
+ if (pass > 0 && normalizeExistingThunk(rel, src))
continue;
- if (!Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Src,
- *Rel.Sym))
+ if (!target->needsThunk(rel.expr, rel.type, isec->file, src,
+ *rel.sym))
continue;
- Thunk *T;
- bool IsNew;
- std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src);
+ Thunk *t;
+ bool isNew;
+ std::tie(t, isNew) = getThunk(isec, rel, src);
- if (IsNew) {
+ if (isNew) {
// Find or create a ThunkSection for the new Thunk
- ThunkSection *TS;
- if (auto *TIS = T->getTargetInputSection())
- TS = getISThunkSec(TIS);
+ ThunkSection *ts;
+ if (auto *tis = t->getTargetInputSection())
+ ts = getISThunkSec(tis);
else
- TS = getISDThunkSec(OS, IS, ISD, Rel.Type, Src);
- TS->addThunk(T);
- Thunks[T->getThunkTargetSym()] = T;
+ ts = getISDThunkSec(os, isec, isd, rel.type, src);
+ ts->addThunk(t);
+ thunks[t->getThunkTargetSym()] = t;
}
// Redirect relocation to Thunk, we never go via the PLT to a Thunk
- Rel.Sym = T->getThunkTargetSym();
- Rel.Expr = fromPlt(Rel.Expr);
+ rel.sym = t->getThunkTargetSym();
+ rel.expr = fromPlt(rel.expr);
+
+ // The addend of R_PPC_PLTREL24 should be ignored after changing to
+ // R_PC.
+ if (config->emachine == EM_PPC && rel.type == R_PPC_PLTREL24)
+ rel.addend = 0;
}
- for (auto &P : ISD->ThunkSections)
- AddressesChanged |= P.first->assignOffsets();
+ for (auto &p : isd->thunkSections)
+ addressesChanged |= p.first->assignOffsets();
});
- for (auto &P : ThunkedSections)
- AddressesChanged |= P.second->assignOffsets();
+ for (auto &p : thunkedSections)
+ addressesChanged |= p.second->assignOffsets();
// Merge all created synthetic ThunkSections back into OutputSection
- mergeThunks(OutputSections);
- ++Pass;
- return AddressesChanged;
+ mergeThunks(outputSections);
+ ++pass;
+ return addressesChanged;
}
template void elf::scanRelocations<ELF32LE>(InputSectionBase &);
template void elf::scanRelocations<ELF32BE>(InputSectionBase &);
template void elf::scanRelocations<ELF64LE>(InputSectionBase &);
template void elf::scanRelocations<ELF64BE>(InputSectionBase &);
+template void elf::reportUndefinedSymbols<ELF32LE>();
+template void elf::reportUndefinedSymbols<ELF32BE>();
+template void elf::reportUndefinedSymbols<ELF64LE>();
+template void elf::reportUndefinedSymbols<ELF64BE>();
diff --git a/ELF/Relocations.h b/ELF/Relocations.h
index d00e68bd36e69..d74d7b9b458e8 100644
--- a/ELF/Relocations.h
+++ b/ELF/Relocations.h
@@ -1,9 +1,8 @@
//===- Relocations.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -24,136 +23,100 @@ class OutputSection;
class SectionBase;
// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
-typedef uint32_t RelType;
+using RelType = uint32_t;
// List of target-independent relocation types. Relocations read
// from files are converted to these types so that the main code
// doesn't have to know about architecture-specific details.
enum RelExpr {
- R_INVALID,
R_ABS,
R_ADDEND,
- R_AARCH64_GOT_PAGE_PC,
- // The expression is used for IFUNC support. Describes PC-relative
- // address of the memory page of GOT entry. This entry is used for
- // a redirection to IPLT.
- R_AARCH64_GOT_PAGE_PC_PLT,
- R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
- R_AARCH64_PAGE_PC,
- R_AARCH64_PLT_PAGE_PC,
- R_AARCH64_TLSDESC_PAGE,
- R_ARM_SBREL,
+ R_DTPREL,
R_GOT,
- // The expression is used for IFUNC support. Evaluates to GOT entry,
- // containing redirection to the IPLT.
- R_GOT_PLT,
- R_GOTONLY_PC,
- R_GOTONLY_PC_FROM_END,
- R_GOTREL,
- R_GOTREL_FROM_END,
- R_GOT_FROM_END,
R_GOT_OFF,
R_GOT_PC,
- R_HEXAGON_GOT,
+ R_GOTONLY_PC,
+ R_GOTPLTONLY_PC,
+ R_GOTPLT,
+ R_GOTPLTREL,
+ R_GOTREL,
R_HINT,
- R_MIPS_GOTREL,
- R_MIPS_GOT_GP,
- R_MIPS_GOT_GP_PC,
- R_MIPS_GOT_LOCAL_PAGE,
- R_MIPS_GOT_OFF,
- R_MIPS_GOT_OFF32,
- R_MIPS_TLSGD,
- R_MIPS_TLSLD,
R_NEG_TLS,
R_NONE,
R_PC,
R_PLT,
R_PLT_PC,
- R_PPC_CALL,
- R_PPC_CALL_PLT,
- R_PPC_TOC,
R_RELAX_GOT_PC,
R_RELAX_GOT_PC_NOPIC,
R_RELAX_TLS_GD_TO_IE,
R_RELAX_TLS_GD_TO_IE_ABS,
- R_RELAX_TLS_GD_TO_IE_END,
R_RELAX_TLS_GD_TO_IE_GOT_OFF,
+ R_RELAX_TLS_GD_TO_IE_GOTPLT,
R_RELAX_TLS_GD_TO_LE,
R_RELAX_TLS_GD_TO_LE_NEG,
R_RELAX_TLS_IE_TO_LE,
R_RELAX_TLS_LD_TO_LE,
R_RELAX_TLS_LD_TO_LE_ABS,
- R_RISCV_PC_INDIRECT,
R_SIZE,
R_TLS,
R_TLSDESC,
R_TLSDESC_CALL,
+ R_TLSDESC_PC,
R_TLSGD_GOT,
- R_TLSGD_GOT_FROM_END,
+ R_TLSGD_GOTPLT,
R_TLSGD_PC,
R_TLSIE_HINT,
R_TLSLD_GOT,
- R_TLSLD_GOT_FROM_END,
+ R_TLSLD_GOTPLT,
R_TLSLD_GOT_OFF,
R_TLSLD_HINT,
R_TLSLD_PC,
-};
-// Build a bitmask with one bit set for each RelExpr.
-//
-// Constexpr function arguments can't be used in static asserts, so we
-// use template arguments to build the mask.
-// But function template partial specializations don't exist (needed
-// for base case of the recursion), so we need a dummy struct.
-template <RelExpr... Exprs> struct RelExprMaskBuilder {
- static inline uint64_t build() { return 0; }
-};
-
-// Specialization for recursive case.
-template <RelExpr Head, RelExpr... Tail>
-struct RelExprMaskBuilder<Head, Tail...> {
- static inline uint64_t build() {
- static_assert(0 <= Head && Head < 64,
- "RelExpr is too large for 64-bit mask!");
- return (uint64_t(1) << Head) | RelExprMaskBuilder<Tail...>::build();
- }
+ // The following is abstract relocation types used for only one target.
+ //
+ // Even though RelExpr is intended to be a target-neutral representation
+ // of a relocation type, there are some relocations whose semantics are
+ // unique to a target. Such relocation are marked with R_<TARGET_NAME>.
+ R_AARCH64_GOT_PAGE_PC,
+ R_AARCH64_PAGE_PC,
+ R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
+ R_AARCH64_TLSDESC_PAGE,
+ R_ARM_SBREL,
+ R_HEXAGON_GOT,
+ R_MIPS_GOTREL,
+ R_MIPS_GOT_GP,
+ R_MIPS_GOT_GP_PC,
+ R_MIPS_GOT_LOCAL_PAGE,
+ R_MIPS_GOT_OFF,
+ R_MIPS_GOT_OFF32,
+ R_MIPS_TLSGD,
+ R_MIPS_TLSLD,
+ R_PPC32_PLTREL,
+ R_PPC64_CALL,
+ R_PPC64_CALL_PLT,
+ R_PPC64_RELAX_TOC,
+ R_PPC64_TOCBASE,
+ R_RISCV_ADD,
+ R_RISCV_PC_INDIRECT,
};
-// Return true if `Expr` is one of `Exprs`.
-// There are fewer than 64 RelExpr's, so we can represent any set of
-// RelExpr's as a constant bit mask and test for membership with a
-// couple cheap bitwise operations.
-template <RelExpr... Exprs> bool isRelExprOneOf(RelExpr Expr) {
- assert(0 <= Expr && (int)Expr < 64 &&
- "RelExpr is too large for 64-bit mask!");
- return (uint64_t(1) << Expr) & RelExprMaskBuilder<Exprs...>::build();
-}
-
// Architecture-neutral representation of relocation.
struct Relocation {
- RelExpr Expr;
- RelType Type;
- uint64_t Offset;
- int64_t Addend;
- Symbol *Sym;
+ RelExpr expr;
+ RelType type;
+ uint64_t offset;
+ int64_t addend;
+ Symbol *sym;
};
-struct RelocationOffsetComparator {
- bool operator()(const Relocation &Lhs, const Relocation &Rhs) {
- return Lhs.Offset < Rhs.Offset;
- }
+// This function writes undefined symbol diagnostics to an internal buffer.
+// Call reportUndefinedSymbols() after calling scanRelocations() to emit
+// the diagnostics.
+template <class ELFT> void scanRelocations(InputSectionBase &);
- // For std::lower_bound, std::upper_bound, std::equal_range.
- bool operator()(const Relocation &Rel, uint64_t Val) {
- return Rel.Offset < Val;
- }
+template <class ELFT> void reportUndefinedSymbols();
- bool operator()(uint64_t Val, const Relocation &Rel) {
- return Val < Rel.Offset;
- }
-};
-
-template <class ELFT> void scanRelocations(InputSectionBase &);
+void addIRelativeRelocs();
class ThunkSection;
class Thunk;
@@ -162,56 +125,57 @@ struct InputSectionDescription;
class ThunkCreator {
public:
// Return true if Thunks have been added to OutputSections
- bool createThunks(ArrayRef<OutputSection *> OutputSections);
+ bool createThunks(ArrayRef<OutputSection *> outputSections);
// The number of completed passes of createThunks this permits us
// to do one time initialization on Pass 0 and put a limit on the
// number of times it can be called to prevent infinite loops.
- uint32_t Pass = 0;
+ uint32_t pass = 0;
private:
- void mergeThunks(ArrayRef<OutputSection *> OutputSections);
+ void mergeThunks(ArrayRef<OutputSection *> outputSections);
- ThunkSection *getISDThunkSec(OutputSection *OS, InputSection *IS,
- InputSectionDescription *ISD, uint32_t Type,
- uint64_t Src);
+ ThunkSection *getISDThunkSec(OutputSection *os, InputSection *isec,
+ InputSectionDescription *isd, uint32_t type,
+ uint64_t src);
- ThunkSection *getISThunkSec(InputSection *IS);
+ ThunkSection *getISThunkSec(InputSection *isec);
- void createInitialThunkSections(ArrayRef<OutputSection *> OutputSections);
+ void createInitialThunkSections(ArrayRef<OutputSection *> outputSections);
- std::pair<Thunk *, bool> getThunk(Symbol &Sym, RelType Type, uint64_t Src);
+ std::pair<Thunk *, bool> getThunk(InputSection *isec, Relocation &rel,
+ uint64_t src);
- ThunkSection *addThunkSection(OutputSection *OS, InputSectionDescription *,
- uint64_t Off);
+ ThunkSection *addThunkSection(OutputSection *os, InputSectionDescription *,
+ uint64_t off);
- bool normalizeExistingThunk(Relocation &Rel, uint64_t Src);
+ bool normalizeExistingThunk(Relocation &rel, uint64_t src);
// Record all the available Thunks for a Symbol
llvm::DenseMap<std::pair<SectionBase *, uint64_t>, std::vector<Thunk *>>
- ThunkedSymbolsBySection;
- llvm::DenseMap<Symbol *, std::vector<Thunk *>> ThunkedSymbols;
+ thunkedSymbolsBySection;
+ llvm::DenseMap<Symbol *, std::vector<Thunk *>> thunkedSymbols;
// Find a Thunk from the Thunks symbol definition, we can use this to find
// the Thunk from a relocation to the Thunks symbol definition.
- llvm::DenseMap<Symbol *, Thunk *> Thunks;
+ llvm::DenseMap<Symbol *, Thunk *> thunks;
// Track InputSections that have an inline ThunkSection placed in front
// an inline ThunkSection may have control fall through to the section below
// so we need to make sure that there is only one of them.
// The Mips LA25 Thunk is an example of an inline ThunkSection.
- llvm::DenseMap<InputSection *, ThunkSection *> ThunkedSections;
+ llvm::DenseMap<InputSection *, ThunkSection *> thunkedSections;
};
// Return a int64_t to make sure we get the sign extension out of the way as
// early as possible.
template <class ELFT>
-static inline int64_t getAddend(const typename ELFT::Rel &Rel) {
+static inline int64_t getAddend(const typename ELFT::Rel &rel) {
return 0;
}
template <class ELFT>
-static inline int64_t getAddend(const typename ELFT::Rela &Rel) {
- return Rel.r_addend;
+static inline int64_t getAddend(const typename ELFT::Rela &rel) {
+ return rel.r_addend;
}
} // namespace elf
} // namespace lld
diff --git a/ELF/ScriptLexer.cpp b/ELF/ScriptLexer.cpp
index 9a372c6d1c6f1..953a3df8a31c6 100644
--- a/ELF/ScriptLexer.cpp
+++ b/ELF/ScriptLexer.cpp
@@ -1,9 +1,8 @@
//===- ScriptLexer.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -42,166 +41,169 @@ using namespace lld::elf;
// Returns a whole line containing the current token.
StringRef ScriptLexer::getLine() {
- StringRef S = getCurrentMB().getBuffer();
- StringRef Tok = Tokens[Pos - 1];
+ StringRef s = getCurrentMB().getBuffer();
+ StringRef tok = tokens[pos - 1];
- size_t Pos = S.rfind('\n', Tok.data() - S.data());
- if (Pos != StringRef::npos)
- S = S.substr(Pos + 1);
- return S.substr(0, S.find_first_of("\r\n"));
+ size_t pos = s.rfind('\n', tok.data() - s.data());
+ if (pos != StringRef::npos)
+ s = s.substr(pos + 1);
+ return s.substr(0, s.find_first_of("\r\n"));
}
// Returns 1-based line number of the current token.
size_t ScriptLexer::getLineNumber() {
- StringRef S = getCurrentMB().getBuffer();
- StringRef Tok = Tokens[Pos - 1];
- return S.substr(0, Tok.data() - S.data()).count('\n') + 1;
+ StringRef s = getCurrentMB().getBuffer();
+ StringRef tok = tokens[pos - 1];
+ return s.substr(0, tok.data() - s.data()).count('\n') + 1;
}
// Returns 0-based column number of the current token.
size_t ScriptLexer::getColumnNumber() {
- StringRef Tok = Tokens[Pos - 1];
- return Tok.data() - getLine().data();
+ StringRef tok = tokens[pos - 1];
+ return tok.data() - getLine().data();
}
std::string ScriptLexer::getCurrentLocation() {
- std::string Filename = getCurrentMB().getBufferIdentifier();
- return (Filename + ":" + Twine(getLineNumber())).str();
+ std::string filename = getCurrentMB().getBufferIdentifier();
+ return (filename + ":" + Twine(getLineNumber())).str();
}
-ScriptLexer::ScriptLexer(MemoryBufferRef MB) { tokenize(MB); }
+ScriptLexer::ScriptLexer(MemoryBufferRef mb) { tokenize(mb); }
// We don't want to record cascading errors. Keep only the first one.
-void ScriptLexer::setError(const Twine &Msg) {
+void ScriptLexer::setError(const Twine &msg) {
if (errorCount())
return;
- std::string S = (getCurrentLocation() + ": " + Msg).str();
- if (Pos)
- S += "\n>>> " + getLine().str() + "\n>>> " +
+ std::string s = (getCurrentLocation() + ": " + msg).str();
+ if (pos)
+ s += "\n>>> " + getLine().str() + "\n>>> " +
std::string(getColumnNumber(), ' ') + "^";
- error(S);
+ error(s);
}
// Split S into linker script tokens.
-void ScriptLexer::tokenize(MemoryBufferRef MB) {
- std::vector<StringRef> Vec;
- MBs.push_back(MB);
- StringRef S = MB.getBuffer();
- StringRef Begin = S;
+void ScriptLexer::tokenize(MemoryBufferRef mb) {
+ std::vector<StringRef> vec;
+ mbs.push_back(mb);
+ StringRef s = mb.getBuffer();
+ StringRef begin = s;
for (;;) {
- S = skipSpace(S);
- if (S.empty())
+ s = skipSpace(s);
+ if (s.empty())
break;
// Quoted token. Note that double-quote characters are parts of a token
// because, in a glob match context, only unquoted tokens are interpreted
// as glob patterns. Double-quoted tokens are literal patterns in that
// context.
- if (S.startswith("\"")) {
- size_t E = S.find("\"", 1);
- if (E == StringRef::npos) {
- StringRef Filename = MB.getBufferIdentifier();
- size_t Lineno = Begin.substr(0, S.data() - Begin.data()).count('\n');
- error(Filename + ":" + Twine(Lineno + 1) + ": unclosed quote");
+ if (s.startswith("\"")) {
+ size_t e = s.find("\"", 1);
+ if (e == StringRef::npos) {
+ StringRef filename = mb.getBufferIdentifier();
+ size_t lineno = begin.substr(0, s.data() - begin.data()).count('\n');
+ error(filename + ":" + Twine(lineno + 1) + ": unclosed quote");
return;
}
- Vec.push_back(S.take_front(E + 1));
- S = S.substr(E + 1);
+ vec.push_back(s.take_front(e + 1));
+ s = s.substr(e + 1);
continue;
}
// ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>".
// "|", "||", "&" and "&&" are different operators.
- if (S.startswith("<<") || S.startswith("<=") || S.startswith(">>") ||
- S.startswith(">=") || S.startswith("||") || S.startswith("&&")) {
- Vec.push_back(S.substr(0, 2));
- S = S.substr(2);
+ if (s.startswith("<<") || s.startswith("<=") || s.startswith(">>") ||
+ s.startswith(">=") || s.startswith("||") || s.startswith("&&")) {
+ vec.push_back(s.substr(0, 2));
+ s = s.substr(2);
continue;
}
// Unquoted token. This is more relaxed than tokens in C-like language,
// so that you can write "file-name.cpp" as one bare token, for example.
- size_t Pos = S.find_first_not_of(
+ size_t pos = s.find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789_.$/\\~=+[]*?-!^:");
// A character that cannot start a word (which is usually a
// punctuation) forms a single character token.
- if (Pos == 0)
- Pos = 1;
- Vec.push_back(S.substr(0, Pos));
- S = S.substr(Pos);
+ if (pos == 0)
+ pos = 1;
+ vec.push_back(s.substr(0, pos));
+ s = s.substr(pos);
}
- Tokens.insert(Tokens.begin() + Pos, Vec.begin(), Vec.end());
+ tokens.insert(tokens.begin() + pos, vec.begin(), vec.end());
}
// Skip leading whitespace characters or comments.
-StringRef ScriptLexer::skipSpace(StringRef S) {
+StringRef ScriptLexer::skipSpace(StringRef s) {
for (;;) {
- if (S.startswith("/*")) {
- size_t E = S.find("*/", 2);
- if (E == StringRef::npos) {
+ if (s.startswith("/*")) {
+ size_t e = s.find("*/", 2);
+ if (e == StringRef::npos) {
error("unclosed comment in a linker script");
return "";
}
- S = S.substr(E + 2);
+ s = s.substr(e + 2);
continue;
}
- if (S.startswith("#")) {
- size_t E = S.find('\n', 1);
- if (E == StringRef::npos)
- E = S.size() - 1;
- S = S.substr(E + 1);
+ if (s.startswith("#")) {
+ size_t e = s.find('\n', 1);
+ if (e == StringRef::npos)
+ e = s.size() - 1;
+ s = s.substr(e + 1);
continue;
}
- size_t Size = S.size();
- S = S.ltrim();
- if (S.size() == Size)
- return S;
+ size_t size = s.size();
+ s = s.ltrim();
+ if (s.size() == size)
+ return s;
}
}
// An erroneous token is handled as if it were the last token before EOF.
-bool ScriptLexer::atEOF() { return errorCount() || Tokens.size() == Pos; }
+bool ScriptLexer::atEOF() { return errorCount() || tokens.size() == pos; }
// Split a given string as an expression.
// This function returns "3", "*" and "5" for "3*5" for example.
-static std::vector<StringRef> tokenizeExpr(StringRef S) {
- StringRef Ops = "+-*/:!~"; // List of operators
+static std::vector<StringRef> tokenizeExpr(StringRef s) {
+ StringRef ops = "+-*/:!~=<>"; // List of operators
// Quoted strings are literal strings, so we don't want to split it.
- if (S.startswith("\""))
- return {S};
+ if (s.startswith("\""))
+ return {s};
// Split S with operators as separators.
- std::vector<StringRef> Ret;
- while (!S.empty()) {
- size_t E = S.find_first_of(Ops);
+ std::vector<StringRef> ret;
+ while (!s.empty()) {
+ size_t e = s.find_first_of(ops);
// No need to split if there is no operator.
- if (E == StringRef::npos) {
- Ret.push_back(S);
+ if (e == StringRef::npos) {
+ ret.push_back(s);
break;
}
// Get a token before the opreator.
- if (E != 0)
- Ret.push_back(S.substr(0, E));
-
- // Get the operator as a token. Keep != as one token.
- if (S.substr(E).startswith("!=")) {
- Ret.push_back(S.substr(E, 2));
- S = S.substr(E + 2);
+ if (e != 0)
+ ret.push_back(s.substr(0, e));
+
+ // Get the operator as a token.
+ // Keep !=, ==, >=, <=, << and >> operators as a single tokens.
+ if (s.substr(e).startswith("!=") || s.substr(e).startswith("==") ||
+ s.substr(e).startswith(">=") || s.substr(e).startswith("<=") ||
+ s.substr(e).startswith("<<") || s.substr(e).startswith(">>")) {
+ ret.push_back(s.substr(e, 2));
+ s = s.substr(e + 2);
} else {
- Ret.push_back(S.substr(E, 1));
- S = S.substr(E + 1);
+ ret.push_back(s.substr(e, 1));
+ s = s.substr(e + 1);
}
}
- return Ret;
+ return ret;
}
// In contexts where expressions are expected, the lexer should apply
@@ -214,14 +216,14 @@ static std::vector<StringRef> tokenizeExpr(StringRef S) {
//
// This function may split the current token into multiple tokens.
void ScriptLexer::maybeSplitExpr() {
- if (!InExpr || errorCount() || atEOF())
+ if (!inExpr || errorCount() || atEOF())
return;
- std::vector<StringRef> V = tokenizeExpr(Tokens[Pos]);
- if (V.size() == 1)
+ std::vector<StringRef> v = tokenizeExpr(tokens[pos]);
+ if (v.size() == 1)
return;
- Tokens.erase(Tokens.begin() + Pos);
- Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
+ tokens.erase(tokens.begin() + pos);
+ tokens.insert(tokens.begin() + pos, v.begin(), v.end());
}
StringRef ScriptLexer::next() {
@@ -233,28 +235,28 @@ StringRef ScriptLexer::next() {
setError("unexpected EOF");
return "";
}
- return Tokens[Pos++];
+ return tokens[pos++];
}
StringRef ScriptLexer::peek() {
- StringRef Tok = next();
+ StringRef tok = next();
if (errorCount())
return "";
- Pos = Pos - 1;
- return Tok;
+ pos = pos - 1;
+ return tok;
}
StringRef ScriptLexer::peek2() {
skip();
- StringRef Tok = next();
+ StringRef tok = next();
if (errorCount())
return "";
- Pos = Pos - 2;
- return Tok;
+ pos = pos - 2;
+ return tok;
}
-bool ScriptLexer::consume(StringRef Tok) {
- if (peek() == Tok) {
+bool ScriptLexer::consume(StringRef tok) {
+ if (peek() == tok) {
skip();
return true;
}
@@ -262,12 +264,12 @@ bool ScriptLexer::consume(StringRef Tok) {
}
// Consumes Tok followed by ":". Space is allowed between Tok and ":".
-bool ScriptLexer::consumeLabel(StringRef Tok) {
- if (consume((Tok + ":").str()))
+bool ScriptLexer::consumeLabel(StringRef tok) {
+ if (consume((tok + ":").str()))
return true;
- if (Tokens.size() >= Pos + 2 && Tokens[Pos] == Tok &&
- Tokens[Pos + 1] == ":") {
- Pos += 2;
+ if (tokens.size() >= pos + 2 && tokens[pos] == tok &&
+ tokens[pos + 1] == ":") {
+ pos += 2;
return true;
}
return false;
@@ -275,24 +277,24 @@ bool ScriptLexer::consumeLabel(StringRef Tok) {
void ScriptLexer::skip() { (void)next(); }
-void ScriptLexer::expect(StringRef Expect) {
+void ScriptLexer::expect(StringRef expect) {
if (errorCount())
return;
- StringRef Tok = next();
- if (Tok != Expect)
- setError(Expect + " expected, but got " + Tok);
+ StringRef tok = next();
+ if (tok != expect)
+ setError(expect + " expected, but got " + tok);
}
// Returns true if S encloses T.
-static bool encloses(StringRef S, StringRef T) {
- return S.bytes_begin() <= T.bytes_begin() && T.bytes_end() <= S.bytes_end();
+static bool encloses(StringRef s, StringRef t) {
+ return s.bytes_begin() <= t.bytes_begin() && t.bytes_end() <= s.bytes_end();
}
MemoryBufferRef ScriptLexer::getCurrentMB() {
// Find input buffer containing the current token.
- assert(!MBs.empty() && Pos > 0);
- for (MemoryBufferRef MB : MBs)
- if (encloses(MB.getBuffer(), Tokens[Pos - 1]))
- return MB;
+ assert(!mbs.empty() && pos > 0);
+ for (MemoryBufferRef mb : mbs)
+ if (encloses(mb.getBuffer(), tokens[pos - 1]))
+ return mb;
llvm_unreachable("getCurrentMB: failed to find a token");
}
diff --git a/ELF/ScriptLexer.h b/ELF/ScriptLexer.h
index fc6b5b1008a72..98e4cac95a73e 100644
--- a/ELF/ScriptLexer.h
+++ b/ELF/ScriptLexer.h
@@ -1,9 +1,8 @@
//===- ScriptLexer.h --------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -21,25 +20,25 @@ namespace elf {
class ScriptLexer {
public:
- explicit ScriptLexer(MemoryBufferRef MB);
+ explicit ScriptLexer(MemoryBufferRef mb);
- void setError(const Twine &Msg);
- void tokenize(MemoryBufferRef MB);
- static StringRef skipSpace(StringRef S);
+ void setError(const Twine &msg);
+ void tokenize(MemoryBufferRef mb);
+ static StringRef skipSpace(StringRef s);
bool atEOF();
StringRef next();
StringRef peek();
StringRef peek2();
void skip();
- bool consume(StringRef Tok);
- void expect(StringRef Expect);
- bool consumeLabel(StringRef Tok);
+ bool consume(StringRef tok);
+ void expect(StringRef expect);
+ bool consumeLabel(StringRef tok);
std::string getCurrentLocation();
- std::vector<MemoryBufferRef> MBs;
- std::vector<StringRef> Tokens;
- bool InExpr = false;
- size_t Pos = 0;
+ std::vector<MemoryBufferRef> mbs;
+ std::vector<StringRef> tokens;
+ bool inExpr = false;
+ size_t pos = 0;
private:
void maybeSplitExpr();
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index eee3f0e330cc3..8f0aa660145a4 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -1,9 +1,8 @@
//===- ScriptParser.cpp ---------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -41,22 +40,29 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-static bool isUnderSysroot(StringRef Path);
-
namespace {
class ScriptParser final : ScriptLexer {
public:
- ScriptParser(MemoryBufferRef MB)
- : ScriptLexer(MB),
- IsUnderSysroot(isUnderSysroot(MB.getBufferIdentifier())) {}
+ ScriptParser(MemoryBufferRef mb) : ScriptLexer(mb) {
+ // Initialize IsUnderSysroot
+ if (config->sysroot == "")
+ return;
+ StringRef path = mb.getBufferIdentifier();
+ for (; !path.empty(); path = sys::path::parent_path(path)) {
+ if (!sys::fs::equivalent(config->sysroot, path))
+ continue;
+ isUnderSysroot = true;
+ return;
+ }
+ }
void readLinkerScript();
void readVersionScript();
void readDynamicList();
- void readDefsym(StringRef Name);
+ void readDefsym(StringRef name);
private:
- void addFile(StringRef Path);
+ void addFile(StringRef path);
void readAsNeeded();
void readEntry();
@@ -76,25 +82,23 @@ private:
void readVersion();
void readVersionScriptCommand();
- SymbolAssignment *readSymbolAssignment(StringRef Name);
- ByteCommand *readByteCommand(StringRef Tok);
+ SymbolAssignment *readSymbolAssignment(StringRef name);
+ ByteCommand *readByteCommand(StringRef tok);
std::array<uint8_t, 4> readFill();
- std::array<uint8_t, 4> parseFill(StringRef Tok);
- bool readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2);
- void readSectionAddressType(OutputSection *Cmd);
+ bool readSectionDirective(OutputSection *cmd, StringRef tok1, StringRef tok2);
+ void readSectionAddressType(OutputSection *cmd);
OutputSection *readOverlaySectionDescription();
- OutputSection *readOutputSectionDescription(StringRef OutSec);
+ OutputSection *readOutputSectionDescription(StringRef outSec);
std::vector<BaseCommand *> readOverlay();
std::vector<StringRef> readOutputSectionPhdrs();
- InputSectionDescription *readInputSectionDescription(StringRef Tok);
+ InputSectionDescription *readInputSectionDescription(StringRef tok);
StringMatcher readFilePatterns();
std::vector<SectionPattern> readInputSectionsList();
- InputSectionDescription *readInputSectionRules(StringRef FilePattern);
+ InputSectionDescription *readInputSectionRules(StringRef filePattern);
unsigned readPhdrType();
SortSectionPolicy readSortKind();
- SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
- SymbolAssignment *readAssignment(StringRef Tok);
- std::tuple<ELFKind, uint16_t, bool> readBfdName();
+ SymbolAssignment *readProvideHidden(bool provide, bool hidden);
+ SymbolAssignment *readAssignment(StringRef tok);
void readSort();
Expr readAssert();
Expr readConstant();
@@ -103,97 +107,88 @@ private:
uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
std::pair<uint32_t, uint32_t> readMemoryAttributes();
- Expr combine(StringRef Op, Expr L, Expr R);
+ Expr combine(StringRef op, Expr l, Expr r);
Expr readExpr();
- Expr readExpr1(Expr Lhs, int MinPrec);
+ Expr readExpr1(Expr lhs, int minPrec);
StringRef readParenLiteral();
Expr readPrimary();
- Expr readTernary(Expr Cond);
+ Expr readTernary(Expr cond);
Expr readParenExpr();
// For parsing version script.
std::vector<SymbolVersion> readVersionExtern();
void readAnonymousDeclaration();
- void readVersionDeclaration(StringRef VerStr);
+ void readVersionDeclaration(StringRef verStr);
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
readSymbols();
// True if a script being read is in a subdirectory specified by -sysroot.
- bool IsUnderSysroot;
+ bool isUnderSysroot = false;
// A set to detect an INCLUDE() cycle.
- StringSet<> Seen;
+ StringSet<> seen;
};
} // namespace
-static StringRef unquote(StringRef S) {
- if (S.startswith("\""))
- return S.substr(1, S.size() - 2);
- return S;
-}
-
-static bool isUnderSysroot(StringRef Path) {
- if (Config->Sysroot == "")
- return false;
- for (; !Path.empty(); Path = sys::path::parent_path(Path))
- if (sys::fs::equivalent(Config->Sysroot, Path))
- return true;
- return false;
+static StringRef unquote(StringRef s) {
+ if (s.startswith("\""))
+ return s.substr(1, s.size() - 2);
+ return s;
}
// Some operations only support one non absolute value. Move the
// absolute one to the right hand side for convenience.
-static void moveAbsRight(ExprValue &A, ExprValue &B) {
- if (A.Sec == nullptr || (A.ForceAbsolute && !B.isAbsolute()))
- std::swap(A, B);
- if (!B.isAbsolute())
- error(A.Loc + ": at least one side of the expression must be absolute");
+static void moveAbsRight(ExprValue &a, ExprValue &b) {
+ if (a.sec == nullptr || (a.forceAbsolute && !b.isAbsolute()))
+ std::swap(a, b);
+ if (!b.isAbsolute())
+ error(a.loc + ": at least one side of the expression must be absolute");
}
-static ExprValue add(ExprValue A, ExprValue B) {
- moveAbsRight(A, B);
- return {A.Sec, A.ForceAbsolute, A.getSectionOffset() + B.getValue(), A.Loc};
+static ExprValue add(ExprValue a, ExprValue b) {
+ moveAbsRight(a, b);
+ return {a.sec, a.forceAbsolute, a.getSectionOffset() + b.getValue(), a.loc};
}
-static ExprValue sub(ExprValue A, ExprValue B) {
+static ExprValue sub(ExprValue a, ExprValue b) {
// The distance between two symbols in sections is absolute.
- if (!A.isAbsolute() && !B.isAbsolute())
- return A.getValue() - B.getValue();
- return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc};
+ if (!a.isAbsolute() && !b.isAbsolute())
+ return a.getValue() - b.getValue();
+ return {a.sec, false, a.getSectionOffset() - b.getValue(), a.loc};
}
-static ExprValue bitAnd(ExprValue A, ExprValue B) {
- moveAbsRight(A, B);
- return {A.Sec, A.ForceAbsolute,
- (A.getValue() & B.getValue()) - A.getSecAddr(), A.Loc};
+static ExprValue bitAnd(ExprValue a, ExprValue b) {
+ moveAbsRight(a, b);
+ return {a.sec, a.forceAbsolute,
+ (a.getValue() & b.getValue()) - a.getSecAddr(), a.loc};
}
-static ExprValue bitOr(ExprValue A, ExprValue B) {
- moveAbsRight(A, B);
- return {A.Sec, A.ForceAbsolute,
- (A.getValue() | B.getValue()) - A.getSecAddr(), A.Loc};
+static ExprValue bitOr(ExprValue a, ExprValue b) {
+ moveAbsRight(a, b);
+ return {a.sec, a.forceAbsolute,
+ (a.getValue() | b.getValue()) - a.getSecAddr(), a.loc};
}
void ScriptParser::readDynamicList() {
- Config->HasDynamicList = true;
+ config->hasDynamicList = true;
expect("{");
- std::vector<SymbolVersion> Locals;
- std::vector<SymbolVersion> Globals;
- std::tie(Locals, Globals) = readSymbols();
+ std::vector<SymbolVersion> locals;
+ std::vector<SymbolVersion> globals;
+ std::tie(locals, globals) = readSymbols();
expect(";");
if (!atEOF()) {
setError("EOF expected, but got " + next());
return;
}
- if (!Locals.empty()) {
+ if (!locals.empty()) {
setError("\"local:\" scope not supported in --dynamic-list");
return;
}
- for (SymbolVersion V : Globals)
- Config->DynamicList.push_back(V);
+ for (SymbolVersion v : globals)
+ config->dynamicList.push_back(v);
}
void ScriptParser::readVersionScript() {
@@ -209,14 +204,14 @@ void ScriptParser::readVersionScriptCommand() {
}
while (!atEOF() && !errorCount() && peek() != "}") {
- StringRef VerStr = next();
- if (VerStr == "{") {
+ StringRef verStr = next();
+ if (verStr == "{") {
setError("anonymous version definition is used in "
"combination with other version definitions");
return;
}
expect("{");
- readVersionDeclaration(VerStr);
+ readVersionDeclaration(verStr);
}
}
@@ -228,135 +223,135 @@ void ScriptParser::readVersion() {
void ScriptParser::readLinkerScript() {
while (!atEOF()) {
- StringRef Tok = next();
- if (Tok == ";")
+ StringRef tok = next();
+ if (tok == ";")
continue;
- if (Tok == "ENTRY") {
+ if (tok == "ENTRY") {
readEntry();
- } else if (Tok == "EXTERN") {
+ } else if (tok == "EXTERN") {
readExtern();
- } else if (Tok == "GROUP") {
+ } else if (tok == "GROUP") {
readGroup();
- } else if (Tok == "INCLUDE") {
+ } else if (tok == "INCLUDE") {
readInclude();
- } else if (Tok == "INPUT") {
+ } else if (tok == "INPUT") {
readInput();
- } else if (Tok == "MEMORY") {
+ } else if (tok == "MEMORY") {
readMemory();
- } else if (Tok == "OUTPUT") {
+ } else if (tok == "OUTPUT") {
readOutput();
- } else if (Tok == "OUTPUT_ARCH") {
+ } else if (tok == "OUTPUT_ARCH") {
readOutputArch();
- } else if (Tok == "OUTPUT_FORMAT") {
+ } else if (tok == "OUTPUT_FORMAT") {
readOutputFormat();
- } else if (Tok == "PHDRS") {
+ } else if (tok == "PHDRS") {
readPhdrs();
- } else if (Tok == "REGION_ALIAS") {
+ } else if (tok == "REGION_ALIAS") {
readRegionAlias();
- } else if (Tok == "SEARCH_DIR") {
+ } else if (tok == "SEARCH_DIR") {
readSearchDir();
- } else if (Tok == "SECTIONS") {
+ } else if (tok == "SECTIONS") {
readSections();
- } else if (Tok == "TARGET") {
+ } else if (tok == "TARGET") {
readTarget();
- } else if (Tok == "VERSION") {
+ } else if (tok == "VERSION") {
readVersion();
- } else if (SymbolAssignment *Cmd = readAssignment(Tok)) {
- Script->SectionCommands.push_back(Cmd);
+ } else if (SymbolAssignment *cmd = readAssignment(tok)) {
+ script->sectionCommands.push_back(cmd);
} else {
- setError("unknown directive: " + Tok);
+ setError("unknown directive: " + tok);
}
}
}
-void ScriptParser::readDefsym(StringRef Name) {
+void ScriptParser::readDefsym(StringRef name) {
if (errorCount())
return;
- Expr E = readExpr();
+ Expr e = readExpr();
if (!atEOF())
setError("EOF expected, but got " + next());
- SymbolAssignment *Cmd = make<SymbolAssignment>(Name, E, getCurrentLocation());
- Script->SectionCommands.push_back(Cmd);
+ SymbolAssignment *cmd = make<SymbolAssignment>(name, e, getCurrentLocation());
+ script->sectionCommands.push_back(cmd);
}
-void ScriptParser::addFile(StringRef S) {
- if (IsUnderSysroot && S.startswith("/")) {
- SmallString<128> PathData;
- StringRef Path = (Config->Sysroot + S).toStringRef(PathData);
- if (sys::fs::exists(Path)) {
- Driver->addFile(Saver.save(Path), /*WithLOption=*/false);
+void ScriptParser::addFile(StringRef s) {
+ if (isUnderSysroot && s.startswith("/")) {
+ SmallString<128> pathData;
+ StringRef path = (config->sysroot + s).toStringRef(pathData);
+ if (sys::fs::exists(path)) {
+ driver->addFile(saver.save(path), /*withLOption=*/false);
return;
}
}
- if (S.startswith("/")) {
- Driver->addFile(S, /*WithLOption=*/false);
- } else if (S.startswith("=")) {
- if (Config->Sysroot.empty())
- Driver->addFile(S.substr(1), /*WithLOption=*/false);
+ if (s.startswith("/")) {
+ driver->addFile(s, /*withLOption=*/false);
+ } else if (s.startswith("=")) {
+ if (config->sysroot.empty())
+ driver->addFile(s.substr(1), /*withLOption=*/false);
else
- Driver->addFile(Saver.save(Config->Sysroot + "/" + S.substr(1)),
- /*WithLOption=*/false);
- } else if (S.startswith("-l")) {
- Driver->addLibrary(S.substr(2));
- } else if (sys::fs::exists(S)) {
- Driver->addFile(S, /*WithLOption=*/false);
+ driver->addFile(saver.save(config->sysroot + "/" + s.substr(1)),
+ /*withLOption=*/false);
+ } else if (s.startswith("-l")) {
+ driver->addLibrary(s.substr(2));
+ } else if (sys::fs::exists(s)) {
+ driver->addFile(s, /*withLOption=*/false);
} else {
- if (Optional<std::string> Path = findFromSearchPaths(S))
- Driver->addFile(Saver.save(*Path), /*WithLOption=*/true);
+ if (Optional<std::string> path = findFromSearchPaths(s))
+ driver->addFile(saver.save(*path), /*withLOption=*/true);
else
- setError("unable to find " + S);
+ setError("unable to find " + s);
}
}
void ScriptParser::readAsNeeded() {
expect("(");
- bool Orig = Config->AsNeeded;
- Config->AsNeeded = true;
+ bool orig = config->asNeeded;
+ config->asNeeded = true;
while (!errorCount() && !consume(")"))
addFile(unquote(next()));
- Config->AsNeeded = Orig;
+ config->asNeeded = orig;
}
void ScriptParser::readEntry() {
// -e <symbol> takes predecence over ENTRY(<symbol>).
expect("(");
- StringRef Tok = next();
- if (Config->Entry.empty())
- Config->Entry = Tok;
+ StringRef tok = next();
+ if (config->entry.empty())
+ config->entry = tok;
expect(")");
}
void ScriptParser::readExtern() {
expect("(");
while (!errorCount() && !consume(")"))
- Config->Undefined.push_back(next());
+ config->undefined.push_back(unquote(next()));
}
void ScriptParser::readGroup() {
- bool Orig = InputFile::IsInGroup;
- InputFile::IsInGroup = true;
+ bool orig = InputFile::isInGroup;
+ InputFile::isInGroup = true;
readInput();
- InputFile::IsInGroup = Orig;
- if (!Orig)
- ++InputFile::NextGroupId;
+ InputFile::isInGroup = orig;
+ if (!orig)
+ ++InputFile::nextGroupId;
}
void ScriptParser::readInclude() {
- StringRef Tok = unquote(next());
+ StringRef tok = unquote(next());
- if (!Seen.insert(Tok).second) {
+ if (!seen.insert(tok).second) {
setError("there is a cycle in linker script INCLUDEs");
return;
}
- if (Optional<std::string> Path = searchScript(Tok)) {
- if (Optional<MemoryBufferRef> MB = readFile(*Path))
- tokenize(*MB);
+ if (Optional<std::string> path = searchScript(tok)) {
+ if (Optional<MemoryBufferRef> mb = readFile(*path))
+ tokenize(*mb);
return;
}
- setError("cannot find linker script " + Tok);
+ setError("cannot find linker script " + tok);
}
void ScriptParser::readInput() {
@@ -372,9 +367,9 @@ void ScriptParser::readInput() {
void ScriptParser::readOutput() {
// -o <file> takes predecence over OUTPUT(<file>).
expect("(");
- StringRef Tok = next();
- if (Config->OutputFile.empty())
- Config->OutputFile = unquote(Tok);
+ StringRef tok = next();
+ if (config->outputFile.empty())
+ config->outputFile = unquote(tok);
expect(")");
}
@@ -385,39 +380,27 @@ void ScriptParser::readOutputArch() {
skip();
}
-std::tuple<ELFKind, uint16_t, bool> ScriptParser::readBfdName() {
- StringRef S = unquote(next());
- if (S == "elf32-i386")
- return std::make_tuple(ELF32LEKind, EM_386, false);
- if (S == "elf32-iamcu")
- return std::make_tuple(ELF32LEKind, EM_IAMCU, false);
- if (S == "elf32-littlearm")
- return std::make_tuple(ELF32LEKind, EM_ARM, false);
- if (S == "elf32-x86-64")
- return std::make_tuple(ELF32LEKind, EM_X86_64, false);
- if (S == "elf64-littleaarch64")
- return std::make_tuple(ELF64LEKind, EM_AARCH64, false);
- if (S == "elf64-powerpc")
- return std::make_tuple(ELF64BEKind, EM_PPC64, false);
- if (S == "elf64-powerpcle")
- return std::make_tuple(ELF64LEKind, EM_PPC64, false);
- if (S == "elf64-x86-64")
- return std::make_tuple(ELF64LEKind, EM_X86_64, false);
- if (S == "elf32-tradbigmips")
- return std::make_tuple(ELF32BEKind, EM_MIPS, false);
- if (S == "elf32-ntradbigmips")
- return std::make_tuple(ELF32BEKind, EM_MIPS, true);
- if (S == "elf32-tradlittlemips")
- return std::make_tuple(ELF32LEKind, EM_MIPS, false);
- if (S == "elf32-ntradlittlemips")
- return std::make_tuple(ELF32LEKind, EM_MIPS, true);
- if (S == "elf64-tradbigmips")
- return std::make_tuple(ELF64BEKind, EM_MIPS, false);
- if (S == "elf64-tradlittlemips")
- return std::make_tuple(ELF64LEKind, EM_MIPS, false);
-
- setError("unknown output format name: " + S);
- return std::make_tuple(ELFNoneKind, EM_NONE, false);
+static std::pair<ELFKind, uint16_t> parseBfdName(StringRef s) {
+ return StringSwitch<std::pair<ELFKind, uint16_t>>(s)
+ .Case("elf32-i386", {ELF32LEKind, EM_386})
+ .Case("elf32-iamcu", {ELF32LEKind, EM_IAMCU})
+ .Case("elf32-littlearm", {ELF32LEKind, EM_ARM})
+ .Case("elf32-x86-64", {ELF32LEKind, EM_X86_64})
+ .Case("elf64-aarch64", {ELF64LEKind, EM_AARCH64})
+ .Case("elf64-littleaarch64", {ELF64LEKind, EM_AARCH64})
+ .Case("elf32-powerpc", {ELF32BEKind, EM_PPC})
+ .Case("elf64-powerpc", {ELF64BEKind, EM_PPC64})
+ .Case("elf64-powerpcle", {ELF64LEKind, EM_PPC64})
+ .Case("elf64-x86-64", {ELF64LEKind, EM_X86_64})
+ .Cases("elf32-tradbigmips", "elf32-bigmips", {ELF32BEKind, EM_MIPS})
+ .Case("elf32-ntradbigmips", {ELF32BEKind, EM_MIPS})
+ .Case("elf32-tradlittlemips", {ELF32LEKind, EM_MIPS})
+ .Case("elf32-ntradlittlemips", {ELF32LEKind, EM_MIPS})
+ .Case("elf64-tradbigmips", {ELF64BEKind, EM_MIPS})
+ .Case("elf64-tradlittlemips", {ELF64LEKind, EM_MIPS})
+ .Case("elf32-littleriscv", {ELF32LEKind, EM_RISCV})
+ .Case("elf64-littleriscv", {ELF64LEKind, EM_RISCV})
+ .Default({ELFNoneKind, EM_NONE});
}
// Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(bfdname, big, little).
@@ -425,9 +408,16 @@ std::tuple<ELFKind, uint16_t, bool> ScriptParser::readBfdName() {
void ScriptParser::readOutputFormat() {
expect("(");
- std::tuple<ELFKind, uint16_t, bool> BfdTuple = readBfdName();
- if (Config->EKind == ELFNoneKind)
- std::tie(Config->EKind, Config->EMachine, Config->MipsN32Abi) = BfdTuple;
+ StringRef name = unquote(next());
+ StringRef s = name;
+ if (s.consume_back("-freebsd"))
+ config->osabi = ELFOSABI_FREEBSD;
+
+ std::tie(config->ekind, config->emachine) = parseBfdName(s);
+ if (config->emachine == EM_NONE)
+ setError("unknown output format name: " + name);
+ if (s == "elf32-ntradlittlemips" || s == "elf32-ntradbigmips")
+ config->mipsN32Abi = true;
if (consume(")"))
return;
@@ -442,46 +432,46 @@ void ScriptParser::readPhdrs() {
expect("{");
while (!errorCount() && !consume("}")) {
- PhdrsCommand Cmd;
- Cmd.Name = next();
- Cmd.Type = readPhdrType();
+ PhdrsCommand cmd;
+ cmd.name = next();
+ cmd.type = readPhdrType();
while (!errorCount() && !consume(";")) {
if (consume("FILEHDR"))
- Cmd.HasFilehdr = true;
+ cmd.hasFilehdr = true;
else if (consume("PHDRS"))
- Cmd.HasPhdrs = true;
+ cmd.hasPhdrs = true;
else if (consume("AT"))
- Cmd.LMAExpr = readParenExpr();
+ cmd.lmaExpr = readParenExpr();
else if (consume("FLAGS"))
- Cmd.Flags = readParenExpr()().getValue();
+ cmd.flags = readParenExpr()().getValue();
else
setError("unexpected header attribute: " + next());
}
- Script->PhdrsCommands.push_back(Cmd);
+ script->phdrsCommands.push_back(cmd);
}
}
void ScriptParser::readRegionAlias() {
expect("(");
- StringRef Alias = unquote(next());
+ StringRef alias = unquote(next());
expect(",");
- StringRef Name = next();
+ StringRef name = next();
expect(")");
- if (Script->MemoryRegions.count(Alias))
- setError("redefinition of memory region '" + Alias + "'");
- if (!Script->MemoryRegions.count(Name))
- setError("memory region '" + Name + "' is not defined");
- Script->MemoryRegions.insert({Alias, Script->MemoryRegions[Name]});
+ if (script->memoryRegions.count(alias))
+ setError("redefinition of memory region '" + alias + "'");
+ if (!script->memoryRegions.count(name))
+ setError("memory region '" + name + "' is not defined");
+ script->memoryRegions.insert({alias, script->memoryRegions[name]});
}
void ScriptParser::readSearchDir() {
expect("(");
- StringRef Tok = next();
- if (!Config->Nostdlib)
- Config->SearchPaths.push_back(unquote(Tok));
+ StringRef tok = next();
+ if (!config->nostdlib)
+ config->searchPaths.push_back(unquote(tok));
expect(")");
}
@@ -493,83 +483,83 @@ std::vector<BaseCommand *> ScriptParser::readOverlay() {
// VA and LMA expressions are optional, though for simplicity of
// implementation we assume they are not. That is what OVERLAY was designed
// for first of all: to allow sections with overlapping VAs at different LMAs.
- Expr AddrExpr = readExpr();
+ Expr addrExpr = readExpr();
expect(":");
expect("AT");
- Expr LMAExpr = readParenExpr();
+ Expr lmaExpr = readParenExpr();
expect("{");
- std::vector<BaseCommand *> V;
- OutputSection *Prev = nullptr;
+ std::vector<BaseCommand *> v;
+ OutputSection *prev = nullptr;
while (!errorCount() && !consume("}")) {
// VA is the same for all sections. The LMAs are consecutive in memory
// starting from the base load address specified.
- OutputSection *OS = readOverlaySectionDescription();
- OS->AddrExpr = AddrExpr;
- if (Prev)
- OS->LMAExpr = [=] { return Prev->getLMA() + Prev->Size; };
+ OutputSection *os = readOverlaySectionDescription();
+ os->addrExpr = addrExpr;
+ if (prev)
+ os->lmaExpr = [=] { return prev->getLMA() + prev->size; };
else
- OS->LMAExpr = LMAExpr;
- V.push_back(OS);
- Prev = OS;
+ os->lmaExpr = lmaExpr;
+ v.push_back(os);
+ prev = os;
}
// According to the specification, at the end of the overlay, the location
// counter should be equal to the overlay base address plus size of the
// largest section seen in the overlay.
// Here we want to create the Dot assignment command to achieve that.
- Expr MoveDot = [=] {
- uint64_t Max = 0;
- for (BaseCommand *Cmd : V)
- Max = std::max(Max, cast<OutputSection>(Cmd)->Size);
- return AddrExpr().getValue() + Max;
+ Expr moveDot = [=] {
+ uint64_t max = 0;
+ for (BaseCommand *cmd : v)
+ max = std::max(max, cast<OutputSection>(cmd)->size);
+ return addrExpr().getValue() + max;
};
- V.push_back(make<SymbolAssignment>(".", MoveDot, getCurrentLocation()));
- return V;
+ v.push_back(make<SymbolAssignment>(".", moveDot, getCurrentLocation()));
+ return v;
}
void ScriptParser::readSections() {
- Script->HasSectionsCommand = true;
+ script->hasSectionsCommand = true;
// -no-rosegment is used to avoid placing read only non-executable sections in
// their own segment. We do the same if SECTIONS command is present in linker
// script. See comment for computeFlags().
- Config->SingleRoRx = true;
+ config->singleRoRx = true;
expect("{");
- std::vector<BaseCommand *> V;
+ std::vector<BaseCommand *> v;
while (!errorCount() && !consume("}")) {
- StringRef Tok = next();
- if (Tok == "OVERLAY") {
- for (BaseCommand *Cmd : readOverlay())
- V.push_back(Cmd);
+ StringRef tok = next();
+ if (tok == "OVERLAY") {
+ for (BaseCommand *cmd : readOverlay())
+ v.push_back(cmd);
continue;
- } else if (Tok == "INCLUDE") {
+ } else if (tok == "INCLUDE") {
readInclude();
continue;
}
- if (BaseCommand *Cmd = readAssignment(Tok))
- V.push_back(Cmd);
+ if (BaseCommand *cmd = readAssignment(tok))
+ v.push_back(cmd);
else
- V.push_back(readOutputSectionDescription(Tok));
+ v.push_back(readOutputSectionDescription(tok));
}
if (!atEOF() && consume("INSERT")) {
- std::vector<BaseCommand *> *Dest = nullptr;
+ std::vector<BaseCommand *> *dest = nullptr;
if (consume("AFTER"))
- Dest = &Script->InsertAfterCommands[next()];
+ dest = &script->insertAfterCommands[next()];
else if (consume("BEFORE"))
- Dest = &Script->InsertBeforeCommands[next()];
+ dest = &script->insertBeforeCommands[next()];
else
setError("expected AFTER/BEFORE, but got '" + next() + "'");
- if (Dest)
- Dest->insert(Dest->end(), V.begin(), V.end());
+ if (dest)
+ dest->insert(dest->end(), v.begin(), v.end());
return;
}
- Script->SectionCommands.insert(Script->SectionCommands.end(), V.begin(),
- V.end());
+ script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
+ v.end());
}
void ScriptParser::readTarget() {
@@ -578,19 +568,19 @@ void ScriptParser::readTarget() {
// for --format. We recognize only /^elf/ and "binary" in the linker
// script as well.
expect("(");
- StringRef Tok = next();
+ StringRef tok = next();
expect(")");
- if (Tok.startswith("elf"))
- Config->FormatBinary = false;
- else if (Tok == "binary")
- Config->FormatBinary = true;
+ if (tok.startswith("elf"))
+ config->formatBinary = false;
+ else if (tok == "binary")
+ config->formatBinary = true;
else
- setError("unknown target: " + Tok);
+ setError("unknown target: " + tok);
}
-static int precedence(StringRef Op) {
- return StringSwitch<int>(Op)
+static int precedence(StringRef op) {
+ return StringSwitch<int>(op)
.Cases("*", "/", "%", 8)
.Cases("+", "-", 7)
.Cases("<<", ">>", 6)
@@ -603,10 +593,10 @@ static int precedence(StringRef Op) {
}
StringMatcher ScriptParser::readFilePatterns() {
- std::vector<StringRef> V;
+ std::vector<StringRef> v;
while (!errorCount() && !consume(")"))
- V.push_back(next());
- return StringMatcher(V);
+ v.push_back(next());
+ return StringMatcher(v);
}
SortSectionPolicy ScriptParser::readSortKind() {
@@ -635,24 +625,24 @@ SortSectionPolicy ScriptParser::readSortKind() {
// The semantics of that is section .foo in any file, section .bar in
// any file but a.o, and section .baz in any file but b.o.
std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
- std::vector<SectionPattern> Ret;
+ std::vector<SectionPattern> ret;
while (!errorCount() && peek() != ")") {
- StringMatcher ExcludeFilePat;
+ StringMatcher excludeFilePat;
if (consume("EXCLUDE_FILE")) {
expect("(");
- ExcludeFilePat = readFilePatterns();
+ excludeFilePat = readFilePatterns();
}
- std::vector<StringRef> V;
+ std::vector<StringRef> v;
while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE")
- V.push_back(next());
+ v.push_back(unquote(next()));
- if (!V.empty())
- Ret.push_back({std::move(ExcludeFilePat), StringMatcher(V)});
+ if (!v.empty())
+ ret.push_back({std::move(excludeFilePat), StringMatcher(v)});
else
setError("section pattern is expected");
}
- return Ret;
+ return ret;
}
// Reads contents of "SECTIONS" directive. That directive contains a
@@ -667,52 +657,52 @@ std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
//
// <section-list> is parsed by readInputSectionsList().
InputSectionDescription *
-ScriptParser::readInputSectionRules(StringRef FilePattern) {
- auto *Cmd = make<InputSectionDescription>(FilePattern);
+ScriptParser::readInputSectionRules(StringRef filePattern) {
+ auto *cmd = make<InputSectionDescription>(filePattern);
expect("(");
while (!errorCount() && !consume(")")) {
- SortSectionPolicy Outer = readSortKind();
- SortSectionPolicy Inner = SortSectionPolicy::Default;
- std::vector<SectionPattern> V;
- if (Outer != SortSectionPolicy::Default) {
+ SortSectionPolicy outer = readSortKind();
+ SortSectionPolicy inner = SortSectionPolicy::Default;
+ std::vector<SectionPattern> v;
+ if (outer != SortSectionPolicy::Default) {
expect("(");
- Inner = readSortKind();
- if (Inner != SortSectionPolicy::Default) {
+ inner = readSortKind();
+ if (inner != SortSectionPolicy::Default) {
expect("(");
- V = readInputSectionsList();
+ v = readInputSectionsList();
expect(")");
} else {
- V = readInputSectionsList();
+ v = readInputSectionsList();
}
expect(")");
} else {
- V = readInputSectionsList();
+ v = readInputSectionsList();
}
- for (SectionPattern &Pat : V) {
- Pat.SortInner = Inner;
- Pat.SortOuter = Outer;
+ for (SectionPattern &pat : v) {
+ pat.sortInner = inner;
+ pat.sortOuter = outer;
}
- std::move(V.begin(), V.end(), std::back_inserter(Cmd->SectionPatterns));
+ std::move(v.begin(), v.end(), std::back_inserter(cmd->sectionPatterns));
}
- return Cmd;
+ return cmd;
}
InputSectionDescription *
-ScriptParser::readInputSectionDescription(StringRef Tok) {
+ScriptParser::readInputSectionDescription(StringRef tok) {
// Input section wildcard can be surrounded by KEEP.
// https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep
- if (Tok == "KEEP") {
+ if (tok == "KEEP") {
expect("(");
- StringRef FilePattern = next();
- InputSectionDescription *Cmd = readInputSectionRules(FilePattern);
+ StringRef filePattern = next();
+ InputSectionDescription *cmd = readInputSectionRules(filePattern);
expect(")");
- Script->KeptSections.push_back(Cmd);
- return Cmd;
+ script->keptSections.push_back(cmd);
+ return cmd;
}
- return readInputSectionRules(Tok);
+ return readInputSectionRules(tok);
}
void ScriptParser::readSort() {
@@ -723,44 +713,33 @@ void ScriptParser::readSort() {
Expr ScriptParser::readAssert() {
expect("(");
- Expr E = readExpr();
+ Expr e = readExpr();
expect(",");
- StringRef Msg = unquote(next());
+ StringRef msg = unquote(next());
expect(")");
return [=] {
- if (!E().getValue())
- error(Msg);
- return Script->getDot();
+ if (!e().getValue())
+ error(msg);
+ return script->getDot();
};
}
-// Reads a FILL(expr) command. We handle the FILL command as an
-// alias for =fillexp section attribute, which is different from
-// what GNU linkers do.
-// https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
-std::array<uint8_t, 4> ScriptParser::readFill() {
- expect("(");
- std::array<uint8_t, 4> V = parseFill(next());
- expect(")");
- return V;
-}
-
// Tries to read the special directive for an output section definition which
// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)" or "(OVERLAY)".
// Tok1 and Tok2 are next 2 tokens peeked. See comment for readSectionAddressType below.
-bool ScriptParser::readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2) {
- if (Tok1 != "(")
+bool ScriptParser::readSectionDirective(OutputSection *cmd, StringRef tok1, StringRef tok2) {
+ if (tok1 != "(")
return false;
- if (Tok2 != "NOLOAD" && Tok2 != "COPY" && Tok2 != "INFO" && Tok2 != "OVERLAY")
+ if (tok2 != "NOLOAD" && tok2 != "COPY" && tok2 != "INFO" && tok2 != "OVERLAY")
return false;
expect("(");
if (consume("NOLOAD")) {
- Cmd->Noload = true;
+ cmd->noload = true;
} else {
skip(); // This is "COPY", "INFO" or "OVERLAY".
- Cmd->NonAlloc = true;
+ cmd->nonAlloc = true;
}
expect(")");
return true;
@@ -777,178 +756,186 @@ bool ScriptParser::readSectionDirective(OutputSection *Cmd, StringRef Tok1, Stri
//
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
-void ScriptParser::readSectionAddressType(OutputSection *Cmd) {
- if (readSectionDirective(Cmd, peek(), peek2()))
+void ScriptParser::readSectionAddressType(OutputSection *cmd) {
+ if (readSectionDirective(cmd, peek(), peek2()))
return;
- Cmd->AddrExpr = readExpr();
- if (peek() == "(" && !readSectionDirective(Cmd, "(", peek2()))
+ cmd->addrExpr = readExpr();
+ if (peek() == "(" && !readSectionDirective(cmd, "(", peek2()))
setError("unknown section directive: " + peek2());
}
-static Expr checkAlignment(Expr E, std::string &Loc) {
+static Expr checkAlignment(Expr e, std::string &loc) {
return [=] {
- uint64_t Alignment = std::max((uint64_t)1, E().getValue());
- if (!isPowerOf2_64(Alignment)) {
- error(Loc + ": alignment must be power of 2");
+ uint64_t alignment = std::max((uint64_t)1, e().getValue());
+ if (!isPowerOf2_64(alignment)) {
+ error(loc + ": alignment must be power of 2");
return (uint64_t)1; // Return a dummy value.
}
- return Alignment;
+ return alignment;
};
}
OutputSection *ScriptParser::readOverlaySectionDescription() {
- OutputSection *Cmd =
- Script->createOutputSection(next(), getCurrentLocation());
- Cmd->InOverlay = true;
+ OutputSection *cmd =
+ script->createOutputSection(next(), getCurrentLocation());
+ cmd->inOverlay = true;
expect("{");
while (!errorCount() && !consume("}"))
- Cmd->SectionCommands.push_back(readInputSectionRules(next()));
- Cmd->Phdrs = readOutputSectionPhdrs();
- return Cmd;
+ cmd->sectionCommands.push_back(readInputSectionRules(next()));
+ cmd->phdrs = readOutputSectionPhdrs();
+ return cmd;
}
-OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
- OutputSection *Cmd =
- Script->createOutputSection(OutSec, getCurrentLocation());
+OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) {
+ OutputSection *cmd =
+ script->createOutputSection(outSec, getCurrentLocation());
- size_t SymbolsReferenced = Script->ReferencedSymbols.size();
+ size_t symbolsReferenced = script->referencedSymbols.size();
if (peek() != ":")
- readSectionAddressType(Cmd);
+ readSectionAddressType(cmd);
expect(":");
- std::string Location = getCurrentLocation();
+ std::string location = getCurrentLocation();
if (consume("AT"))
- Cmd->LMAExpr = readParenExpr();
+ cmd->lmaExpr = readParenExpr();
if (consume("ALIGN"))
- Cmd->AlignExpr = checkAlignment(readParenExpr(), Location);
+ cmd->alignExpr = checkAlignment(readParenExpr(), location);
if (consume("SUBALIGN"))
- Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location);
+ cmd->subalignExpr = checkAlignment(readParenExpr(), location);
// Parse constraints.
if (consume("ONLY_IF_RO"))
- Cmd->Constraint = ConstraintKind::ReadOnly;
+ cmd->constraint = ConstraintKind::ReadOnly;
if (consume("ONLY_IF_RW"))
- Cmd->Constraint = ConstraintKind::ReadWrite;
+ cmd->constraint = ConstraintKind::ReadWrite;
expect("{");
while (!errorCount() && !consume("}")) {
- StringRef Tok = next();
- if (Tok == ";") {
+ StringRef tok = next();
+ if (tok == ";") {
// Empty commands are allowed. Do nothing here.
- } else if (SymbolAssignment *Assign = readAssignment(Tok)) {
- Cmd->SectionCommands.push_back(Assign);
- } else if (ByteCommand *Data = readByteCommand(Tok)) {
- Cmd->SectionCommands.push_back(Data);
- } else if (Tok == "CONSTRUCTORS") {
+ } else if (SymbolAssignment *assign = readAssignment(tok)) {
+ cmd->sectionCommands.push_back(assign);
+ } else if (ByteCommand *data = readByteCommand(tok)) {
+ cmd->sectionCommands.push_back(data);
+ } else if (tok == "CONSTRUCTORS") {
// CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
// by name. This is for very old file formats such as ECOFF/XCOFF.
// For ELF, we should ignore.
- } else if (Tok == "FILL") {
- Cmd->Filler = readFill();
- } else if (Tok == "SORT") {
+ } else if (tok == "FILL") {
+ // We handle the FILL command as an alias for =fillexp section attribute,
+ // which is different from what GNU linkers do.
+ // https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
+ expect("(");
+ cmd->filler = readFill();
+ expect(")");
+ } else if (tok == "SORT") {
readSort();
- } else if (Tok == "INCLUDE") {
+ } else if (tok == "INCLUDE") {
readInclude();
} else if (peek() == "(") {
- Cmd->SectionCommands.push_back(readInputSectionDescription(Tok));
+ cmd->sectionCommands.push_back(readInputSectionDescription(tok));
} else {
// We have a file name and no input sections description. It is not a
// commonly used syntax, but still acceptable. In that case, all sections
// from the file will be included.
- auto *ISD = make<InputSectionDescription>(Tok);
- ISD->SectionPatterns.push_back({{}, StringMatcher({"*"})});
- Cmd->SectionCommands.push_back(ISD);
+ auto *isd = make<InputSectionDescription>(tok);
+ isd->sectionPatterns.push_back({{}, StringMatcher({"*"})});
+ cmd->sectionCommands.push_back(isd);
}
}
if (consume(">"))
- Cmd->MemoryRegionName = next();
+ cmd->memoryRegionName = next();
if (consume("AT")) {
expect(">");
- Cmd->LMARegionName = next();
+ cmd->lmaRegionName = next();
}
- if (Cmd->LMAExpr && !Cmd->LMARegionName.empty())
+ if (cmd->lmaExpr && !cmd->lmaRegionName.empty())
error("section can't have both LMA and a load region");
- Cmd->Phdrs = readOutputSectionPhdrs();
+ cmd->phdrs = readOutputSectionPhdrs();
- if (consume("="))
- Cmd->Filler = parseFill(next());
- else if (peek().startswith("="))
- Cmd->Filler = parseFill(next().drop_front());
+ if (peek() == "=" || peek().startswith("=")) {
+ inExpr = true;
+ consume("=");
+ cmd->filler = readFill();
+ inExpr = false;
+ }
// Consume optional comma following output section command.
consume(",");
- if (Script->ReferencedSymbols.size() > SymbolsReferenced)
- Cmd->ExpressionsUseSymbols = true;
- return Cmd;
+ if (script->referencedSymbols.size() > symbolsReferenced)
+ cmd->expressionsUseSymbols = true;
+ return cmd;
}
-// Parses a given string as a octal/decimal/hexadecimal number and
-// returns it as a big-endian number. Used for `=<fillexp>`.
+// Reads a `=<fillexp>` expression and returns its value as a big-endian number.
// https://sourceware.org/binutils/docs/ld/Output-Section-Fill.html
+// We do not support using symbols in such expressions.
//
// When reading a hexstring, ld.bfd handles it as a blob of arbitrary
// size, while ld.gold always handles it as a 32-bit big-endian number.
// We are compatible with ld.gold because it's easier to implement.
-std::array<uint8_t, 4> ScriptParser::parseFill(StringRef Tok) {
- uint32_t V = 0;
- if (!to_integer(Tok, V))
- setError("invalid filler expression: " + Tok);
+std::array<uint8_t, 4> ScriptParser::readFill() {
+ uint64_t value = readExpr()().val;
+ if (value > UINT32_MAX)
+ setError("filler expression result does not fit 32-bit: 0x" +
+ Twine::utohexstr(value));
- std::array<uint8_t, 4> Buf;
- write32be(Buf.data(), V);
- return Buf;
+ std::array<uint8_t, 4> buf;
+ write32be(buf.data(), (uint32_t)value);
+ return buf;
}
-SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) {
+SymbolAssignment *ScriptParser::readProvideHidden(bool provide, bool hidden) {
expect("(");
- SymbolAssignment *Cmd = readSymbolAssignment(next());
- Cmd->Provide = Provide;
- Cmd->Hidden = Hidden;
+ SymbolAssignment *cmd = readSymbolAssignment(next());
+ cmd->provide = provide;
+ cmd->hidden = hidden;
expect(")");
- return Cmd;
+ return cmd;
}
-SymbolAssignment *ScriptParser::readAssignment(StringRef Tok) {
+SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
// Assert expression returns Dot, so this is equal to ".=."
- if (Tok == "ASSERT")
+ if (tok == "ASSERT")
return make<SymbolAssignment>(".", readAssert(), getCurrentLocation());
- size_t OldPos = Pos;
- SymbolAssignment *Cmd = nullptr;
+ size_t oldPos = pos;
+ SymbolAssignment *cmd = nullptr;
if (peek() == "=" || peek() == "+=")
- Cmd = readSymbolAssignment(Tok);
- else if (Tok == "PROVIDE")
- Cmd = readProvideHidden(true, false);
- else if (Tok == "HIDDEN")
- Cmd = readProvideHidden(false, true);
- else if (Tok == "PROVIDE_HIDDEN")
- Cmd = readProvideHidden(true, true);
-
- if (Cmd) {
- Cmd->CommandString =
- Tok.str() + " " +
- llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
+ cmd = readSymbolAssignment(tok);
+ else if (tok == "PROVIDE")
+ cmd = readProvideHidden(true, false);
+ else if (tok == "HIDDEN")
+ cmd = readProvideHidden(false, true);
+ else if (tok == "PROVIDE_HIDDEN")
+ cmd = readProvideHidden(true, true);
+
+ if (cmd) {
+ cmd->commandString =
+ tok.str() + " " +
+ llvm::join(tokens.begin() + oldPos, tokens.begin() + pos, " ");
expect(";");
}
- return Cmd;
+ return cmd;
}
-SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) {
- StringRef Op = next();
- assert(Op == "=" || Op == "+=");
- Expr E = readExpr();
- if (Op == "+=") {
- std::string Loc = getCurrentLocation();
- E = [=] { return add(Script->getSymbolValue(Name, Loc), E()); };
+SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
+ StringRef op = next();
+ assert(op == "=" || op == "+=");
+ Expr e = readExpr();
+ if (op == "+=") {
+ std::string loc = getCurrentLocation();
+ e = [=] { return add(script->getSymbolValue(name, loc), e()); };
}
- return make<SymbolAssignment>(Name, E, getCurrentLocation());
+ return make<SymbolAssignment>(name, e, getCurrentLocation());
}
// This is an operator-precedence parser to parse a linker
@@ -956,178 +943,178 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) {
Expr ScriptParser::readExpr() {
// Our lexer is context-aware. Set the in-expression bit so that
// they apply different tokenization rules.
- bool Orig = InExpr;
- InExpr = true;
- Expr E = readExpr1(readPrimary(), 0);
- InExpr = Orig;
- return E;
-}
-
-Expr ScriptParser::combine(StringRef Op, Expr L, Expr R) {
- if (Op == "+")
- return [=] { return add(L(), R()); };
- if (Op == "-")
- return [=] { return sub(L(), R()); };
- if (Op == "*")
- return [=] { return L().getValue() * R().getValue(); };
- if (Op == "/") {
- std::string Loc = getCurrentLocation();
+ bool orig = inExpr;
+ inExpr = true;
+ Expr e = readExpr1(readPrimary(), 0);
+ inExpr = orig;
+ return e;
+}
+
+Expr ScriptParser::combine(StringRef op, Expr l, Expr r) {
+ if (op == "+")
+ return [=] { return add(l(), r()); };
+ if (op == "-")
+ return [=] { return sub(l(), r()); };
+ if (op == "*")
+ return [=] { return l().getValue() * r().getValue(); };
+ if (op == "/") {
+ std::string loc = getCurrentLocation();
return [=]() -> uint64_t {
- if (uint64_t RV = R().getValue())
- return L().getValue() / RV;
- error(Loc + ": division by zero");
+ if (uint64_t rv = r().getValue())
+ return l().getValue() / rv;
+ error(loc + ": division by zero");
return 0;
};
}
- if (Op == "%") {
- std::string Loc = getCurrentLocation();
+ if (op == "%") {
+ std::string loc = getCurrentLocation();
return [=]() -> uint64_t {
- if (uint64_t RV = R().getValue())
- return L().getValue() % RV;
- error(Loc + ": modulo by zero");
+ if (uint64_t rv = r().getValue())
+ return l().getValue() % rv;
+ error(loc + ": modulo by zero");
return 0;
};
}
- if (Op == "<<")
- return [=] { return L().getValue() << R().getValue(); };
- if (Op == ">>")
- return [=] { return L().getValue() >> R().getValue(); };
- if (Op == "<")
- return [=] { return L().getValue() < R().getValue(); };
- if (Op == ">")
- return [=] { return L().getValue() > R().getValue(); };
- if (Op == ">=")
- return [=] { return L().getValue() >= R().getValue(); };
- if (Op == "<=")
- return [=] { return L().getValue() <= R().getValue(); };
- if (Op == "==")
- return [=] { return L().getValue() == R().getValue(); };
- if (Op == "!=")
- return [=] { return L().getValue() != R().getValue(); };
- if (Op == "||")
- return [=] { return L().getValue() || R().getValue(); };
- if (Op == "&&")
- return [=] { return L().getValue() && R().getValue(); };
- if (Op == "&")
- return [=] { return bitAnd(L(), R()); };
- if (Op == "|")
- return [=] { return bitOr(L(), R()); };
+ if (op == "<<")
+ return [=] { return l().getValue() << r().getValue(); };
+ if (op == ">>")
+ return [=] { return l().getValue() >> r().getValue(); };
+ if (op == "<")
+ return [=] { return l().getValue() < r().getValue(); };
+ if (op == ">")
+ return [=] { return l().getValue() > r().getValue(); };
+ if (op == ">=")
+ return [=] { return l().getValue() >= r().getValue(); };
+ if (op == "<=")
+ return [=] { return l().getValue() <= r().getValue(); };
+ if (op == "==")
+ return [=] { return l().getValue() == r().getValue(); };
+ if (op == "!=")
+ return [=] { return l().getValue() != r().getValue(); };
+ if (op == "||")
+ return [=] { return l().getValue() || r().getValue(); };
+ if (op == "&&")
+ return [=] { return l().getValue() && r().getValue(); };
+ if (op == "&")
+ return [=] { return bitAnd(l(), r()); };
+ if (op == "|")
+ return [=] { return bitOr(l(), r()); };
llvm_unreachable("invalid operator");
}
// This is a part of the operator-precedence parser. This function
// assumes that the remaining token stream starts with an operator.
-Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) {
+Expr ScriptParser::readExpr1(Expr lhs, int minPrec) {
while (!atEOF() && !errorCount()) {
// Read an operator and an expression.
if (consume("?"))
- return readTernary(Lhs);
- StringRef Op1 = peek();
- if (precedence(Op1) < MinPrec)
+ return readTernary(lhs);
+ StringRef op1 = peek();
+ if (precedence(op1) < minPrec)
break;
skip();
- Expr Rhs = readPrimary();
+ Expr rhs = readPrimary();
// Evaluate the remaining part of the expression first if the
// next operator has greater precedence than the previous one.
// For example, if we have read "+" and "3", and if the next
// operator is "*", then we'll evaluate 3 * ... part first.
while (!atEOF()) {
- StringRef Op2 = peek();
- if (precedence(Op2) <= precedence(Op1))
+ StringRef op2 = peek();
+ if (precedence(op2) <= precedence(op1))
break;
- Rhs = readExpr1(Rhs, precedence(Op2));
+ rhs = readExpr1(rhs, precedence(op2));
}
- Lhs = combine(Op1, Lhs, Rhs);
+ lhs = combine(op1, lhs, rhs);
}
- return Lhs;
+ return lhs;
}
Expr ScriptParser::getPageSize() {
- std::string Location = getCurrentLocation();
+ std::string location = getCurrentLocation();
return [=]() -> uint64_t {
- if (Target)
- return Target->PageSize;
- error(Location + ": unable to calculate page size");
+ if (target)
+ return config->commonPageSize;
+ error(location + ": unable to calculate page size");
return 4096; // Return a dummy value.
};
}
Expr ScriptParser::readConstant() {
- StringRef S = readParenLiteral();
- if (S == "COMMONPAGESIZE")
+ StringRef s = readParenLiteral();
+ if (s == "COMMONPAGESIZE")
return getPageSize();
- if (S == "MAXPAGESIZE")
- return [] { return Config->MaxPageSize; };
- setError("unknown constant: " + S);
+ if (s == "MAXPAGESIZE")
+ return [] { return config->maxPageSize; };
+ setError("unknown constant: " + s);
return [] { return 0; };
}
// Parses Tok as an integer. It recognizes hexadecimal (prefixed with
// "0x" or suffixed with "H") and decimal numbers. Decimal numbers may
// have "K" (Ki) or "M" (Mi) suffixes.
-static Optional<uint64_t> parseInt(StringRef Tok) {
+static Optional<uint64_t> parseInt(StringRef tok) {
// Hexadecimal
- uint64_t Val;
- if (Tok.startswith_lower("0x")) {
- if (!to_integer(Tok.substr(2), Val, 16))
+ uint64_t val;
+ if (tok.startswith_lower("0x")) {
+ if (!to_integer(tok.substr(2), val, 16))
return None;
- return Val;
+ return val;
}
- if (Tok.endswith_lower("H")) {
- if (!to_integer(Tok.drop_back(), Val, 16))
+ if (tok.endswith_lower("H")) {
+ if (!to_integer(tok.drop_back(), val, 16))
return None;
- return Val;
+ return val;
}
// Decimal
- if (Tok.endswith_lower("K")) {
- if (!to_integer(Tok.drop_back(), Val, 10))
+ if (tok.endswith_lower("K")) {
+ if (!to_integer(tok.drop_back(), val, 10))
return None;
- return Val * 1024;
+ return val * 1024;
}
- if (Tok.endswith_lower("M")) {
- if (!to_integer(Tok.drop_back(), Val, 10))
+ if (tok.endswith_lower("M")) {
+ if (!to_integer(tok.drop_back(), val, 10))
return None;
- return Val * 1024 * 1024;
+ return val * 1024 * 1024;
}
- if (!to_integer(Tok, Val, 10))
+ if (!to_integer(tok, val, 10))
return None;
- return Val;
+ return val;
}
-ByteCommand *ScriptParser::readByteCommand(StringRef Tok) {
- int Size = StringSwitch<int>(Tok)
+ByteCommand *ScriptParser::readByteCommand(StringRef tok) {
+ int size = StringSwitch<int>(tok)
.Case("BYTE", 1)
.Case("SHORT", 2)
.Case("LONG", 4)
.Case("QUAD", 8)
.Default(-1);
- if (Size == -1)
+ if (size == -1)
return nullptr;
- size_t OldPos = Pos;
- Expr E = readParenExpr();
- std::string CommandString =
- Tok.str() + " " +
- llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
- return make<ByteCommand>(E, Size, CommandString);
+ size_t oldPos = pos;
+ Expr e = readParenExpr();
+ std::string commandString =
+ tok.str() + " " +
+ llvm::join(tokens.begin() + oldPos, tokens.begin() + pos, " ");
+ return make<ByteCommand>(e, size, commandString);
}
StringRef ScriptParser::readParenLiteral() {
expect("(");
- bool Orig = InExpr;
- InExpr = false;
- StringRef Tok = next();
- InExpr = Orig;
+ bool orig = inExpr;
+ inExpr = false;
+ StringRef tok = next();
+ inExpr = orig;
expect(")");
- return Tok;
+ return tok;
}
-static void checkIfExists(OutputSection *Cmd, StringRef Location) {
- if (Cmd->Location.empty() && Script->ErrorOnMissingSection)
- error(Location + ": undefined section " + Cmd->Name);
+static void checkIfExists(OutputSection *cmd, StringRef location) {
+ if (cmd->location.empty() && script->errorOnMissingSection)
+ error(location + ": undefined section " + cmd->name);
}
Expr ScriptParser::readPrimary() {
@@ -1135,84 +1122,85 @@ Expr ScriptParser::readPrimary() {
return readParenExpr();
if (consume("~")) {
- Expr E = readPrimary();
- return [=] { return ~E().getValue(); };
+ Expr e = readPrimary();
+ return [=] { return ~e().getValue(); };
}
if (consume("!")) {
- Expr E = readPrimary();
- return [=] { return !E().getValue(); };
+ Expr e = readPrimary();
+ return [=] { return !e().getValue(); };
}
if (consume("-")) {
- Expr E = readPrimary();
- return [=] { return -E().getValue(); };
+ Expr e = readPrimary();
+ return [=] { return -e().getValue(); };
}
- StringRef Tok = next();
- std::string Location = getCurrentLocation();
+ StringRef tok = next();
+ std::string location = getCurrentLocation();
// Built-in functions are parsed here.
// https://sourceware.org/binutils/docs/ld/Builtin-Functions.html.
- if (Tok == "ABSOLUTE") {
- Expr Inner = readParenExpr();
+ if (tok == "ABSOLUTE") {
+ Expr inner = readParenExpr();
return [=] {
- ExprValue I = Inner();
- I.ForceAbsolute = true;
- return I;
+ ExprValue i = inner();
+ i.forceAbsolute = true;
+ return i;
};
}
- if (Tok == "ADDR") {
- StringRef Name = readParenLiteral();
- OutputSection *Sec = Script->getOrCreateOutputSection(Name);
+ if (tok == "ADDR") {
+ StringRef name = readParenLiteral();
+ OutputSection *sec = script->getOrCreateOutputSection(name);
+ sec->usedInExpression = true;
return [=]() -> ExprValue {
- checkIfExists(Sec, Location);
- return {Sec, false, 0, Location};
+ checkIfExists(sec, location);
+ return {sec, false, 0, location};
};
}
- if (Tok == "ALIGN") {
+ if (tok == "ALIGN") {
expect("(");
- Expr E = readExpr();
+ Expr e = readExpr();
if (consume(")")) {
- E = checkAlignment(E, Location);
- return [=] { return alignTo(Script->getDot(), E().getValue()); };
+ e = checkAlignment(e, location);
+ return [=] { return alignTo(script->getDot(), e().getValue()); };
}
expect(",");
- Expr E2 = checkAlignment(readExpr(), Location);
+ Expr e2 = checkAlignment(readExpr(), location);
expect(")");
return [=] {
- ExprValue V = E();
- V.Alignment = E2().getValue();
- return V;
+ ExprValue v = e();
+ v.alignment = e2().getValue();
+ return v;
};
}
- if (Tok == "ALIGNOF") {
- StringRef Name = readParenLiteral();
- OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
+ if (tok == "ALIGNOF") {
+ StringRef name = readParenLiteral();
+ OutputSection *cmd = script->getOrCreateOutputSection(name);
return [=] {
- checkIfExists(Cmd, Location);
- return Cmd->Alignment;
+ checkIfExists(cmd, location);
+ return cmd->alignment;
};
}
- if (Tok == "ASSERT")
+ if (tok == "ASSERT")
return readAssert();
- if (Tok == "CONSTANT")
+ if (tok == "CONSTANT")
return readConstant();
- if (Tok == "DATA_SEGMENT_ALIGN") {
+ if (tok == "DATA_SEGMENT_ALIGN") {
expect("(");
- Expr E = readExpr();
+ Expr e = readExpr();
expect(",");
readExpr();
expect(")");
return [=] {
- return alignTo(Script->getDot(), std::max((uint64_t)1, E().getValue()));
+ return alignTo(script->getDot(), std::max((uint64_t)1, e().getValue()));
};
}
- if (Tok == "DATA_SEGMENT_END") {
+ if (tok == "DATA_SEGMENT_END") {
expect("(");
expect(".");
expect(")");
- return [] { return Script->getDot(); };
+ return [] { return script->getDot(); };
}
- if (Tok == "DATA_SEGMENT_RELRO_END") {
+ if (tok == "DATA_SEGMENT_RELRO_END") {
// GNU linkers implements more complicated logic to handle
// DATA_SEGMENT_RELRO_END. We instead ignore the arguments and
// just align to the next page boundary for simplicity.
@@ -1221,112 +1209,113 @@ Expr ScriptParser::readPrimary() {
expect(",");
readExpr();
expect(")");
- Expr E = getPageSize();
- return [=] { return alignTo(Script->getDot(), E().getValue()); };
+ Expr e = getPageSize();
+ return [=] { return alignTo(script->getDot(), e().getValue()); };
}
- if (Tok == "DEFINED") {
- StringRef Name = readParenLiteral();
- return [=] { return Symtab->find(Name) ? 1 : 0; };
+ if (tok == "DEFINED") {
+ StringRef name = readParenLiteral();
+ return [=] { return symtab->find(name) ? 1 : 0; };
}
- if (Tok == "LENGTH") {
- StringRef Name = readParenLiteral();
- if (Script->MemoryRegions.count(Name) == 0) {
- setError("memory region not defined: " + Name);
+ if (tok == "LENGTH") {
+ StringRef name = readParenLiteral();
+ if (script->memoryRegions.count(name) == 0) {
+ setError("memory region not defined: " + name);
return [] { return 0; };
}
- return [=] { return Script->MemoryRegions[Name]->Length; };
+ return [=] { return script->memoryRegions[name]->length; };
}
- if (Tok == "LOADADDR") {
- StringRef Name = readParenLiteral();
- OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
+ if (tok == "LOADADDR") {
+ StringRef name = readParenLiteral();
+ OutputSection *cmd = script->getOrCreateOutputSection(name);
+ cmd->usedInExpression = true;
return [=] {
- checkIfExists(Cmd, Location);
- return Cmd->getLMA();
+ checkIfExists(cmd, location);
+ return cmd->getLMA();
};
}
- if (Tok == "MAX" || Tok == "MIN") {
+ if (tok == "MAX" || tok == "MIN") {
expect("(");
- Expr A = readExpr();
+ Expr a = readExpr();
expect(",");
- Expr B = readExpr();
+ Expr b = readExpr();
expect(")");
- if (Tok == "MIN")
- return [=] { return std::min(A().getValue(), B().getValue()); };
- return [=] { return std::max(A().getValue(), B().getValue()); };
+ if (tok == "MIN")
+ return [=] { return std::min(a().getValue(), b().getValue()); };
+ return [=] { return std::max(a().getValue(), b().getValue()); };
}
- if (Tok == "ORIGIN") {
- StringRef Name = readParenLiteral();
- if (Script->MemoryRegions.count(Name) == 0) {
- setError("memory region not defined: " + Name);
+ if (tok == "ORIGIN") {
+ StringRef name = readParenLiteral();
+ if (script->memoryRegions.count(name) == 0) {
+ setError("memory region not defined: " + name);
return [] { return 0; };
}
- return [=] { return Script->MemoryRegions[Name]->Origin; };
+ return [=] { return script->memoryRegions[name]->origin; };
}
- if (Tok == "SEGMENT_START") {
+ if (tok == "SEGMENT_START") {
expect("(");
skip();
expect(",");
- Expr E = readExpr();
+ Expr e = readExpr();
expect(")");
- return [=] { return E(); };
+ return [=] { return e(); };
}
- if (Tok == "SIZEOF") {
- StringRef Name = readParenLiteral();
- OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
+ if (tok == "SIZEOF") {
+ StringRef name = readParenLiteral();
+ OutputSection *cmd = script->getOrCreateOutputSection(name);
// Linker script does not create an output section if its content is empty.
// We want to allow SIZEOF(.foo) where .foo is a section which happened to
// be empty.
- return [=] { return Cmd->Size; };
+ return [=] { return cmd->size; };
}
- if (Tok == "SIZEOF_HEADERS")
+ if (tok == "SIZEOF_HEADERS")
return [=] { return elf::getHeaderSize(); };
// Tok is the dot.
- if (Tok == ".")
- return [=] { return Script->getSymbolValue(Tok, Location); };
+ if (tok == ".")
+ return [=] { return script->getSymbolValue(tok, location); };
// Tok is a literal number.
- if (Optional<uint64_t> Val = parseInt(Tok))
- return [=] { return *Val; };
+ if (Optional<uint64_t> val = parseInt(tok))
+ return [=] { return *val; };
// Tok is a symbol name.
- if (!isValidCIdentifier(Tok))
- setError("malformed number: " + Tok);
- Script->ReferencedSymbols.push_back(Tok);
- return [=] { return Script->getSymbolValue(Tok, Location); };
+ if (!isValidCIdentifier(tok))
+ setError("malformed number: " + tok);
+ script->referencedSymbols.push_back(tok);
+ return [=] { return script->getSymbolValue(tok, location); };
}
-Expr ScriptParser::readTernary(Expr Cond) {
- Expr L = readExpr();
+Expr ScriptParser::readTernary(Expr cond) {
+ Expr l = readExpr();
expect(":");
- Expr R = readExpr();
- return [=] { return Cond().getValue() ? L() : R(); };
+ Expr r = readExpr();
+ return [=] { return cond().getValue() ? l() : r(); };
}
Expr ScriptParser::readParenExpr() {
expect("(");
- Expr E = readExpr();
+ Expr e = readExpr();
expect(")");
- return E;
+ return e;
}
std::vector<StringRef> ScriptParser::readOutputSectionPhdrs() {
- std::vector<StringRef> Phdrs;
+ std::vector<StringRef> phdrs;
while (!errorCount() && peek().startswith(":")) {
- StringRef Tok = next();
- Phdrs.push_back((Tok.size() == 1) ? next() : Tok.substr(1));
+ StringRef tok = next();
+ phdrs.push_back((tok.size() == 1) ? next() : tok.substr(1));
}
- return Phdrs;
+ return phdrs;
}
// Read a program header type name. The next token must be a
// name of a program header type or a constant (e.g. "0x3").
unsigned ScriptParser::readPhdrType() {
- StringRef Tok = next();
- if (Optional<uint64_t> Val = parseInt(Tok))
- return *Val;
+ StringRef tok = next();
+ if (Optional<uint64_t> val = parseInt(tok))
+ return *val;
- unsigned Ret = StringSwitch<unsigned>(Tok)
+ unsigned ret = StringSwitch<unsigned>(tok)
.Case("PT_NULL", PT_NULL)
.Case("PT_LOAD", PT_LOAD)
.Case("PT_DYNAMIC", PT_DYNAMIC)
@@ -1343,56 +1332,56 @@ unsigned ScriptParser::readPhdrType() {
.Case("PT_OPENBSD_BOOTDATA", PT_OPENBSD_BOOTDATA)
.Default(-1);
- if (Ret == (unsigned)-1) {
- setError("invalid program header type: " + Tok);
+ if (ret == (unsigned)-1) {
+ setError("invalid program header type: " + tok);
return PT_NULL;
}
- return Ret;
+ return ret;
}
// Reads an anonymous version declaration.
void ScriptParser::readAnonymousDeclaration() {
- std::vector<SymbolVersion> Locals;
- std::vector<SymbolVersion> Globals;
- std::tie(Locals, Globals) = readSymbols();
+ std::vector<SymbolVersion> locals;
+ std::vector<SymbolVersion> globals;
+ std::tie(locals, globals) = readSymbols();
- for (SymbolVersion V : Locals) {
- if (V.Name == "*")
- Config->DefaultSymbolVersion = VER_NDX_LOCAL;
+ for (SymbolVersion v : locals) {
+ if (v.name == "*")
+ config->defaultSymbolVersion = VER_NDX_LOCAL;
else
- Config->VersionScriptLocals.push_back(V);
+ config->versionScriptLocals.push_back(v);
}
- for (SymbolVersion V : Globals)
- Config->VersionScriptGlobals.push_back(V);
+ for (SymbolVersion v : globals)
+ config->versionScriptGlobals.push_back(v);
expect(";");
}
// Reads a non-anonymous version definition,
// e.g. "VerStr { global: foo; bar; local: *; };".
-void ScriptParser::readVersionDeclaration(StringRef VerStr) {
+void ScriptParser::readVersionDeclaration(StringRef verStr) {
// Read a symbol list.
- std::vector<SymbolVersion> Locals;
- std::vector<SymbolVersion> Globals;
- std::tie(Locals, Globals) = readSymbols();
+ std::vector<SymbolVersion> locals;
+ std::vector<SymbolVersion> globals;
+ std::tie(locals, globals) = readSymbols();
- for (SymbolVersion V : Locals) {
- if (V.Name == "*")
- Config->DefaultSymbolVersion = VER_NDX_LOCAL;
+ for (SymbolVersion v : locals) {
+ if (v.name == "*")
+ config->defaultSymbolVersion = VER_NDX_LOCAL;
else
- Config->VersionScriptLocals.push_back(V);
+ config->versionScriptLocals.push_back(v);
}
// Create a new version definition and add that to the global symbols.
- VersionDefinition Ver;
- Ver.Name = VerStr;
- Ver.Globals = Globals;
+ VersionDefinition ver;
+ ver.name = verStr;
+ ver.globals = globals;
// User-defined version number starts from 2 because 0 and 1 are
// reserved for VER_NDX_LOCAL and VER_NDX_GLOBAL, respectively.
- Ver.Id = Config->VersionDefinitions.size() + 2;
- Config->VersionDefinitions.push_back(Ver);
+ ver.id = config->versionDefinitions.size() + 2;
+ config->versionDefinitions.push_back(ver);
// Each version may have a parent version. For example, "Ver2"
// defined as "Ver2 { global: foo; local: *; } Ver1;" has "Ver1"
@@ -1404,39 +1393,39 @@ void ScriptParser::readVersionDeclaration(StringRef VerStr) {
expect(";");
}
-static bool hasWildcard(StringRef S) {
- return S.find_first_of("?*[") != StringRef::npos;
+static bool hasWildcard(StringRef s) {
+ return s.find_first_of("?*[") != StringRef::npos;
}
// Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };".
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
ScriptParser::readSymbols() {
- std::vector<SymbolVersion> Locals;
- std::vector<SymbolVersion> Globals;
- std::vector<SymbolVersion> *V = &Globals;
+ std::vector<SymbolVersion> locals;
+ std::vector<SymbolVersion> globals;
+ std::vector<SymbolVersion> *v = &globals;
while (!errorCount()) {
if (consume("}"))
break;
if (consumeLabel("local")) {
- V = &Locals;
+ v = &locals;
continue;
}
if (consumeLabel("global")) {
- V = &Globals;
+ v = &globals;
continue;
}
if (consume("extern")) {
- std::vector<SymbolVersion> Ext = readVersionExtern();
- V->insert(V->end(), Ext.begin(), Ext.end());
+ std::vector<SymbolVersion> ext = readVersionExtern();
+ v->insert(v->end(), ext.begin(), ext.end());
} else {
- StringRef Tok = next();
- V->push_back({unquote(Tok), false, hasWildcard(Tok)});
+ StringRef tok = next();
+ v->push_back({unquote(tok), false, hasWildcard(tok)});
}
expect(";");
}
- return {Locals, Globals};
+ return {locals, globals};
}
// Reads an "extern C++" directive, e.g.,
@@ -1445,30 +1434,30 @@ ScriptParser::readSymbols() {
// The last semicolon is optional. E.g. this is OK:
// "extern "C++" { ns::*; "f(int, double)" };"
std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
- StringRef Tok = next();
- bool IsCXX = Tok == "\"C++\"";
- if (!IsCXX && Tok != "\"C\"")
+ StringRef tok = next();
+ bool isCXX = tok == "\"C++\"";
+ if (!isCXX && tok != "\"C\"")
setError("Unknown language");
expect("{");
- std::vector<SymbolVersion> Ret;
+ std::vector<SymbolVersion> ret;
while (!errorCount() && peek() != "}") {
- StringRef Tok = next();
- bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok);
- Ret.push_back({unquote(Tok), IsCXX, HasWildcard});
+ StringRef tok = next();
+ ret.push_back(
+ {unquote(tok), isCXX, !tok.startswith("\"") && hasWildcard(tok)});
if (consume("}"))
- return Ret;
+ return ret;
expect(";");
}
expect("}");
- return Ret;
+ return ret;
}
-uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2,
- StringRef S3) {
- if (!consume(S1) && !consume(S2) && !consume(S3)) {
- setError("expected one of: " + S1 + ", " + S2 + ", or " + S3);
+uint64_t ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2,
+ StringRef s3) {
+ if (!consume(s1) && !consume(s2) && !consume(s3)) {
+ setError("expected one of: " + s1 + ", " + s2 + ", or " + s3);
return 0;
}
expect("=");
@@ -1482,28 +1471,28 @@ uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2,
void ScriptParser::readMemory() {
expect("{");
while (!errorCount() && !consume("}")) {
- StringRef Tok = next();
- if (Tok == "INCLUDE") {
+ StringRef tok = next();
+ if (tok == "INCLUDE") {
readInclude();
continue;
}
- uint32_t Flags = 0;
- uint32_t NegFlags = 0;
+ uint32_t flags = 0;
+ uint32_t negFlags = 0;
if (consume("(")) {
- std::tie(Flags, NegFlags) = readMemoryAttributes();
+ std::tie(flags, negFlags) = readMemoryAttributes();
expect(")");
}
expect(":");
- uint64_t Origin = readMemoryAssignment("ORIGIN", "org", "o");
+ uint64_t origin = readMemoryAssignment("ORIGIN", "org", "o");
expect(",");
- uint64_t Length = readMemoryAssignment("LENGTH", "len", "l");
+ uint64_t length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
- MemoryRegion *MR = make<MemoryRegion>(Tok, Origin, Length, Flags, NegFlags);
- if (!Script->MemoryRegions.insert({Tok, MR}).second)
- setError("region '" + Tok + "' already defined");
+ MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, negFlags);
+ if (!script->memoryRegions.insert({tok, mr}).second)
+ setError("region '" + tok + "' already defined");
}
}
@@ -1511,43 +1500,43 @@ void ScriptParser::readMemory() {
// flags when placing output sections in a memory region. These flags
// are only used when an explicit memory region name is not used.
std::pair<uint32_t, uint32_t> ScriptParser::readMemoryAttributes() {
- uint32_t Flags = 0;
- uint32_t NegFlags = 0;
- bool Invert = false;
-
- for (char C : next().lower()) {
- uint32_t Flag = 0;
- if (C == '!')
- Invert = !Invert;
- else if (C == 'w')
- Flag = SHF_WRITE;
- else if (C == 'x')
- Flag = SHF_EXECINSTR;
- else if (C == 'a')
- Flag = SHF_ALLOC;
- else if (C != 'r')
+ uint32_t flags = 0;
+ uint32_t negFlags = 0;
+ bool invert = false;
+
+ for (char c : next().lower()) {
+ uint32_t flag = 0;
+ if (c == '!')
+ invert = !invert;
+ else if (c == 'w')
+ flag = SHF_WRITE;
+ else if (c == 'x')
+ flag = SHF_EXECINSTR;
+ else if (c == 'a')
+ flag = SHF_ALLOC;
+ else if (c != 'r')
setError("invalid memory region attribute");
- if (Invert)
- NegFlags |= Flag;
+ if (invert)
+ negFlags |= flag;
else
- Flags |= Flag;
+ flags |= flag;
}
- return {Flags, NegFlags};
+ return {flags, negFlags};
}
-void elf::readLinkerScript(MemoryBufferRef MB) {
- ScriptParser(MB).readLinkerScript();
+void elf::readLinkerScript(MemoryBufferRef mb) {
+ ScriptParser(mb).readLinkerScript();
}
-void elf::readVersionScript(MemoryBufferRef MB) {
- ScriptParser(MB).readVersionScript();
+void elf::readVersionScript(MemoryBufferRef mb) {
+ ScriptParser(mb).readVersionScript();
}
-void elf::readDynamicList(MemoryBufferRef MB) {
- ScriptParser(MB).readDynamicList();
+void elf::readDynamicList(MemoryBufferRef mb) {
+ ScriptParser(mb).readDynamicList();
}
-void elf::readDefsym(StringRef Name, MemoryBufferRef MB) {
- ScriptParser(MB).readDefsym(Name);
+void elf::readDefsym(StringRef name, MemoryBufferRef mb) {
+ ScriptParser(mb).readDefsym(name);
}
diff --git a/ELF/ScriptParser.h b/ELF/ScriptParser.h
index d48d5aa2115e3..c953fb302b9a7 100644
--- a/ELF/ScriptParser.h
+++ b/ELF/ScriptParser.h
@@ -1,9 +1,8 @@
//===- ScriptParser.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -17,16 +16,16 @@ namespace lld {
namespace elf {
// Parses a linker script. Calling this function updates
-// Config and ScriptConfig.
-void readLinkerScript(MemoryBufferRef MB);
+// lld::elf::config and lld::elf::script.
+void readLinkerScript(MemoryBufferRef mb);
// Parses a version script.
-void readVersionScript(MemoryBufferRef MB);
+void readVersionScript(MemoryBufferRef mb);
-void readDynamicList(MemoryBufferRef MB);
+void readDynamicList(MemoryBufferRef mb);
// Parses the defsym expression.
-void readDefsym(StringRef Name, MemoryBufferRef MB);
+void readDefsym(StringRef name, MemoryBufferRef mb);
} // namespace elf
} // namespace lld
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index 7615e12199faf..3faeed8c2bdc3 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -1,9 +1,8 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -31,576 +30,76 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-SymbolTable *elf::Symtab;
-
-static InputFile *getFirstElf() {
- if (!ObjectFiles.empty())
- return ObjectFiles[0];
- if (!SharedFiles.empty())
- return SharedFiles[0];
- return BitcodeFiles[0];
-}
-
-// All input object files must be for the same architecture
-// (e.g. it does not make sense to link x86 object files with
-// MIPS object files.) This function checks for that error.
-static bool isCompatible(InputFile *F) {
- if (!F->isElf() && !isa<BitcodeFile>(F))
- return true;
-
- if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) {
- if (Config->EMachine != EM_MIPS)
- return true;
- if (isMipsN32Abi(F) == Config->MipsN32Abi)
- return true;
- }
-
- if (!Config->Emulation.empty())
- error(toString(F) + " is incompatible with " + Config->Emulation);
- else
- error(toString(F) + " is incompatible with " + toString(getFirstElf()));
- return false;
-}
-
-// Add symbols in File to the symbol table.
-template <class ELFT> void SymbolTable::addFile(InputFile *File) {
- if (!isCompatible(File))
- return;
-
- // Binary file
- if (auto *F = dyn_cast<BinaryFile>(File)) {
- BinaryFiles.push_back(F);
- F->parse();
- return;
- }
-
- // .a file
- if (auto *F = dyn_cast<ArchiveFile>(File)) {
- F->parse<ELFT>();
- return;
- }
-
- // Lazy object file
- if (auto *F = dyn_cast<LazyObjFile>(File)) {
- LazyObjFiles.push_back(F);
- F->parse<ELFT>();
- return;
- }
-
- if (Config->Trace)
- message(toString(File));
-
- // .so file
- if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) {
- // DSOs are uniquified not by filename but by soname.
- F->parseSoName();
- if (errorCount())
- return;
-
- // If a DSO appears more than once on the command line with and without
- // --as-needed, --no-as-needed takes precedence over --as-needed because a
- // user can add an extra DSO with --no-as-needed to force it to be added to
- // the dependency list.
- DenseMap<StringRef, InputFile *>::iterator It;
- bool WasInserted;
- std::tie(It, WasInserted) = SoNames.try_emplace(F->SoName, F);
- cast<SharedFile<ELFT>>(It->second)->IsNeeded |= F->IsNeeded;
- if (!WasInserted)
- return;
-
- SharedFiles.push_back(F);
- F->parseRest();
- return;
- }
-
- // LLVM bitcode file
- if (auto *F = dyn_cast<BitcodeFile>(File)) {
- BitcodeFiles.push_back(F);
- F->parse<ELFT>(ComdatGroups);
- return;
- }
-
- // Regular object file
- ObjectFiles.push_back(File);
- cast<ObjFile<ELFT>>(File)->parse(ComdatGroups);
-}
-
-// This function is where all the optimizations of link-time
-// optimization happens. When LTO is in use, some input files are
-// not in native object file format but in the LLVM bitcode format.
-// This function compiles bitcode files into a few big native files
-// using LLVM functions and replaces bitcode symbols with the results.
-// Because all bitcode files that the program consists of are passed
-// to the compiler at once, it can do whole-program optimization.
-template <class ELFT> void SymbolTable::addCombinedLTOObject() {
- if (BitcodeFiles.empty())
- return;
-
- // Compile bitcode files and replace bitcode symbols.
- LTO.reset(new BitcodeCompiler);
- for (BitcodeFile *F : BitcodeFiles)
- LTO->add(*F);
-
- for (InputFile *File : LTO->compile()) {
- DenseSet<CachedHashStringRef> DummyGroups;
- auto *Obj = cast<ObjFile<ELFT>>(File);
- Obj->parse(DummyGroups);
- for (Symbol *Sym : Obj->getGlobalSymbols())
- Sym->parseSymbolVersion();
- ObjectFiles.push_back(File);
- }
-}
-
-// Set a flag for --trace-symbol so that we can print out a log message
-// if a new symbol with the same name is inserted into the symbol table.
-void SymbolTable::trace(StringRef Name) {
- SymMap.insert({CachedHashStringRef(Name), -1});
-}
+SymbolTable *elf::symtab;
-void SymbolTable::wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap) {
+void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
// Swap symbols as instructed by -wrap.
- int &Idx1 = SymMap[CachedHashStringRef(Sym->getName())];
- int &Idx2 = SymMap[CachedHashStringRef(Real->getName())];
- int &Idx3 = SymMap[CachedHashStringRef(Wrap->getName())];
+ int &idx1 = symMap[CachedHashStringRef(sym->getName())];
+ int &idx2 = symMap[CachedHashStringRef(real->getName())];
+ int &idx3 = symMap[CachedHashStringRef(wrap->getName())];
- Idx2 = Idx1;
- Idx1 = Idx3;
+ idx2 = idx1;
+ idx1 = idx3;
// Now renaming is complete. No one refers Real symbol. We could leave
// Real as-is, but if Real is written to the symbol table, that may
// contain irrelevant values. So, we copy all values from Sym to Real.
- StringRef S = Real->getName();
- memcpy(Real, Sym, sizeof(SymbolUnion));
- Real->setName(S);
-}
-
-static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
- if (VA == STV_DEFAULT)
- return VB;
- if (VB == STV_DEFAULT)
- return VA;
- return std::min(VA, VB);
+ StringRef s = real->getName();
+ memcpy(real, sym, sizeof(SymbolUnion));
+ real->setName(s);
}
-// Find an existing symbol or create and insert a new one.
-std::pair<Symbol *, bool> SymbolTable::insertName(StringRef Name) {
+// Find an existing symbol or create a new one.
+Symbol *SymbolTable::insert(StringRef name) {
// <name>@@<version> means the symbol is the default version. In that
// case <name>@@<version> will be used to resolve references to <name>.
//
// Since this is a hot path, the following string search code is
// optimized for speed. StringRef::find(char) is much faster than
// StringRef::find(StringRef).
- size_t Pos = Name.find('@');
- if (Pos != StringRef::npos && Pos + 1 < Name.size() && Name[Pos + 1] == '@')
- Name = Name.take_front(Pos);
-
- auto P = SymMap.insert({CachedHashStringRef(Name), (int)SymVector.size()});
- int &SymIndex = P.first->second;
- bool IsNew = P.second;
- bool Traced = false;
-
- if (SymIndex == -1) {
- SymIndex = SymVector.size();
- IsNew = true;
- Traced = true;
- }
-
- if (!IsNew)
- return {SymVector[SymIndex], false};
-
- auto *Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
- Sym->SymbolKind = Symbol::PlaceholderKind;
- Sym->Visibility = STV_DEFAULT;
- Sym->IsUsedInRegularObj = false;
- Sym->ExportDynamic = false;
- Sym->CanInline = true;
- Sym->Traced = Traced;
- Sym->VersionId = Config->DefaultSymbolVersion;
- SymVector.push_back(Sym);
- return {Sym, true};
+ size_t pos = name.find('@');
+ if (pos != StringRef::npos && pos + 1 < name.size() && name[pos + 1] == '@')
+ name = name.take_front(pos);
+
+ auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()});
+ int &symIndex = p.first->second;
+ bool isNew = p.second;
+
+ if (!isNew)
+ return symVector[symIndex];
+
+ Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ symVector.push_back(sym);
+
+ sym->setName(name);
+ sym->symbolKind = Symbol::PlaceholderKind;
+ sym->versionId = config->defaultSymbolVersion;
+ sym->visibility = STV_DEFAULT;
+ sym->isUsedInRegularObj = false;
+ sym->exportDynamic = false;
+ sym->canInline = true;
+ sym->scriptDefined = false;
+ sym->partition = 1;
+ return sym;
}
-// Find an existing symbol or create and insert a new one, then apply the given
-// attributes.
-std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name,
- uint8_t Visibility,
- bool CanOmitFromDynSym,
- InputFile *File) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insertName(Name);
-
- // Merge in the new symbol's visibility.
- S->Visibility = getMinVisibility(S->Visibility, Visibility);
-
- if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
- S->ExportDynamic = true;
-
- if (!File || File->kind() == InputFile::ObjKind)
- S->IsUsedInRegularObj = true;
-
- return {S, WasInserted};
+Symbol *SymbolTable::addSymbol(const Symbol &New) {
+ Symbol *sym = symtab->insert(New.getName());
+ sym->resolve(New);
+ return sym;
}
-static uint8_t getVisibility(uint8_t StOther) { return StOther & 3; }
-
-template <class ELFT>
-Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type,
- bool CanOmitFromDynSym, InputFile *File) {
- Symbol *S;
- bool WasInserted;
- uint8_t Visibility = getVisibility(StOther);
- std::tie(S, WasInserted) = insert(Name, Visibility, CanOmitFromDynSym, File);
-
- // An undefined symbol with non default visibility must be satisfied
- // in the same DSO.
- if (WasInserted || (isa<SharedSymbol>(S) && Visibility != STV_DEFAULT)) {
- replaceSymbol<Undefined>(S, File, Name, Binding, StOther, Type);
- return S;
- }
-
- if (S->isShared() || S->isLazy() || (S->isUndefined() && Binding != STB_WEAK))
- S->Binding = Binding;
-
- if (S->isLazy()) {
- // An undefined weak will not fetch archive members. See comment on Lazy in
- // Symbols.h for the details.
- if (Binding == STB_WEAK) {
- S->Type = Type;
- return S;
- }
-
- // Do extra check for --warn-backrefs.
- //
- // --warn-backrefs is an option to prevent an undefined reference from
- // fetching an archive member written earlier in the command line. It can be
- // used to keep compatibility with GNU linkers to some degree.
- // I'll explain the feature and why you may find it useful in this comment.
- //
- // lld's symbol resolution semantics is more relaxed than traditional Unix
- // linkers. For example,
- //
- // ld.lld foo.a bar.o
- //
- // succeeds even if bar.o contains an undefined symbol that has to be
- // resolved by some object file in foo.a. Traditional Unix linkers don't
- // allow this kind of backward reference, as they visit each file only once
- // from left to right in the command line while resolving all undefined
- // symbols at the moment of visiting.
- //
- // In the above case, since there's no undefined symbol when a linker visits
- // foo.a, no files are pulled out from foo.a, and because the linker forgets
- // about foo.a after visiting, it can't resolve undefined symbols in bar.o
- // that could have been resolved otherwise.
- //
- // That lld accepts more relaxed form means that (besides it'd make more
- // sense) you can accidentally write a command line or a build file that
- // works only with lld, even if you have a plan to distribute it to wider
- // users who may be using GNU linkers. With --warn-backrefs, you can detect
- // a library order that doesn't work with other Unix linkers.
- //
- // The option is also useful to detect cyclic dependencies between static
- // archives. Again, lld accepts
- //
- // ld.lld foo.a bar.a
- //
- // even if foo.a and bar.a depend on each other. With --warn-backrefs, it is
- // handled as an error.
- //
- // Here is how the option works. We assign a group ID to each file. A file
- // with a smaller group ID can pull out object files from an archive file
- // with an equal or greater group ID. Otherwise, it is a reverse dependency
- // and an error.
- //
- // A file outside --{start,end}-group gets a fresh ID when instantiated. All
- // files within the same --{start,end}-group get the same group ID. E.g.
- //
- // ld.lld A B --start-group C D --end-group E
- //
- // A forms group 0. B form group 1. C and D (including their member object
- // files) form group 2. E forms group 3. I think that you can see how this
- // group assignment rule simulates the traditional linker's semantics.
- bool Backref =
- Config->WarnBackrefs && File && S->File->GroupId < File->GroupId;
- fetchLazy<ELFT>(S);
-
- // We don't report backward references to weak symbols as they can be
- // overridden later.
- if (Backref && S->Binding != STB_WEAK)
- warn("backward reference detected: " + Name + " in " + toString(File) +
- " refers to " + toString(S->File));
- }
- return S;
-}
-
-// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and
-// foo@@VER. We want to effectively ignore foo, so give precedence to
-// foo@@VER.
-// FIXME: If users can transition to using
-// .symver foo,foo@@@VER
-// we can delete this hack.
-static int compareVersion(Symbol *S, StringRef Name) {
- bool A = Name.contains("@@");
- bool B = S->getName().contains("@@");
- if (A && !B)
- return 1;
- if (!A && B)
- return -1;
- return 0;
-}
-
-// We have a new defined symbol with the specified binding. Return 1 if the new
-// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are
-// strong defined symbols.
-static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding,
- StringRef Name) {
- if (WasInserted)
- return 1;
- if (!S->isDefined())
- return 1;
- if (int R = compareVersion(S, Name))
- return R;
- if (Binding == STB_WEAK)
- return -1;
- if (S->isWeak())
- return 1;
- return 0;
-}
-
-// We have a new non-common defined symbol with the specified binding. Return 1
-// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
-// is a conflict. If the new symbol wins, also update the binding.
-static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
- bool IsAbsolute, uint64_t Value,
- StringRef Name) {
- if (int Cmp = compareDefined(S, WasInserted, Binding, Name))
- return Cmp;
- if (auto *R = dyn_cast<Defined>(S)) {
- if (R->Section && isa<BssSection>(R->Section)) {
- // Non-common symbols take precedence over common symbols.
- if (Config->WarnCommon)
- warn("common " + S->getName() + " is overridden");
- return 1;
- }
- if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute &&
- R->Value == Value)
- return -1;
- }
- return 0;
-}
-
-Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
- uint8_t Binding, uint8_t StOther, uint8_t Type,
- InputFile &File) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, getVisibility(StOther),
- /*CanOmitFromDynSym*/ false, &File);
-
- int Cmp = compareDefined(S, WasInserted, Binding, N);
- if (Cmp < 0)
- return S;
-
- if (Cmp > 0) {
- auto *Bss = make<BssSection>("COMMON", Size, Alignment);
- Bss->File = &File;
- Bss->Live = !Config->GcSections;
- InputSections.push_back(Bss);
-
- replaceSymbol<Defined>(S, &File, N, Binding, StOther, Type, 0, Size, Bss);
- return S;
- }
-
- auto *D = cast<Defined>(S);
- auto *Bss = dyn_cast_or_null<BssSection>(D->Section);
- if (!Bss) {
- // Non-common symbols take precedence over common symbols.
- if (Config->WarnCommon)
- warn("common " + S->getName() + " is overridden");
- return S;
- }
-
- if (Config->WarnCommon)
- warn("multiple common of " + D->getName());
-
- Bss->Alignment = std::max(Bss->Alignment, Alignment);
- if (Size > Bss->Size) {
- D->File = Bss->File = &File;
- D->Size = Bss->Size = Size;
- }
- return S;
-}
-
-static void reportDuplicate(Symbol *Sym, InputFile *NewFile,
- InputSectionBase *ErrSec, uint64_t ErrOffset) {
- if (Config->AllowMultipleDefinition)
- return;
-
- Defined *D = cast<Defined>(Sym);
- if (!D->Section || !ErrSec) {
- error("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
- toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
- return;
- }
-
- // Construct and print an error message in the form of:
- //
- // ld.lld: error: duplicate symbol: foo
- // >>> defined at bar.c:30
- // >>> bar.o (/home/alice/src/bar.o)
- // >>> defined at baz.c:563
- // >>> baz.o in archive libbaz.a
- auto *Sec1 = cast<InputSectionBase>(D->Section);
- std::string Src1 = Sec1->getSrcMsg(*Sym, D->Value);
- std::string Obj1 = Sec1->getObjMsg(D->Value);
- std::string Src2 = ErrSec->getSrcMsg(*Sym, ErrOffset);
- std::string Obj2 = ErrSec->getObjMsg(ErrOffset);
-
- std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at ";
- if (!Src1.empty())
- Msg += Src1 + "\n>>> ";
- Msg += Obj1 + "\n>>> defined at ";
- if (!Src2.empty())
- Msg += Src2 + "\n>>> ";
- Msg += Obj2;
- error(Msg);
-}
-
-Defined *SymbolTable::addDefined(StringRef Name, uint8_t StOther, uint8_t Type,
- uint64_t Value, uint64_t Size, uint8_t Binding,
- SectionBase *Section, InputFile *File) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, getVisibility(StOther),
- /*CanOmitFromDynSym*/ false, File);
- int Cmp = compareDefinedNonCommon(S, WasInserted, Binding, Section == nullptr,
- Value, Name);
- if (Cmp > 0)
- replaceSymbol<Defined>(S, File, Name, Binding, StOther, Type, Value, Size,
- Section);
- else if (Cmp == 0)
- reportDuplicate(S, File, dyn_cast_or_null<InputSectionBase>(Section),
- Value);
- return cast<Defined>(S);
-}
-
-template <typename ELFT>
-void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> &File,
- const typename ELFT::Sym &Sym, uint32_t Alignment,
- uint32_t VerdefIndex) {
- // DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
- // as the visibility, which will leave the visibility in the symbol table
- // unchanged.
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, STV_DEFAULT,
- /*CanOmitFromDynSym*/ true, &File);
- // Make sure we preempt DSO symbols with default visibility.
- if (Sym.getVisibility() == STV_DEFAULT)
- S->ExportDynamic = true;
-
- // An undefined symbol with non default visibility must be satisfied
- // in the same DSO.
- auto Replace = [&](uint8_t Binding) {
- replaceSymbol<SharedSymbol>(S, File, Name, Binding, Sym.st_other,
- Sym.getType(), Sym.st_value, Sym.st_size,
- Alignment, VerdefIndex);
- };
-
- if (WasInserted)
- Replace(Sym.getBinding());
- else if (S->Visibility == STV_DEFAULT && (S->isUndefined() || S->isLazy()))
- Replace(S->Binding);
-}
-
-Symbol *SymbolTable::addBitcode(StringRef Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type,
- bool CanOmitFromDynSym, BitcodeFile &F) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) =
- insert(Name, getVisibility(StOther), CanOmitFromDynSym, &F);
- int Cmp = compareDefinedNonCommon(S, WasInserted, Binding,
- /*IsAbs*/ false, /*Value*/ 0, Name);
- if (Cmp > 0)
- replaceSymbol<Defined>(S, &F, Name, Binding, StOther, Type, 0, 0, nullptr);
- else if (Cmp == 0)
- reportDuplicate(S, &F, nullptr, 0);
- return S;
-}
-
-Symbol *SymbolTable::find(StringRef Name) {
- auto It = SymMap.find(CachedHashStringRef(Name));
- if (It == SymMap.end())
+Symbol *SymbolTable::find(StringRef name) {
+ auto it = symMap.find(CachedHashStringRef(name));
+ if (it == symMap.end())
return nullptr;
- if (It->second == -1)
+ Symbol *sym = symVector[it->second];
+ if (sym->isPlaceholder())
return nullptr;
- return SymVector[It->second];
+ return sym;
}
-template <class ELFT>
-void SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &File,
- const object::Archive::Symbol Sym) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insertName(Name);
- if (WasInserted) {
- replaceSymbol<LazyArchive>(S, File, STT_NOTYPE, Sym);
- return;
- }
- if (!S->isUndefined())
- return;
-
- // An undefined weak will not fetch archive members. See comment on Lazy in
- // Symbols.h for the details.
- if (S->isWeak()) {
- replaceSymbol<LazyArchive>(S, File, S->Type, Sym);
- S->Binding = STB_WEAK;
- return;
- }
-
- if (InputFile *F = File.fetch(Sym))
- addFile<ELFT>(F);
-}
-
-template <class ELFT>
-void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &File) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insertName(Name);
- if (WasInserted) {
- replaceSymbol<LazyObject>(S, File, STT_NOTYPE, Name);
- return;
- }
- if (!S->isUndefined())
- return;
-
- // An undefined weak will not fetch archive members. See comment on Lazy in
- // Symbols.h for the details.
- if (S->isWeak()) {
- replaceSymbol<LazyObject>(S, File, S->Type, Name);
- S->Binding = STB_WEAK;
- return;
- }
-
- if (InputFile *F = File.fetch())
- addFile<ELFT>(F);
-}
-
-template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) {
- if (auto *S = dyn_cast<LazyArchive>(Sym)) {
- if (InputFile *File = S->fetch())
- addFile<ELFT>(File);
- return;
- }
-
- auto *S = cast<LazyObject>(Sym);
- if (InputFile *File = cast<LazyObjFile>(S->File)->fetch())
- addFile<ELFT>(File);
-}
-
-// Initialize DemangledSyms with a map from demangled symbols to symbol
+// Initialize demangledSyms with a map from demangled symbols to symbol
// objects. Used to handle "extern C++" directive in version scripts.
//
// The map will contain all demangled symbols. That can be very large,
@@ -614,204 +113,156 @@ template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) {
// So, if "extern C++" feature is used, we need to demangle all known
// symbols.
StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
- if (!DemangledSyms) {
- DemangledSyms.emplace();
- for (Symbol *Sym : SymVector) {
- if (!Sym->isDefined())
+ if (!demangledSyms) {
+ demangledSyms.emplace();
+ for (Symbol *sym : symVector) {
+ if (!sym->isDefined() && !sym->isCommon())
continue;
- if (Optional<std::string> S = demangleItanium(Sym->getName()))
- (*DemangledSyms)[*S].push_back(Sym);
+ if (Optional<std::string> s = demangleItanium(sym->getName()))
+ (*demangledSyms)[*s].push_back(sym);
else
- (*DemangledSyms)[Sym->getName()].push_back(Sym);
+ (*demangledSyms)[sym->getName()].push_back(sym);
}
}
- return *DemangledSyms;
+ return *demangledSyms;
}
-std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion Ver) {
- if (Ver.IsExternCpp)
- return getDemangledSyms().lookup(Ver.Name);
- if (Symbol *B = find(Ver.Name))
- if (B->isDefined())
- return {B};
+std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) {
+ if (ver.isExternCpp)
+ return getDemangledSyms().lookup(ver.name);
+ if (Symbol *b = find(ver.name))
+ if (b->isDefined() || b->isCommon())
+ return {b};
return {};
}
-std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion Ver) {
- std::vector<Symbol *> Res;
- StringMatcher M(Ver.Name);
+std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
+ std::vector<Symbol *> res;
+ StringMatcher m(ver.name);
- if (Ver.IsExternCpp) {
- for (auto &P : getDemangledSyms())
- if (M.match(P.first()))
- Res.insert(Res.end(), P.second.begin(), P.second.end());
- return Res;
+ if (ver.isExternCpp) {
+ for (auto &p : getDemangledSyms())
+ if (m.match(p.first()))
+ res.insert(res.end(), p.second.begin(), p.second.end());
+ return res;
}
- for (Symbol *Sym : SymVector)
- if (Sym->isDefined() && M.match(Sym->getName()))
- Res.push_back(Sym);
- return Res;
-}
-
-// If there's only one anonymous version definition in a version
-// script file, the script does not actually define any symbol version,
-// but just specifies symbols visibilities.
-void SymbolTable::handleAnonymousVersion() {
- for (SymbolVersion &Ver : Config->VersionScriptGlobals)
- assignExactVersion(Ver, VER_NDX_GLOBAL, "global");
- for (SymbolVersion &Ver : Config->VersionScriptGlobals)
- assignWildcardVersion(Ver, VER_NDX_GLOBAL);
- for (SymbolVersion &Ver : Config->VersionScriptLocals)
- assignExactVersion(Ver, VER_NDX_LOCAL, "local");
- for (SymbolVersion &Ver : Config->VersionScriptLocals)
- assignWildcardVersion(Ver, VER_NDX_LOCAL);
+ for (Symbol *sym : symVector)
+ if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName()))
+ res.push_back(sym);
+ return res;
}
// Handles -dynamic-list.
void SymbolTable::handleDynamicList() {
- for (SymbolVersion &Ver : Config->DynamicList) {
- std::vector<Symbol *> Syms;
- if (Ver.HasWildcard)
- Syms = findAllByVersion(Ver);
+ for (SymbolVersion &ver : config->dynamicList) {
+ std::vector<Symbol *> syms;
+ if (ver.hasWildcard)
+ syms = findAllByVersion(ver);
else
- Syms = findByVersion(Ver);
+ syms = findByVersion(ver);
- for (Symbol *B : Syms) {
- if (!Config->Shared)
- B->ExportDynamic = true;
- else if (B->includeInDynsym())
- B->IsPreemptible = true;
+ for (Symbol *b : syms) {
+ if (!config->shared)
+ b->exportDynamic = true;
+ else if (b->includeInDynsym())
+ b->isPreemptible = true;
}
}
}
// Set symbol versions to symbols. This function handles patterns
// containing no wildcard characters.
-void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
- StringRef VersionName) {
- if (Ver.HasWildcard)
+void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
+ StringRef versionName) {
+ if (ver.hasWildcard)
return;
// Get a list of symbols which we need to assign the version to.
- std::vector<Symbol *> Syms = findByVersion(Ver);
- if (Syms.empty()) {
- if (!Config->UndefinedVersion)
- error("version script assignment of '" + VersionName + "' to symbol '" +
- Ver.Name + "' failed: symbol not defined");
+ std::vector<Symbol *> syms = findByVersion(ver);
+ if (syms.empty()) {
+ if (!config->undefinedVersion)
+ error("version script assignment of '" + versionName + "' to symbol '" +
+ ver.name + "' failed: symbol not defined");
return;
}
+ auto getName = [](uint16_t ver) -> std::string {
+ if (ver == VER_NDX_LOCAL)
+ return "VER_NDX_LOCAL";
+ if (ver == VER_NDX_GLOBAL)
+ return "VER_NDX_GLOBAL";
+ return ("version '" + config->versionDefinitions[ver - 2].name + "'").str();
+ };
+
// Assign the version.
- for (Symbol *Sym : Syms) {
+ for (Symbol *sym : syms) {
// Skip symbols containing version info because symbol versions
// specified by symbol names take precedence over version scripts.
// See parseSymbolVersion().
- if (Sym->getName().contains('@'))
+ if (sym->getName().contains('@'))
+ continue;
+
+ if (sym->versionId == config->defaultSymbolVersion)
+ sym->versionId = versionId;
+ if (sym->versionId == versionId)
continue;
- if (Sym->VersionId != Config->DefaultSymbolVersion &&
- Sym->VersionId != VersionId)
- error("duplicate symbol '" + Ver.Name + "' in version script");
- Sym->VersionId = VersionId;
+ warn("attempt to reassign symbol '" + ver.name + "' of " +
+ getName(sym->versionId) + " to " + getName(versionId));
}
}
-void SymbolTable::assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId) {
- if (!Ver.HasWildcard)
+void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
+ if (!ver.hasWildcard)
return;
// Exact matching takes precendence over fuzzy matching,
// so we set a version to a symbol only if no version has been assigned
// to the symbol. This behavior is compatible with GNU.
- for (Symbol *B : findAllByVersion(Ver))
- if (B->VersionId == Config->DefaultSymbolVersion)
- B->VersionId = VersionId;
+ for (Symbol *b : findAllByVersion(ver))
+ if (b->versionId == config->defaultSymbolVersion)
+ b->versionId = versionId;
}
-// This function processes version scripts by updating VersionId
+// This function processes version scripts by updating the versionId
// member of symbols.
+// If there's only one anonymous version definition in a version
+// script file, the script does not actually define any symbol version,
+// but just specifies symbols visibilities.
void SymbolTable::scanVersionScript() {
- // Handle edge cases first.
- handleAnonymousVersion();
- handleDynamicList();
-
- // Now we have version definitions, so we need to set version ids to symbols.
- // Each version definition has a glob pattern, and all symbols that match
- // with the pattern get that version.
-
// First, we assign versions to exact matching symbols,
// i.e. version definitions not containing any glob meta-characters.
- for (VersionDefinition &V : Config->VersionDefinitions)
- for (SymbolVersion &Ver : V.Globals)
- assignExactVersion(Ver, V.Id, V.Name);
+ for (SymbolVersion &ver : config->versionScriptGlobals)
+ assignExactVersion(ver, VER_NDX_GLOBAL, "global");
+ for (SymbolVersion &ver : config->versionScriptLocals)
+ assignExactVersion(ver, VER_NDX_LOCAL, "local");
+ for (VersionDefinition &v : config->versionDefinitions)
+ for (SymbolVersion &ver : v.globals)
+ assignExactVersion(ver, v.id, v.name);
// Next, we assign versions to fuzzy matching symbols,
// i.e. version definitions containing glob meta-characters.
+ for (SymbolVersion &ver : config->versionScriptGlobals)
+ assignWildcardVersion(ver, VER_NDX_GLOBAL);
+ for (SymbolVersion &ver : config->versionScriptLocals)
+ assignWildcardVersion(ver, VER_NDX_LOCAL);
+
// Note that because the last match takes precedence over previous matches,
// we iterate over the definitions in the reverse order.
- for (VersionDefinition &V : llvm::reverse(Config->VersionDefinitions))
- for (SymbolVersion &Ver : V.Globals)
- assignWildcardVersion(Ver, V.Id);
+ for (VersionDefinition &v : llvm::reverse(config->versionDefinitions))
+ for (SymbolVersion &ver : v.globals)
+ assignWildcardVersion(ver, v.id);
// Symbol themselves might know their versions because symbols
// can contain versions in the form of <name>@<version>.
// Let them parse and update their names to exclude version suffix.
- for (Symbol *Sym : SymVector)
- Sym->parseSymbolVersion();
-}
+ for (Symbol *sym : symVector)
+ sym->parseSymbolVersion();
-template void SymbolTable::addFile<ELF32LE>(InputFile *);
-template void SymbolTable::addFile<ELF32BE>(InputFile *);
-template void SymbolTable::addFile<ELF64LE>(InputFile *);
-template void SymbolTable::addFile<ELF64BE>(InputFile *);
-
-template Symbol *SymbolTable::addUndefined<ELF32LE>(StringRef, uint8_t, uint8_t,
- uint8_t, bool, InputFile *);
-template Symbol *SymbolTable::addUndefined<ELF32BE>(StringRef, uint8_t, uint8_t,
- uint8_t, bool, InputFile *);
-template Symbol *SymbolTable::addUndefined<ELF64LE>(StringRef, uint8_t, uint8_t,
- uint8_t, bool, InputFile *);
-template Symbol *SymbolTable::addUndefined<ELF64BE>(StringRef, uint8_t, uint8_t,
- uint8_t, bool, InputFile *);
-
-template void SymbolTable::addCombinedLTOObject<ELF32LE>();
-template void SymbolTable::addCombinedLTOObject<ELF32BE>();
-template void SymbolTable::addCombinedLTOObject<ELF64LE>();
-template void SymbolTable::addCombinedLTOObject<ELF64BE>();
-
-template void
-SymbolTable::addLazyArchive<ELF32LE>(StringRef, ArchiveFile &,
- const object::Archive::Symbol);
-template void
-SymbolTable::addLazyArchive<ELF32BE>(StringRef, ArchiveFile &,
- const object::Archive::Symbol);
-template void
-SymbolTable::addLazyArchive<ELF64LE>(StringRef, ArchiveFile &,
- const object::Archive::Symbol);
-template void
-SymbolTable::addLazyArchive<ELF64BE>(StringRef, ArchiveFile &,
- const object::Archive::Symbol);
-
-template void SymbolTable::addLazyObject<ELF32LE>(StringRef, LazyObjFile &);
-template void SymbolTable::addLazyObject<ELF32BE>(StringRef, LazyObjFile &);
-template void SymbolTable::addLazyObject<ELF64LE>(StringRef, LazyObjFile &);
-template void SymbolTable::addLazyObject<ELF64BE>(StringRef, LazyObjFile &);
-
-template void SymbolTable::fetchLazy<ELF32LE>(Symbol *);
-template void SymbolTable::fetchLazy<ELF32BE>(Symbol *);
-template void SymbolTable::fetchLazy<ELF64LE>(Symbol *);
-template void SymbolTable::fetchLazy<ELF64BE>(Symbol *);
-
-template void SymbolTable::addShared<ELF32LE>(StringRef, SharedFile<ELF32LE> &,
- const typename ELF32LE::Sym &,
- uint32_t Alignment, uint32_t);
-template void SymbolTable::addShared<ELF32BE>(StringRef, SharedFile<ELF32BE> &,
- const typename ELF32BE::Sym &,
- uint32_t Alignment, uint32_t);
-template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> &,
- const typename ELF64LE::Sym &,
- uint32_t Alignment, uint32_t);
-template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> &,
- const typename ELF64BE::Sym &,
- uint32_t Alignment, uint32_t);
+ // isPreemptible is false at this point. To correctly compute the binding of a
+ // Defined (which is used by includeInDynsym()), we need to know if it is
+ // VER_NDX_LOCAL or not. If defaultSymbolVersion is VER_NDX_LOCAL, we should
+ // compute symbol versions before handling --dynamic-list.
+ handleDynamicList();
+}
diff --git a/ELF/SymbolTable.h b/ELF/SymbolTable.h
index 898185fc96126..b64707f4ab02c 100644
--- a/ELF/SymbolTable.h
+++ b/ELF/SymbolTable.h
@@ -1,9 +1,8 @@
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -11,15 +10,14 @@
#define LLD_ELF_SYMBOL_TABLE_H
#include "InputFiles.h"
-#include "LTO.h"
+#include "Symbols.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
namespace lld {
namespace elf {
-class Defined;
-class SectionBase;
// SymbolTable is a bucket of all known symbols, including defined,
// undefined, or lazy symbols (the last one is symbols in archive
@@ -35,62 +33,40 @@ class SectionBase;
// is one add* function per symbol type.
class SymbolTable {
public:
- template <class ELFT> void addFile(InputFile *File);
- template <class ELFT> void addCombinedLTOObject();
- void wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap);
-
- ArrayRef<Symbol *> getSymbols() const { return SymVector; }
-
- template <class ELFT>
- Symbol *addUndefined(StringRef Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, bool CanOmitFromDynSym, InputFile *File);
-
- Defined *addDefined(StringRef Name, uint8_t StOther, uint8_t Type,
- uint64_t Value, uint64_t Size, uint8_t Binding,
- SectionBase *Section, InputFile *File);
-
- template <class ELFT>
- void addShared(StringRef Name, SharedFile<ELFT> &F,
- const typename ELFT::Sym &Sym, uint32_t Alignment,
- uint32_t VerdefIndex);
-
- template <class ELFT>
- void addLazyArchive(StringRef Name, ArchiveFile &F,
- const llvm::object::Archive::Symbol S);
+ void wrap(Symbol *sym, Symbol *real, Symbol *wrap);
- template <class ELFT> void addLazyObject(StringRef Name, LazyObjFile &Obj);
+ void forEachSymbol(llvm::function_ref<void(Symbol *)> fn) {
+ for (Symbol *sym : symVector)
+ if (!sym->isPlaceholder())
+ fn(sym);
+ }
- Symbol *addBitcode(StringRef Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, bool CanOmitFromDynSym, BitcodeFile &File);
+ Symbol *insert(StringRef name);
- Symbol *addCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
- uint8_t Binding, uint8_t StOther, uint8_t Type,
- InputFile &File);
-
- std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Visibility,
- bool CanOmitFromDynSym, InputFile *File);
-
- template <class ELFT> void fetchLazy(Symbol *Sym);
+ Symbol *addSymbol(const Symbol &New);
void scanVersionScript();
- Symbol *find(StringRef Name);
-
- void trace(StringRef Name);
+ Symbol *find(StringRef name);
void handleDynamicList();
-private:
- std::pair<Symbol *, bool> insertName(StringRef Name);
+ // Set of .so files to not link the same shared object file more than once.
+ llvm::DenseMap<StringRef, SharedFile *> soNames;
- std::vector<Symbol *> findByVersion(SymbolVersion Ver);
- std::vector<Symbol *> findAllByVersion(SymbolVersion Ver);
+ // Comdat groups define "link once" sections. If two comdat groups have the
+ // same name, only one of them is linked, and the other is ignored. This map
+ // is used to uniquify them.
+ llvm::DenseMap<llvm::CachedHashStringRef, const InputFile *> comdatGroups;
+
+private:
+ std::vector<Symbol *> findByVersion(SymbolVersion ver);
+ std::vector<Symbol *> findAllByVersion(SymbolVersion ver);
llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms();
- void handleAnonymousVersion();
- void assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
- StringRef VersionName);
- void assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId);
+ void assignExactVersion(SymbolVersion ver, uint16_t versionId,
+ StringRef versionName);
+ void assignWildcardVersion(SymbolVersion ver, uint16_t versionId);
// The order the global symbols are in is not defined. We can use an arbitrary
// order, but it has to be reproducible. That is true even when cross linking.
@@ -99,28 +75,18 @@ private:
// but a bit inefficient.
// FIXME: Experiment with passing in a custom hashing or sorting the symbols
// once symbol resolution is finished.
- llvm::DenseMap<llvm::CachedHashStringRef, int> SymMap;
- std::vector<Symbol *> SymVector;
-
- // Comdat groups define "link once" sections. If two comdat groups have the
- // same name, only one of them is linked, and the other is ignored. This set
- // is used to uniquify them.
- llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups;
-
- // Set of .so files to not link the same shared object file more than once.
- llvm::DenseMap<StringRef, InputFile *> SoNames;
+ llvm::DenseMap<llvm::CachedHashStringRef, int> symMap;
+ std::vector<Symbol *> symVector;
// A map from demangled symbol names to their symbol objects.
// This mapping is 1:N because two symbols with different versions
// can have the same name. We use this map to handle "extern C++ {}"
// directive in version scripts.
- llvm::Optional<llvm::StringMap<std::vector<Symbol *>>> DemangledSyms;
-
- // For LTO.
- std::unique_ptr<BitcodeCompiler> LTO;
+ llvm::Optional<llvm::StringMap<std::vector<Symbol *>>> demangledSyms;
};
-extern SymbolTable *Symtab;
+extern SymbolTable *symtab;
+
} // namespace elf
} // namespace lld
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index a713ec539d826..62c552e048287 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -1,9 +1,8 @@
//===- Symbols.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -27,40 +26,36 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-Defined *ElfSym::Bss;
-Defined *ElfSym::Etext1;
-Defined *ElfSym::Etext2;
-Defined *ElfSym::Edata1;
-Defined *ElfSym::Edata2;
-Defined *ElfSym::End1;
-Defined *ElfSym::End2;
-Defined *ElfSym::GlobalOffsetTable;
-Defined *ElfSym::MipsGp;
-Defined *ElfSym::MipsGpDisp;
-Defined *ElfSym::MipsLocalGp;
-Defined *ElfSym::RelaIpltStart;
-Defined *ElfSym::RelaIpltEnd;
-
-static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
- switch (Sym.kind()) {
+Defined *ElfSym::bss;
+Defined *ElfSym::etext1;
+Defined *ElfSym::etext2;
+Defined *ElfSym::edata1;
+Defined *ElfSym::edata2;
+Defined *ElfSym::end1;
+Defined *ElfSym::end2;
+Defined *ElfSym::globalOffsetTable;
+Defined *ElfSym::mipsGp;
+Defined *ElfSym::mipsGpDisp;
+Defined *ElfSym::mipsLocalGp;
+Defined *ElfSym::relaIpltStart;
+Defined *ElfSym::relaIpltEnd;
+Defined *ElfSym::riscvGlobalPointer;
+Defined *ElfSym::tlsModuleBase;
+
+static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
+ switch (sym.kind()) {
case Symbol::DefinedKind: {
- auto &D = cast<Defined>(Sym);
- SectionBase *IS = D.Section;
-
- // According to the ELF spec reference to a local symbol from outside
- // the group are not allowed. Unfortunately .eh_frame breaks that rule
- // and must be treated specially. For now we just replace the symbol with
- // 0.
- if (IS == &InputSection::Discarded)
- return 0;
+ auto &d = cast<Defined>(sym);
+ SectionBase *isec = d.section;
// This is an absolute symbol.
- if (!IS)
- return D.Value;
+ if (!isec)
+ return d.value;
- IS = IS->Repl;
+ assert(isec != &InputSection::discarded);
+ isec = isec->repl;
- uint64_t Offset = D.Value;
+ uint64_t offset = d.value;
// An object in an SHF_MERGE section might be referenced via a
// section symbol (as a hack for reducing the number of local
@@ -73,9 +68,9 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
// To make this work, we incorporate the addend into the section
// offset (and zero out the addend for later processing) so that
// we find the right object in the section.
- if (D.isSection()) {
- Offset += Addend;
- Addend = 0;
+ if (d.isSection()) {
+ offset += addend;
+ addend = 0;
}
// In the typical case, this is actually very simple and boils
@@ -88,83 +83,107 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
// If you understand the data structures involved with this next
// line (and how they get built), then you have a pretty good
// understanding of the linker.
- uint64_t VA = IS->getVA(Offset);
-
- if (D.isTls() && !Config->Relocatable) {
+ uint64_t va = isec->getVA(offset);
+
+ // MIPS relocatable files can mix regular and microMIPS code.
+ // Linker needs to distinguish such code. To do so microMIPS
+ // symbols has the `STO_MIPS_MICROMIPS` flag in the `st_other`
+ // field. Unfortunately, the `MIPS::relocateOne()` method has
+ // a symbol value only. To pass type of the symbol (regular/microMIPS)
+ // to that routine as well as other places where we write
+ // a symbol value as-is (.dynamic section, `Elf_Ehdr::e_entry`
+ // field etc) do the same trick as compiler uses to mark microMIPS
+ // for CPU - set the less-significant bit.
+ if (config->emachine == EM_MIPS && isMicroMips() &&
+ ((sym.stOther & STO_MIPS_MICROMIPS) || sym.needsPltAddr))
+ va |= 1;
+
+ if (d.isTls() && !config->relocatable) {
// Use the address of the TLS segment's first section rather than the
// segment's address, because segment addresses aren't initialized until
// after sections are finalized. (e.g. Measuring the size of .rela.dyn
// for Android relocation packing requires knowing TLS symbol addresses
// during section finalization.)
- if (!Out::TlsPhdr || !Out::TlsPhdr->FirstSec)
- fatal(toString(D.File) +
+ if (!Out::tlsPhdr || !Out::tlsPhdr->firstSec)
+ fatal(toString(d.file) +
" has an STT_TLS symbol but doesn't have an SHF_TLS section");
- return VA - Out::TlsPhdr->FirstSec->Addr;
+ return va - Out::tlsPhdr->firstSec->addr;
}
- return VA;
+ return va;
}
case Symbol::SharedKind:
case Symbol::UndefinedKind:
return 0;
case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
- assert(Sym.IsUsedInRegularObj && "lazy symbol reached writer");
+ assert(sym.isUsedInRegularObj && "lazy symbol reached writer");
return 0;
+ case Symbol::CommonKind:
+ llvm_unreachable("common symbol reached writer");
case Symbol::PlaceholderKind:
llvm_unreachable("placeholder symbol reached writer");
}
llvm_unreachable("invalid symbol kind");
}
-uint64_t Symbol::getVA(int64_t Addend) const {
- uint64_t OutVA = getSymVA(*this, Addend);
- return OutVA + Addend;
+uint64_t Symbol::getVA(int64_t addend) const {
+ uint64_t outVA = getSymVA(*this, addend);
+ return outVA + addend;
}
-uint64_t Symbol::getGotVA() const { return In.Got->getVA() + getGotOffset(); }
-
-uint64_t Symbol::getGotOffset() const {
- return GotIndex * Target->GotEntrySize;
+uint64_t Symbol::getGotVA() const {
+ if (gotInIgot)
+ return in.igotPlt->getVA() + getGotPltOffset();
+ return in.got->getVA() + getGotOffset();
}
+uint64_t Symbol::getGotOffset() const { return gotIndex * config->wordsize; }
+
uint64_t Symbol::getGotPltVA() const {
- if (this->IsInIgot)
- return In.IgotPlt->getVA() + getGotPltOffset();
- return In.GotPlt->getVA() + getGotPltOffset();
+ if (isInIplt)
+ return in.igotPlt->getVA() + getGotPltOffset();
+ return in.gotPlt->getVA() + getGotPltOffset();
}
uint64_t Symbol::getGotPltOffset() const {
- if (IsInIgot)
- return PltIndex * Target->GotPltEntrySize;
- return (PltIndex + Target->GotPltHeaderEntriesNum) * Target->GotPltEntrySize;
+ if (isInIplt)
+ return pltIndex * config->wordsize;
+ return (pltIndex + target->gotPltHeaderEntriesNum) * config->wordsize;
}
uint64_t Symbol::getPPC64LongBranchOffset() const {
- assert(PPC64BranchltIndex != 0xffff);
- return PPC64BranchltIndex * Target->GotPltEntrySize;
+ assert(ppc64BranchltIndex != 0xffff);
+ return ppc64BranchltIndex * config->wordsize;
}
uint64_t Symbol::getPltVA() const {
- PltSection *Plt = IsInIplt ? In.Iplt : In.Plt;
- return Plt->getVA() + Plt->HeaderSize + PltIndex * Target->PltEntrySize;
+ PltSection *plt = isInIplt ? in.iplt : in.plt;
+ uint64_t outVA =
+ plt->getVA() + plt->headerSize + pltIndex * target->pltEntrySize;
+ // While linking microMIPS code PLT code are always microMIPS
+ // code. Set the less-significant bit to track that fact.
+ // See detailed comment in the `getSymVA` function.
+ if (config->emachine == EM_MIPS && isMicroMips())
+ outVA |= 1;
+ return outVA;
}
uint64_t Symbol::getPPC64LongBranchTableVA() const {
- assert(PPC64BranchltIndex != 0xffff);
- return In.PPC64LongBranchTarget->getVA() +
- PPC64BranchltIndex * Target->GotPltEntrySize;
+ assert(ppc64BranchltIndex != 0xffff);
+ return in.ppc64LongBranchTarget->getVA() +
+ ppc64BranchltIndex * config->wordsize;
}
uint64_t Symbol::getSize() const {
- if (const auto *DR = dyn_cast<Defined>(this))
- return DR->Size;
- return cast<SharedSymbol>(this)->Size;
+ if (const auto *dr = dyn_cast<Defined>(this))
+ return dr->size;
+ return cast<SharedSymbol>(this)->size;
}
OutputSection *Symbol::getOutputSection() const {
- if (auto *S = dyn_cast<Defined>(this)) {
- if (auto *Sec = S->Section)
- return Sec->Repl->getOutputSection();
+ if (auto *s = dyn_cast<Defined>(this)) {
+ if (auto *sec = s->section)
+ return sec->repl->getOutputSection();
return nullptr;
}
return nullptr;
@@ -173,16 +192,16 @@ OutputSection *Symbol::getOutputSection() const {
// If a symbol name contains '@', the characters after that is
// a symbol version name. This function parses that.
void Symbol::parseSymbolVersion() {
- StringRef S = getName();
- size_t Pos = S.find('@');
- if (Pos == 0 || Pos == StringRef::npos)
+ StringRef s = getName();
+ size_t pos = s.find('@');
+ if (pos == 0 || pos == StringRef::npos)
return;
- StringRef Verstr = S.substr(Pos + 1);
- if (Verstr.empty())
+ StringRef verstr = s.substr(pos + 1);
+ if (verstr.empty())
return;
// Truncate the symbol name so that it doesn't include the version string.
- NameSize = Pos;
+ nameSize = pos;
// If this is not in this DSO, it is not a definition.
if (!isDefined())
@@ -190,18 +209,18 @@ void Symbol::parseSymbolVersion() {
// '@@' in a symbol name means the default version.
// It is usually the most recent one.
- bool IsDefault = (Verstr[0] == '@');
- if (IsDefault)
- Verstr = Verstr.substr(1);
+ bool isDefault = (verstr[0] == '@');
+ if (isDefault)
+ verstr = verstr.substr(1);
- for (VersionDefinition &Ver : Config->VersionDefinitions) {
- if (Ver.Name != Verstr)
+ for (VersionDefinition &ver : config->versionDefinitions) {
+ if (ver.name != verstr)
continue;
- if (IsDefault)
- VersionId = Ver.Id;
+ if (isDefault)
+ versionId = ver.id;
else
- VersionId = Ver.Id | VERSYM_HIDDEN;
+ versionId = ver.id | VERSYM_HIDDEN;
return;
}
@@ -211,63 +230,79 @@ void Symbol::parseSymbolVersion() {
// so we do not report error in this case. We also do not error
// if the symbol has a local version as it won't be in the dynamic
// symbol table.
- if (Config->Shared && VersionId != VER_NDX_LOCAL)
- error(toString(File) + ": symbol " + S + " has undefined version " +
- Verstr);
+ if (config->shared && versionId != VER_NDX_LOCAL)
+ error(toString(file) + ": symbol " + s + " has undefined version " +
+ verstr);
}
-InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); }
+void Symbol::fetch() const {
+ if (auto *sym = dyn_cast<LazyArchive>(this)) {
+ cast<ArchiveFile>(sym->file)->fetch(sym->sym);
+ return;
+ }
+
+ if (auto *sym = dyn_cast<LazyObject>(this)) {
+ dyn_cast<LazyObjFile>(sym->file)->fetch();
+ return;
+ }
+
+ llvm_unreachable("Symbol::fetch() is called on a non-lazy symbol");
+}
MemoryBufferRef LazyArchive::getMemberBuffer() {
- Archive::Child C = CHECK(
- Sym.getMember(), "could not get the member for symbol " + Sym.getName());
+ Archive::Child c = CHECK(
+ sym.getMember(), "could not get the member for symbol " + sym.getName());
- return CHECK(C.getMemoryBufferRef(),
+ return CHECK(c.getMemoryBufferRef(),
"could not get the buffer for the member defining symbol " +
- Sym.getName());
+ sym.getName());
}
uint8_t Symbol::computeBinding() const {
- if (Config->Relocatable)
- return Binding;
- if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
+ if (config->relocatable)
+ return binding;
+ if (visibility != STV_DEFAULT && visibility != STV_PROTECTED)
return STB_LOCAL;
- if (VersionId == VER_NDX_LOCAL && isDefined() && !IsPreemptible)
+ if (versionId == VER_NDX_LOCAL && isDefined() && !isPreemptible)
return STB_LOCAL;
- if (!Config->GnuUnique && Binding == STB_GNU_UNIQUE)
+ if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
return STB_GLOBAL;
- return Binding;
+ return binding;
}
bool Symbol::includeInDynsym() const {
- if (!Config->HasDynSymTab)
+ if (!config->hasDynSymTab)
return false;
if (computeBinding() == STB_LOCAL)
return false;
- if (!isDefined())
- return true;
- return ExportDynamic;
+
+ // If a PIE binary was not linked against any shared libraries, then we can
+ // safely drop weak undef symbols from .dynsym.
+ if (isUndefWeak() && config->pie && sharedFiles.empty())
+ return false;
+
+ return isUndefined() || isShared() || exportDynamic;
}
// Print out a log message for --trace-symbol.
-void elf::printTraceSymbol(Symbol *Sym) {
- std::string S;
- if (Sym->isUndefined())
- S = ": reference to ";
- else if (Sym->isLazy())
- S = ": lazy definition of ";
- else if (Sym->isShared())
- S = ": shared definition of ";
- else if (dyn_cast_or_null<BssSection>(cast<Defined>(Sym)->Section))
- S = ": common definition of ";
+void elf::printTraceSymbol(const Symbol *sym) {
+ std::string s;
+ if (sym->isUndefined())
+ s = ": reference to ";
+ else if (sym->isLazy())
+ s = ": lazy definition of ";
+ else if (sym->isShared())
+ s = ": shared definition of ";
+ else if (sym->isCommon())
+ s = ": common definition of ";
else
- S = ": definition of ";
+ s = ": definition of ";
- message(toString(Sym->File) + S + Sym->getName());
+ message(toString(sym->file) + s + sym->getName());
}
-void elf::maybeWarnUnorderableSymbol(const Symbol *Sym) {
- if (!Config->WarnSymbolOrdering)
+void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
+ if (!config->warnSymbolOrdering)
return;
// If UnresolvedPolicy::Ignore is used, no "undefined symbol" error/warning
@@ -275,31 +310,347 @@ void elf::maybeWarnUnorderableSymbol(const Symbol *Sym) {
//
// Note, ld.bfd --symbol-ordering-file= does not warn on undefined symbols,
// but we don't have to be compatible here.
- if (Sym->isUndefined() &&
- Config->UnresolvedSymbols == UnresolvedPolicy::Ignore)
+ if (sym->isUndefined() &&
+ config->unresolvedSymbols == UnresolvedPolicy::Ignore)
return;
- const InputFile *File = Sym->File;
- auto *D = dyn_cast<Defined>(Sym);
-
- auto Warn = [&](StringRef S) { warn(toString(File) + S + Sym->getName()); };
-
- if (Sym->isUndefined())
- Warn(": unable to order undefined symbol: ");
- else if (Sym->isShared())
- Warn(": unable to order shared symbol: ");
- else if (D && !D->Section)
- Warn(": unable to order absolute symbol: ");
- else if (D && isa<OutputSection>(D->Section))
- Warn(": unable to order synthetic symbol: ");
- else if (D && !D->Section->Repl->Live)
- Warn(": unable to order discarded symbol: ");
+ const InputFile *file = sym->file;
+ auto *d = dyn_cast<Defined>(sym);
+
+ auto report = [&](StringRef s) { warn(toString(file) + s + sym->getName()); };
+
+ if (sym->isUndefined())
+ report(": unable to order undefined symbol: ");
+ else if (sym->isShared())
+ report(": unable to order shared symbol: ");
+ else if (d && !d->section)
+ report(": unable to order absolute symbol: ");
+ else if (d && isa<OutputSection>(d->section))
+ report(": unable to order synthetic symbol: ");
+ else if (d && !d->section->repl->isLive())
+ report(": unable to order discarded symbol: ");
}
// Returns a symbol for an error message.
-std::string lld::toString(const Symbol &B) {
- if (Config->Demangle)
- if (Optional<std::string> S = demangleItanium(B.getName()))
- return *S;
- return B.getName();
+std::string lld::toString(const Symbol &b) {
+ if (config->demangle)
+ if (Optional<std::string> s = demangleItanium(b.getName()))
+ return *s;
+ return b.getName();
+}
+
+static uint8_t getMinVisibility(uint8_t va, uint8_t vb) {
+ if (va == STV_DEFAULT)
+ return vb;
+ if (vb == STV_DEFAULT)
+ return va;
+ return std::min(va, vb);
+}
+
+// Merge symbol properties.
+//
+// When we have many symbols of the same name, we choose one of them,
+// and that's the result of symbol resolution. However, symbols that
+// were not chosen still affect some symbol properties.
+void Symbol::mergeProperties(const Symbol &other) {
+ if (other.exportDynamic)
+ exportDynamic = true;
+ if (other.isUsedInRegularObj)
+ isUsedInRegularObj = true;
+
+ // DSO symbols do not affect visibility in the output.
+ if (!other.isShared())
+ visibility = getMinVisibility(visibility, other.visibility);
+}
+
+void Symbol::resolve(const Symbol &other) {
+ mergeProperties(other);
+
+ if (isPlaceholder()) {
+ replace(other);
+ return;
+ }
+
+ switch (other.kind()) {
+ case Symbol::UndefinedKind:
+ resolveUndefined(cast<Undefined>(other));
+ break;
+ case Symbol::CommonKind:
+ resolveCommon(cast<CommonSymbol>(other));
+ break;
+ case Symbol::DefinedKind:
+ resolveDefined(cast<Defined>(other));
+ break;
+ case Symbol::LazyArchiveKind:
+ resolveLazy(cast<LazyArchive>(other));
+ break;
+ case Symbol::LazyObjectKind:
+ resolveLazy(cast<LazyObject>(other));
+ break;
+ case Symbol::SharedKind:
+ resolveShared(cast<SharedSymbol>(other));
+ break;
+ case Symbol::PlaceholderKind:
+ llvm_unreachable("bad symbol kind");
+ }
+}
+
+void Symbol::resolveUndefined(const Undefined &other) {
+ // An undefined symbol with non default visibility must be satisfied
+ // in the same DSO.
+ //
+ // If this is a non-weak defined symbol in a discarded section, override the
+ // existing undefined symbol for better error message later.
+ if ((isShared() && other.visibility != STV_DEFAULT) ||
+ (isUndefined() && other.binding != STB_WEAK && other.discardedSecIdx)) {
+ replace(other);
+ return;
+ }
+
+ if (traced)
+ printTraceSymbol(&other);
+
+ if (isLazy()) {
+ // An undefined weak will not fetch archive members. See comment on Lazy in
+ // Symbols.h for the details.
+ if (other.binding == STB_WEAK) {
+ binding = STB_WEAK;
+ type = other.type;
+ return;
+ }
+
+ // Do extra check for --warn-backrefs.
+ //
+ // --warn-backrefs is an option to prevent an undefined reference from
+ // fetching an archive member written earlier in the command line. It can be
+ // used to keep compatibility with GNU linkers to some degree.
+ // I'll explain the feature and why you may find it useful in this comment.
+ //
+ // lld's symbol resolution semantics is more relaxed than traditional Unix
+ // linkers. For example,
+ //
+ // ld.lld foo.a bar.o
+ //
+ // succeeds even if bar.o contains an undefined symbol that has to be
+ // resolved by some object file in foo.a. Traditional Unix linkers don't
+ // allow this kind of backward reference, as they visit each file only once
+ // from left to right in the command line while resolving all undefined
+ // symbols at the moment of visiting.
+ //
+ // In the above case, since there's no undefined symbol when a linker visits
+ // foo.a, no files are pulled out from foo.a, and because the linker forgets
+ // about foo.a after visiting, it can't resolve undefined symbols in bar.o
+ // that could have been resolved otherwise.
+ //
+ // That lld accepts more relaxed form means that (besides it'd make more
+ // sense) you can accidentally write a command line or a build file that
+ // works only with lld, even if you have a plan to distribute it to wider
+ // users who may be using GNU linkers. With --warn-backrefs, you can detect
+ // a library order that doesn't work with other Unix linkers.
+ //
+ // The option is also useful to detect cyclic dependencies between static
+ // archives. Again, lld accepts
+ //
+ // ld.lld foo.a bar.a
+ //
+ // even if foo.a and bar.a depend on each other. With --warn-backrefs, it is
+ // handled as an error.
+ //
+ // Here is how the option works. We assign a group ID to each file. A file
+ // with a smaller group ID can pull out object files from an archive file
+ // with an equal or greater group ID. Otherwise, it is a reverse dependency
+ // and an error.
+ //
+ // A file outside --{start,end}-group gets a fresh ID when instantiated. All
+ // files within the same --{start,end}-group get the same group ID. E.g.
+ //
+ // ld.lld A B --start-group C D --end-group E
+ //
+ // A forms group 0. B form group 1. C and D (including their member object
+ // files) form group 2. E forms group 3. I think that you can see how this
+ // group assignment rule simulates the traditional linker's semantics.
+ bool backref = config->warnBackrefs && other.file &&
+ file->groupId < other.file->groupId;
+ fetch();
+
+ // We don't report backward references to weak symbols as they can be
+ // overridden later.
+ if (backref && !isWeak())
+ warn("backward reference detected: " + other.getName() + " in " +
+ toString(other.file) + " refers to " + toString(file));
+ return;
+ }
+
+ // Undefined symbols in a SharedFile do not change the binding.
+ if (dyn_cast_or_null<SharedFile>(other.file))
+ return;
+
+ if (isUndefined()) {
+ // The binding may "upgrade" from weak to non-weak.
+ if (other.binding != STB_WEAK)
+ binding = other.binding;
+ } else if (auto *s = dyn_cast<SharedSymbol>(this)) {
+ // The binding of a SharedSymbol will be weak if there is at least one
+ // reference and all are weak. The binding has one opportunity to change to
+ // weak: if the first reference is weak.
+ if (other.binding != STB_WEAK || !s->referenced)
+ binding = other.binding;
+ s->referenced = true;
+ }
+}
+
+// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and
+// foo@@VER. We want to effectively ignore foo, so give precedence to
+// foo@@VER.
+// FIXME: If users can transition to using
+// .symver foo,foo@@@VER
+// we can delete this hack.
+static int compareVersion(StringRef a, StringRef b) {
+ bool x = a.contains("@@");
+ bool y = b.contains("@@");
+ if (!x && y)
+ return 1;
+ if (x && !y)
+ return -1;
+ return 0;
+}
+
+// Compare two symbols. Return 1 if the new symbol should win, -1 if
+// the new symbol should lose, or 0 if there is a conflict.
+int Symbol::compare(const Symbol *other) const {
+ assert(other->isDefined() || other->isCommon());
+
+ if (!isDefined() && !isCommon())
+ return 1;
+
+ if (int cmp = compareVersion(getName(), other->getName()))
+ return cmp;
+
+ if (other->isWeak())
+ return -1;
+
+ if (isWeak())
+ return 1;
+
+ if (isCommon() && other->isCommon()) {
+ if (config->warnCommon)
+ warn("multiple common of " + getName());
+ return 0;
+ }
+
+ if (isCommon()) {
+ if (config->warnCommon)
+ warn("common " + getName() + " is overridden");
+ return 1;
+ }
+
+ if (other->isCommon()) {
+ if (config->warnCommon)
+ warn("common " + getName() + " is overridden");
+ return -1;
+ }
+
+ auto *oldSym = cast<Defined>(this);
+ auto *newSym = cast<Defined>(other);
+
+ if (other->file && isa<BitcodeFile>(other->file))
+ return 0;
+
+ if (!oldSym->section && !newSym->section && oldSym->value == newSym->value &&
+ newSym->binding == STB_GLOBAL)
+ return -1;
+
+ return 0;
+}
+
+static void reportDuplicate(Symbol *sym, InputFile *newFile,
+ InputSectionBase *errSec, uint64_t errOffset) {
+ if (config->allowMultipleDefinition)
+ return;
+
+ Defined *d = cast<Defined>(sym);
+ if (!d->section || !errSec) {
+ error("duplicate symbol: " + toString(*sym) + "\n>>> defined in " +
+ toString(sym->file) + "\n>>> defined in " + toString(newFile));
+ return;
+ }
+
+ // Construct and print an error message in the form of:
+ //
+ // ld.lld: error: duplicate symbol: foo
+ // >>> defined at bar.c:30
+ // >>> bar.o (/home/alice/src/bar.o)
+ // >>> defined at baz.c:563
+ // >>> baz.o in archive libbaz.a
+ auto *sec1 = cast<InputSectionBase>(d->section);
+ std::string src1 = sec1->getSrcMsg(*sym, d->value);
+ std::string obj1 = sec1->getObjMsg(d->value);
+ std::string src2 = errSec->getSrcMsg(*sym, errOffset);
+ std::string obj2 = errSec->getObjMsg(errOffset);
+
+ std::string msg = "duplicate symbol: " + toString(*sym) + "\n>>> defined at ";
+ if (!src1.empty())
+ msg += src1 + "\n>>> ";
+ msg += obj1 + "\n>>> defined at ";
+ if (!src2.empty())
+ msg += src2 + "\n>>> ";
+ msg += obj2;
+ error(msg);
+}
+
+void Symbol::resolveCommon(const CommonSymbol &other) {
+ int cmp = compare(&other);
+ if (cmp < 0)
+ return;
+
+ if (cmp > 0) {
+ replace(other);
+ return;
+ }
+
+ CommonSymbol *oldSym = cast<CommonSymbol>(this);
+
+ oldSym->alignment = std::max(oldSym->alignment, other.alignment);
+ if (oldSym->size < other.size) {
+ oldSym->file = other.file;
+ oldSym->size = other.size;
+ }
+}
+
+void Symbol::resolveDefined(const Defined &other) {
+ int cmp = compare(&other);
+ if (cmp > 0)
+ replace(other);
+ else if (cmp == 0)
+ reportDuplicate(this, other.file,
+ dyn_cast_or_null<InputSectionBase>(other.section),
+ other.value);
+}
+
+template <class LazyT> void Symbol::resolveLazy(const LazyT &other) {
+ if (!isUndefined())
+ return;
+
+ // An undefined weak will not fetch archive members. See comment on Lazy in
+ // Symbols.h for the details.
+ if (isWeak()) {
+ uint8_t ty = type;
+ replace(other);
+ type = ty;
+ binding = STB_WEAK;
+ return;
+ }
+
+ other.fetch();
+}
+
+void Symbol::resolveShared(const SharedSymbol &other) {
+ if (visibility == STV_DEFAULT && (isUndefined() || isLazy())) {
+ // An undefined symbol with non default visibility must be satisfied
+ // in the same DSO.
+ uint8_t bind = binding;
+ replace(other);
+ binding = bind;
+ cast<SharedSymbol>(this)->referenced = true;
+ }
}
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index 4d55405d8936d..d640495b0e011 100644
--- a/ELF/Symbols.h
+++ b/ELF/Symbols.h
@@ -1,9 +1,8 @@
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -14,6 +13,7 @@
#ifndef LLD_ELF_SYMBOLS_H
#define LLD_ELF_SYMBOLS_H
+#include "InputFiles.h"
#include "InputSection.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Strings.h"
@@ -22,8 +22,14 @@
namespace lld {
namespace elf {
-class Symbol;
+class CommonSymbol;
+class Defined;
class InputFile;
+class LazyArchive;
+class LazyObject;
+class SharedSymbol;
+class Symbol;
+class Undefined;
} // namespace elf
std::string toString(const elf::Symbol &);
@@ -31,15 +37,6 @@ std::string toString(const elf::InputFile *);
namespace elf {
-class ArchiveFile;
-class BitcodeFile;
-class BssSection;
-class InputFile;
-class LazyObjFile;
-template <class ELFT> class ObjFile;
-class OutputSection;
-template <class ELFT> class SharedFile;
-
// This is a StringRef-like container that doesn't run strlen().
//
// ELF string tables contain a lot of null-terminated strings. Most of them
@@ -47,11 +44,11 @@ template <class ELFT> class SharedFile;
// and the linker doesn't use local symbol names for name resolution. So, we
// use this class to represents strings read from string tables.
struct StringRefZ {
- StringRefZ(const char *S) : Data(S), Size(-1) {}
- StringRefZ(StringRef S) : Data(S.data()), Size(S.size()) {}
+ StringRefZ(const char *s) : data(s), size(-1) {}
+ StringRefZ(StringRef s) : data(s.data()), size(s.size()) {}
- const char *Data;
- const uint32_t Size;
+ const char *data;
+ const uint32_t size;
};
// The base class for real symbol classes.
@@ -60,85 +57,91 @@ public:
enum Kind {
PlaceholderKind,
DefinedKind,
+ CommonKind,
SharedKind,
UndefinedKind,
LazyArchiveKind,
LazyObjectKind,
};
- Kind kind() const { return static_cast<Kind>(SymbolKind); }
+ Kind kind() const { return static_cast<Kind>(symbolKind); }
// The file from which this symbol was created.
- InputFile *File;
+ InputFile *file;
protected:
- const char *NameData;
- mutable uint32_t NameSize;
+ const char *nameData;
+ mutable uint32_t nameSize;
public:
- uint32_t DynsymIndex = 0;
- uint32_t GotIndex = -1;
- uint32_t PltIndex = -1;
+ uint32_t dynsymIndex = 0;
+ uint32_t gotIndex = -1;
+ uint32_t pltIndex = -1;
- uint32_t GlobalDynIndex = -1;
+ uint32_t globalDynIndex = -1;
// This field is a index to the symbol's version definition.
- uint32_t VerdefIndex = -1;
+ uint32_t verdefIndex = -1;
// Version definition index.
- uint16_t VersionId;
+ uint16_t versionId;
// An index into the .branch_lt section on PPC64.
- uint16_t PPC64BranchltIndex = -1;
+ uint16_t ppc64BranchltIndex = -1;
- // Symbol binding. This is not overwritten by replaceSymbol to track
+ // Symbol binding. This is not overwritten by replace() to track
// changes during resolution. In particular:
// - An undefined weak is still weak when it resolves to a shared library.
// - An undefined weak will not fetch archive members, but we have to
// remember it is weak.
- uint8_t Binding;
+ uint8_t binding;
// The following fields have the same meaning as the ELF symbol attributes.
- uint8_t Type; // symbol type
- uint8_t StOther; // st_other field value
+ uint8_t type; // symbol type
+ uint8_t stOther; // st_other field value
- uint8_t SymbolKind;
+ uint8_t symbolKind;
// Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols.
- unsigned Visibility : 2;
+ unsigned visibility : 2;
// True if the symbol was used for linking and thus need to be added to the
// output file's symbol table. This is true for all symbols except for
- // unreferenced DSO symbols and bitcode symbols that are unreferenced except
- // by other bitcode objects.
- unsigned IsUsedInRegularObj : 1;
+ // unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
+ // are unreferenced except by other bitcode objects.
+ unsigned isUsedInRegularObj : 1;
// If this flag is true and the symbol has protected or default visibility, it
// will appear in .dynsym. This flag is set by interposable DSO symbols in
// executables, by most symbols in DSOs and executables built with
// --export-dynamic, and by dynamic lists.
- unsigned ExportDynamic : 1;
+ unsigned exportDynamic : 1;
// False if LTO shouldn't inline whatever this symbol points to. If a symbol
// is overwritten after LTO, LTO shouldn't inline the symbol because it
// doesn't know the final contents of the symbol.
- unsigned CanInline : 1;
+ unsigned canInline : 1;
// True if this symbol is specified by --trace-symbol option.
- unsigned Traced : 1;
+ unsigned traced : 1;
+
+ inline void replace(const Symbol &New);
bool includeInDynsym() const;
uint8_t computeBinding() const;
- bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
+ bool isWeak() const { return binding == llvm::ELF::STB_WEAK; }
+
+ bool isUndefined() const { return symbolKind == UndefinedKind; }
+ bool isCommon() const { return symbolKind == CommonKind; }
+ bool isDefined() const { return symbolKind == DefinedKind; }
+ bool isShared() const { return symbolKind == SharedKind; }
+ bool isPlaceholder() const { return symbolKind == PlaceholderKind; }
- bool isUndefined() const { return SymbolKind == UndefinedKind; }
- bool isDefined() const { return SymbolKind == DefinedKind; }
- bool isShared() const { return SymbolKind == SharedKind; }
- bool isLocal() const { return Binding == llvm::ELF::STB_LOCAL; }
+ bool isLocal() const { return binding == llvm::ELF::STB_LOCAL; }
bool isLazy() const {
- return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
+ return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
// True if this is an undefined weak symbol. This only works once
@@ -149,23 +152,23 @@ public:
}
StringRef getName() const {
- if (NameSize == (uint32_t)-1)
- NameSize = strlen(NameData);
- return {NameData, NameSize};
+ if (nameSize == (uint32_t)-1)
+ nameSize = strlen(nameData);
+ return {nameData, nameSize};
}
- void setName(StringRef S) {
- NameData = S.data();
- NameSize = S.size();
+ void setName(StringRef s) {
+ nameData = s.data();
+ nameSize = s.size();
}
void parseSymbolVersion();
- bool isInGot() const { return GotIndex != -1U; }
- bool isInPlt() const { return PltIndex != -1U; }
- bool isInPPC64Branchlt() const { return PPC64BranchltIndex != 0xffff; }
+ bool isInGot() const { return gotIndex != -1U; }
+ bool isInPlt() const { return pltIndex != -1U; }
+ bool isInPPC64Branchlt() const { return ppc64BranchltIndex != 0xffff; }
- uint64_t getVA(int64_t Addend = 0) const;
+ uint64_t getVA(int64_t addend = 0) const;
uint64_t getGotOffset() const;
uint64_t getGotVA() const;
@@ -177,81 +180,164 @@ public:
uint64_t getSize() const;
OutputSection *getOutputSection() const;
+ // The following two functions are used for symbol resolution.
+ //
+ // You are expected to call mergeProperties for all symbols in input
+ // files so that attributes that are attached to names rather than
+ // indivisual symbol (such as visibility) are merged together.
+ //
+ // Every time you read a new symbol from an input, you are supposed
+ // to call resolve() with the new symbol. That function replaces
+ // "this" object as a result of name resolution if the new symbol is
+ // more appropriate to be included in the output.
+ //
+ // For example, if "this" is an undefined symbol and a new symbol is
+ // a defined symbol, "this" is replaced with the new symbol.
+ void mergeProperties(const Symbol &other);
+ void resolve(const Symbol &other);
+
+ // If this is a lazy symbol, fetch an input file and add the symbol
+ // in the file to the symbol table. Calling this function on
+ // non-lazy object causes a runtime error.
+ void fetch() const;
+
+private:
+ static bool isExportDynamic(Kind k, uint8_t visibility) {
+ if (k == SharedKind)
+ return visibility == llvm::ELF::STV_DEFAULT;
+ return config->shared || config->exportDynamic;
+ }
+
+ void resolveUndefined(const Undefined &other);
+ void resolveCommon(const CommonSymbol &other);
+ void resolveDefined(const Defined &other);
+ template <class LazyT> void resolveLazy(const LazyT &other);
+ void resolveShared(const SharedSymbol &other);
+
+ int compare(const Symbol *other) const;
+
+ inline size_t getSymbolSize() const;
+
protected:
- Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type)
- : File(File), NameData(Name.Data), NameSize(Name.Size), Binding(Binding),
- Type(Type), StOther(StOther), SymbolKind(K), NeedsPltAddr(false),
- IsInIplt(false), IsInIgot(false), IsPreemptible(false),
- Used(!Config->GcSections), NeedsTocRestore(false),
- ScriptDefined(false) {}
+ Symbol(Kind k, InputFile *file, StringRefZ name, uint8_t binding,
+ uint8_t stOther, uint8_t type)
+ : file(file), nameData(name.data), nameSize(name.size), binding(binding),
+ type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3),
+ isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind),
+ exportDynamic(isExportDynamic(k, visibility)), canInline(false),
+ traced(false), needsPltAddr(false), isInIplt(false), gotInIgot(false),
+ isPreemptible(false), used(!config->gcSections), needsTocRestore(false),
+ scriptDefined(false) {}
public:
// True the symbol should point to its PLT entry.
// For SharedSymbol only.
- unsigned NeedsPltAddr : 1;
+ unsigned needsPltAddr : 1;
- // True if this symbol is in the Iplt sub-section of the Plt.
- unsigned IsInIplt : 1;
+ // True if this symbol is in the Iplt sub-section of the Plt and the Igot
+ // sub-section of the .got.plt or .got.
+ unsigned isInIplt : 1;
- // True if this symbol is in the Igot sub-section of the .got.plt or .got.
- unsigned IsInIgot : 1;
+ // True if this symbol needs a GOT entry and its GOT entry is actually in
+ // Igot. This will be true only for certain non-preemptible ifuncs.
+ unsigned gotInIgot : 1;
// True if this symbol is preemptible at load time.
- unsigned IsPreemptible : 1;
+ unsigned isPreemptible : 1;
// True if an undefined or shared symbol is used from a live section.
- unsigned Used : 1;
+ unsigned used : 1;
// True if a call to this symbol needs to be followed by a restore of the
// PPC64 toc pointer.
- unsigned NeedsTocRestore : 1;
+ unsigned needsTocRestore : 1;
// True if this symbol is defined by a linker script.
- unsigned ScriptDefined : 1;
-
- bool isSection() const { return Type == llvm::ELF::STT_SECTION; }
- bool isTls() const { return Type == llvm::ELF::STT_TLS; }
- bool isFunc() const { return Type == llvm::ELF::STT_FUNC; }
- bool isGnuIFunc() const { return Type == llvm::ELF::STT_GNU_IFUNC; }
- bool isObject() const { return Type == llvm::ELF::STT_OBJECT; }
- bool isFile() const { return Type == llvm::ELF::STT_FILE; }
+ unsigned scriptDefined : 1;
+
+ // The partition whose dynamic symbol table contains this symbol's definition.
+ uint8_t partition = 1;
+
+ bool isSection() const { return type == llvm::ELF::STT_SECTION; }
+ bool isTls() const { return type == llvm::ELF::STT_TLS; }
+ bool isFunc() const { return type == llvm::ELF::STT_FUNC; }
+ bool isGnuIFunc() const { return type == llvm::ELF::STT_GNU_IFUNC; }
+ bool isObject() const { return type == llvm::ELF::STT_OBJECT; }
+ bool isFile() const { return type == llvm::ELF::STT_FILE; }
};
// Represents a symbol that is defined in the current output file.
class Defined : public Symbol {
public:
- Defined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, uint64_t Value, uint64_t Size, SectionBase *Section)
- : Symbol(DefinedKind, File, Name, Binding, StOther, Type), Value(Value),
- Size(Size), Section(Section) {}
+ Defined(InputFile *file, StringRefZ name, uint8_t binding, uint8_t stOther,
+ uint8_t type, uint64_t value, uint64_t size, SectionBase *section)
+ : Symbol(DefinedKind, file, name, binding, stOther, type), value(value),
+ size(size), section(section) {}
+
+ static bool classof(const Symbol *s) { return s->isDefined(); }
+
+ uint64_t value;
+ uint64_t size;
+ SectionBase *section;
+};
+
+// Represents a common symbol.
+//
+// On Unix, it is traditionally allowed to write variable definitions
+// without initialization expressions (such as "int foo;") to header
+// files. Such definition is called "tentative definition".
+//
+// Using tentative definition is usually considered a bad practice
+// because you should write only declarations (such as "extern int
+// foo;") to header files. Nevertheless, the linker and the compiler
+// have to do something to support bad code by allowing duplicate
+// definitions for this particular case.
+//
+// Common symbols represent variable definitions without initializations.
+// The compiler creates common symbols when it sees varaible definitions
+// without initialization (you can suppress this behavior and let the
+// compiler create a regular defined symbol by -fno-common).
+//
+// The linker allows common symbols to be replaced by regular defined
+// symbols. If there are remaining common symbols after name resolution is
+// complete, they are converted to regular defined symbols in a .bss
+// section. (Therefore, the later passes don't see any CommonSymbols.)
+class CommonSymbol : public Symbol {
+public:
+ CommonSymbol(InputFile *file, StringRefZ name, uint8_t binding,
+ uint8_t stOther, uint8_t type, uint64_t alignment, uint64_t size)
+ : Symbol(CommonKind, file, name, binding, stOther, type),
+ alignment(alignment), size(size) {}
- static bool classof(const Symbol *S) { return S->isDefined(); }
+ static bool classof(const Symbol *s) { return s->isCommon(); }
- uint64_t Value;
- uint64_t Size;
- SectionBase *Section;
+ uint32_t alignment;
+ uint64_t size;
};
class Undefined : public Symbol {
public:
- Undefined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type)
- : Symbol(UndefinedKind, File, Name, Binding, StOther, Type) {}
+ Undefined(InputFile *file, StringRefZ name, uint8_t binding, uint8_t stOther,
+ uint8_t type, uint32_t discardedSecIdx = 0)
+ : Symbol(UndefinedKind, file, name, binding, stOther, type),
+ discardedSecIdx(discardedSecIdx) {}
- static bool classof(const Symbol *S) { return S->kind() == UndefinedKind; }
+ static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
+
+ // The section index if in a discarded section, 0 otherwise.
+ uint32_t discardedSecIdx;
};
class SharedSymbol : public Symbol {
public:
- static bool classof(const Symbol *S) { return S->kind() == SharedKind; }
-
- SharedSymbol(InputFile &File, StringRef Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type, uint64_t Value, uint64_t Size,
- uint32_t Alignment, uint32_t VerdefIndex)
- : Symbol(SharedKind, &File, Name, Binding, StOther, Type),
- Alignment(Alignment), Value(Value), Size(Size) {
- this->VerdefIndex = VerdefIndex;
+ static bool classof(const Symbol *s) { return s->kind() == SharedKind; }
+
+ SharedSymbol(InputFile &file, StringRef name, uint8_t binding,
+ uint8_t stOther, uint8_t type, uint64_t value, uint64_t size,
+ uint32_t alignment, uint32_t verdefIndex)
+ : Symbol(SharedKind, &file, name, binding, stOther, type), value(value),
+ size(size), alignment(alignment) {
+ this->verdefIndex = verdefIndex;
// GNU ifunc is a mechanism to allow user-supplied functions to
// resolve PLT slot values at load-time. This is contrary to the
// regular symbol resolution scheme in which symbols are resolved just
@@ -268,18 +354,20 @@ public:
// For DSO symbols, we always call them through PLT slots anyway.
// So there's no difference between GNU ifunc and regular function
// symbols if they are in DSOs. So we can handle GNU_IFUNC as FUNC.
- if (this->Type == llvm::ELF::STT_GNU_IFUNC)
- this->Type = llvm::ELF::STT_FUNC;
+ if (this->type == llvm::ELF::STT_GNU_IFUNC)
+ this->type = llvm::ELF::STT_FUNC;
}
- template <class ELFT> SharedFile<ELFT> &getFile() const {
- return *cast<SharedFile<ELFT>>(File);
- }
+ SharedFile &getFile() const { return *cast<SharedFile>(file); }
- uint32_t Alignment;
+ uint64_t value; // st_value
+ uint64_t size; // st_size
+ uint32_t alignment;
- uint64_t Value; // st_value
- uint64_t Size; // st_size
+ // This is true if there has been at least one undefined reference to the
+ // symbol. The binding may change to STB_WEAK if the first undefined reference
+ // is weak.
+ bool referenced = false;
};
// LazyArchive and LazyObject represent a symbols that is not yet in the link,
@@ -298,121 +386,168 @@ public:
// symbol.
class LazyArchive : public Symbol {
public:
- LazyArchive(InputFile &File, uint8_t Type,
- const llvm::object::Archive::Symbol S)
- : Symbol(LazyArchiveKind, &File, S.getName(), llvm::ELF::STB_GLOBAL,
- llvm::ELF::STV_DEFAULT, Type),
- Sym(S) {}
+ LazyArchive(InputFile &file, const llvm::object::Archive::Symbol s)
+ : Symbol(LazyArchiveKind, &file, s.getName(), llvm::ELF::STB_GLOBAL,
+ llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE),
+ sym(s) {}
- static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
+ static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
- InputFile *fetch();
MemoryBufferRef getMemberBuffer();
-private:
- const llvm::object::Archive::Symbol Sym;
+ const llvm::object::Archive::Symbol sym;
};
// LazyObject symbols represents symbols in object files between
// --start-lib and --end-lib options.
class LazyObject : public Symbol {
public:
- LazyObject(InputFile &File, uint8_t Type, StringRef Name)
- : Symbol(LazyObjectKind, &File, Name, llvm::ELF::STB_GLOBAL,
- llvm::ELF::STV_DEFAULT, Type) {}
+ LazyObject(InputFile &file, StringRef name)
+ : Symbol(LazyObjectKind, &file, name, llvm::ELF::STB_GLOBAL,
+ llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE) {}
- static bool classof(const Symbol *S) { return S->kind() == LazyObjectKind; }
+ static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
};
// Some linker-generated symbols need to be created as
// Defined symbols.
struct ElfSym {
// __bss_start
- static Defined *Bss;
+ static Defined *bss;
// etext and _etext
- static Defined *Etext1;
- static Defined *Etext2;
+ static Defined *etext1;
+ static Defined *etext2;
// edata and _edata
- static Defined *Edata1;
- static Defined *Edata2;
+ static Defined *edata1;
+ static Defined *edata2;
// end and _end
- static Defined *End1;
- static Defined *End2;
+ static Defined *end1;
+ static Defined *end2;
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
// be at some offset from the base of the .got section, usually 0 or
// the end of the .got.
- static Defined *GlobalOffsetTable;
+ static Defined *globalOffsetTable;
// _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS.
- static Defined *MipsGp;
- static Defined *MipsGpDisp;
- static Defined *MipsLocalGp;
+ static Defined *mipsGp;
+ static Defined *mipsGpDisp;
+ static Defined *mipsLocalGp;
// __rel{,a}_iplt_{start,end} symbols.
- static Defined *RelaIpltStart;
- static Defined *RelaIpltEnd;
+ static Defined *relaIpltStart;
+ static Defined *relaIpltEnd;
+
+ // __global_pointer$ for RISC-V.
+ static Defined *riscvGlobalPointer;
+
+ // _TLS_MODULE_BASE_ on targets that support TLSDESC.
+ static Defined *tlsModuleBase;
};
// A buffer class that is large enough to hold any Symbol-derived
// object. We allocate memory using this class and instantiate a symbol
// using the placement new.
union SymbolUnion {
- alignas(Defined) char A[sizeof(Defined)];
- alignas(Undefined) char C[sizeof(Undefined)];
- alignas(SharedSymbol) char D[sizeof(SharedSymbol)];
- alignas(LazyArchive) char E[sizeof(LazyArchive)];
- alignas(LazyObject) char F[sizeof(LazyObject)];
+ alignas(Defined) char a[sizeof(Defined)];
+ alignas(CommonSymbol) char b[sizeof(CommonSymbol)];
+ alignas(Undefined) char c[sizeof(Undefined)];
+ alignas(SharedSymbol) char d[sizeof(SharedSymbol)];
+ alignas(LazyArchive) char e[sizeof(LazyArchive)];
+ alignas(LazyObject) char f[sizeof(LazyObject)];
};
-void printTraceSymbol(Symbol *Sym);
-
-template <typename T, typename... ArgT>
-void replaceSymbol(Symbol *S, ArgT &&... Arg) {
- using llvm::ELF::STT_TLS;
+// It is important to keep the size of SymbolUnion small for performance and
+// memory usage reasons. 80 bytes is a soft limit based on the size of Defined
+// on a 64-bit system.
+static_assert(sizeof(SymbolUnion) <= 80, "SymbolUnion too large");
+template <typename T> struct AssertSymbol {
static_assert(std::is_trivially_destructible<T>(),
"Symbol types must be trivially destructible");
static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
static_assert(alignof(T) <= alignof(SymbolUnion),
"SymbolUnion not aligned enough");
- assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
- "Not a Symbol");
+};
- Symbol Sym = *S;
+static inline void assertSymbols() {
+ AssertSymbol<Defined>();
+ AssertSymbol<CommonSymbol>();
+ AssertSymbol<Undefined>();
+ AssertSymbol<SharedSymbol>();
+ AssertSymbol<LazyArchive>();
+ AssertSymbol<LazyObject>();
+}
- new (S) T(std::forward<ArgT>(Arg)...);
+void printTraceSymbol(const Symbol *sym);
+
+size_t Symbol::getSymbolSize() const {
+ switch (kind()) {
+ case CommonKind:
+ return sizeof(CommonSymbol);
+ case DefinedKind:
+ return sizeof(Defined);
+ case LazyArchiveKind:
+ return sizeof(LazyArchive);
+ case LazyObjectKind:
+ return sizeof(LazyObject);
+ case SharedKind:
+ return sizeof(SharedSymbol);
+ case UndefinedKind:
+ return sizeof(Undefined);
+ case PlaceholderKind:
+ return sizeof(Symbol);
+ }
+ llvm_unreachable("unknown symbol kind");
+}
- S->VersionId = Sym.VersionId;
- S->Visibility = Sym.Visibility;
- S->IsUsedInRegularObj = Sym.IsUsedInRegularObj;
- S->ExportDynamic = Sym.ExportDynamic;
- S->CanInline = Sym.CanInline;
- S->Traced = Sym.Traced;
- S->ScriptDefined = Sym.ScriptDefined;
+// replace() replaces "this" object with a given symbol by memcpy'ing
+// it over to "this". This function is called as a result of name
+// resolution, e.g. to replace an undefind symbol with a defined symbol.
+void Symbol::replace(const Symbol &New) {
+ using llvm::ELF::STT_TLS;
// Symbols representing thread-local variables must be referenced by
// TLS-aware relocations, and non-TLS symbols must be reference by
// non-TLS relocations, so there's a clear distinction between TLS
// and non-TLS symbols. It is an error if the same symbol is defined
// as a TLS symbol in one file and as a non-TLS symbol in other file.
- bool TlsMismatch = (Sym.Type == STT_TLS && S->Type != STT_TLS) ||
- (Sym.Type != STT_TLS && S->Type == STT_TLS);
+ if (symbolKind != PlaceholderKind && !isLazy() && !New.isLazy()) {
+ bool tlsMismatch = (type == STT_TLS && New.type != STT_TLS) ||
+ (type != STT_TLS && New.type == STT_TLS);
+ if (tlsMismatch)
+ error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
+ toString(New.file) + "\n>>> defined in " + toString(file));
+ }
+
+ Symbol old = *this;
+ memcpy(this, &New, New.getSymbolSize());
+
+ versionId = old.versionId;
+ visibility = old.visibility;
+ isUsedInRegularObj = old.isUsedInRegularObj;
+ exportDynamic = old.exportDynamic;
+ canInline = old.canInline;
+ traced = old.traced;
+ isPreemptible = old.isPreemptible;
+ scriptDefined = old.scriptDefined;
+ partition = old.partition;
- if (Sym.SymbolKind != Symbol::PlaceholderKind && TlsMismatch && !Sym.isLazy())
- error("TLS attribute mismatch: " + toString(Sym) + "\n>>> defined in " +
- toString(Sym.File) + "\n>>> defined in " + toString(S->File));
+ // Symbol length is computed lazily. If we already know a symbol length,
+ // propagate it.
+ if (nameData == old.nameData && nameSize == 0 && old.nameSize != 0)
+ nameSize = old.nameSize;
// Print out a log message if --trace-symbol was specified.
// This is for debugging.
- if (S->Traced)
- printTraceSymbol(S);
+ if (traced)
+ printTraceSymbol(this);
}
-void maybeWarnUnorderableSymbol(const Symbol *Sym);
+void maybeWarnUnorderableSymbol(const Symbol *sym);
} // namespace elf
} // namespace lld
diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
index f459c1b6b4792..f6d0f190d84d0 100644
--- a/ELF/SyntheticSections.cpp
+++ b/ELF/SyntheticSections.cpp
@@ -1,9 +1,8 @@
//===- SyntheticSections.cpp ----------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -15,7 +14,6 @@
//===----------------------------------------------------------------------===//
#include "SyntheticSections.h"
-#include "Bits.h"
#include "Config.h"
#include "InputFiles.h"
#include "LinkerScript.h"
@@ -38,9 +36,6 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MD5.h"
-#include "llvm/Support/RandomNumberGenerator.h"
-#include "llvm/Support/SHA1.h"
-#include "llvm/Support/xxhash.h"
#include <cstdlib>
#include <thread>
@@ -57,19 +52,30 @@ using llvm::support::endian::read32le;
using llvm::support::endian::write32le;
using llvm::support::endian::write64le;
-constexpr size_t MergeNoTailSection::NumShards;
+constexpr size_t MergeNoTailSection::numShards;
+
+static uint64_t readUint(uint8_t *buf) {
+ return config->is64 ? read64(buf) : read32(buf);
+}
+
+static void writeUint(uint8_t *buf, uint64_t val) {
+ if (config->is64)
+ write64(buf, val);
+ else
+ write32(buf, val);
+}
// Returns an LLD version string.
static ArrayRef<uint8_t> getVersion() {
// Check LLD_VERSION first for ease of testing.
// You can get consistent output by using the environment variable.
// This is only for testing.
- StringRef S = getenv("LLD_VERSION");
- if (S.empty())
- S = Saver.save(Twine("Linker: ") + getLLDVersion());
+ StringRef s = getenv("LLD_VERSION");
+ if (s.empty())
+ s = saver.save(Twine("Linker: ") + getLLDVersion());
// +1 to include the terminating '\0'.
- return {(const uint8_t *)S.data(), S.size() + 1};
+ return {(const uint8_t *)s.data(), s.size() + 1};
}
// Creates a .comment section containing LLD version info.
@@ -83,79 +89,79 @@ MergeInputSection *elf::createCommentSection() {
// .MIPS.abiflags section.
template <class ELFT>
-MipsAbiFlagsSection<ELFT>::MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags)
+MipsAbiFlagsSection<ELFT>::MipsAbiFlagsSection(Elf_Mips_ABIFlags flags)
: SyntheticSection(SHF_ALLOC, SHT_MIPS_ABIFLAGS, 8, ".MIPS.abiflags"),
- Flags(Flags) {
- this->Entsize = sizeof(Elf_Mips_ABIFlags);
+ flags(flags) {
+ this->entsize = sizeof(Elf_Mips_ABIFlags);
}
-template <class ELFT> void MipsAbiFlagsSection<ELFT>::writeTo(uint8_t *Buf) {
- memcpy(Buf, &Flags, sizeof(Flags));
+template <class ELFT> void MipsAbiFlagsSection<ELFT>::writeTo(uint8_t *buf) {
+ memcpy(buf, &flags, sizeof(flags));
}
template <class ELFT>
MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
- Elf_Mips_ABIFlags Flags = {};
- bool Create = false;
+ Elf_Mips_ABIFlags flags = {};
+ bool create = false;
- for (InputSectionBase *Sec : InputSections) {
- if (Sec->Type != SHT_MIPS_ABIFLAGS)
+ for (InputSectionBase *sec : inputSections) {
+ if (sec->type != SHT_MIPS_ABIFLAGS)
continue;
- Sec->Live = false;
- Create = true;
+ sec->markDead();
+ create = true;
- std::string Filename = toString(Sec->File);
- const size_t Size = Sec->data().size();
+ std::string filename = toString(sec->file);
+ const size_t size = sec->data().size();
// Older version of BFD (such as the default FreeBSD linker) concatenate
// .MIPS.abiflags instead of merging. To allow for this case (or potential
// zero padding) we ignore everything after the first Elf_Mips_ABIFlags
- if (Size < sizeof(Elf_Mips_ABIFlags)) {
- error(Filename + ": invalid size of .MIPS.abiflags section: got " +
- Twine(Size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags)));
+ if (size < sizeof(Elf_Mips_ABIFlags)) {
+ error(filename + ": invalid size of .MIPS.abiflags section: got " +
+ Twine(size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags)));
return nullptr;
}
- auto *S = reinterpret_cast<const Elf_Mips_ABIFlags *>(Sec->data().data());
- if (S->version != 0) {
- error(Filename + ": unexpected .MIPS.abiflags version " +
- Twine(S->version));
+ auto *s = reinterpret_cast<const Elf_Mips_ABIFlags *>(sec->data().data());
+ if (s->version != 0) {
+ error(filename + ": unexpected .MIPS.abiflags version " +
+ Twine(s->version));
return nullptr;
}
// LLD checks ISA compatibility in calcMipsEFlags(). Here we just
// select the highest number of ISA/Rev/Ext.
- Flags.isa_level = std::max(Flags.isa_level, S->isa_level);
- Flags.isa_rev = std::max(Flags.isa_rev, S->isa_rev);
- Flags.isa_ext = std::max(Flags.isa_ext, S->isa_ext);
- Flags.gpr_size = std::max(Flags.gpr_size, S->gpr_size);
- Flags.cpr1_size = std::max(Flags.cpr1_size, S->cpr1_size);
- Flags.cpr2_size = std::max(Flags.cpr2_size, S->cpr2_size);
- Flags.ases |= S->ases;
- Flags.flags1 |= S->flags1;
- Flags.flags2 |= S->flags2;
- Flags.fp_abi = elf::getMipsFpAbiFlag(Flags.fp_abi, S->fp_abi, Filename);
+ flags.isa_level = std::max(flags.isa_level, s->isa_level);
+ flags.isa_rev = std::max(flags.isa_rev, s->isa_rev);
+ flags.isa_ext = std::max(flags.isa_ext, s->isa_ext);
+ flags.gpr_size = std::max(flags.gpr_size, s->gpr_size);
+ flags.cpr1_size = std::max(flags.cpr1_size, s->cpr1_size);
+ flags.cpr2_size = std::max(flags.cpr2_size, s->cpr2_size);
+ flags.ases |= s->ases;
+ flags.flags1 |= s->flags1;
+ flags.flags2 |= s->flags2;
+ flags.fp_abi = elf::getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename);
};
- if (Create)
- return make<MipsAbiFlagsSection<ELFT>>(Flags);
+ if (create)
+ return make<MipsAbiFlagsSection<ELFT>>(flags);
return nullptr;
}
// .MIPS.options section.
template <class ELFT>
-MipsOptionsSection<ELFT>::MipsOptionsSection(Elf_Mips_RegInfo Reginfo)
+MipsOptionsSection<ELFT>::MipsOptionsSection(Elf_Mips_RegInfo reginfo)
: SyntheticSection(SHF_ALLOC, SHT_MIPS_OPTIONS, 8, ".MIPS.options"),
- Reginfo(Reginfo) {
- this->Entsize = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
+ reginfo(reginfo) {
+ this->entsize = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
}
-template <class ELFT> void MipsOptionsSection<ELFT>::writeTo(uint8_t *Buf) {
- auto *Options = reinterpret_cast<Elf_Mips_Options *>(Buf);
- Options->kind = ODK_REGINFO;
- Options->size = getSize();
+template <class ELFT> void MipsOptionsSection<ELFT>::writeTo(uint8_t *buf) {
+ auto *options = reinterpret_cast<Elf_Mips_Options *>(buf);
+ options->kind = ODK_REGINFO;
+ options->size = getSize();
- if (!Config->Relocatable)
- Reginfo.ri_gp_value = In.MipsGot->getGp();
- memcpy(Buf + sizeof(Elf_Mips_Options), &Reginfo, sizeof(Reginfo));
+ if (!config->relocatable)
+ reginfo.ri_gp_value = in.mipsGot->getGp();
+ memcpy(buf + sizeof(Elf_Mips_Options), &reginfo, sizeof(reginfo));
}
template <class ELFT>
@@ -164,55 +170,55 @@ MipsOptionsSection<ELFT> *MipsOptionsSection<ELFT>::create() {
if (!ELFT::Is64Bits)
return nullptr;
- std::vector<InputSectionBase *> Sections;
- for (InputSectionBase *Sec : InputSections)
- if (Sec->Type == SHT_MIPS_OPTIONS)
- Sections.push_back(Sec);
+ std::vector<InputSectionBase *> sections;
+ for (InputSectionBase *sec : inputSections)
+ if (sec->type == SHT_MIPS_OPTIONS)
+ sections.push_back(sec);
- if (Sections.empty())
+ if (sections.empty())
return nullptr;
- Elf_Mips_RegInfo Reginfo = {};
- for (InputSectionBase *Sec : Sections) {
- Sec->Live = false;
+ Elf_Mips_RegInfo reginfo = {};
+ for (InputSectionBase *sec : sections) {
+ sec->markDead();
- std::string Filename = toString(Sec->File);
- ArrayRef<uint8_t> D = Sec->data();
+ std::string filename = toString(sec->file);
+ ArrayRef<uint8_t> d = sec->data();
- while (!D.empty()) {
- if (D.size() < sizeof(Elf_Mips_Options)) {
- error(Filename + ": invalid size of .MIPS.options section");
+ while (!d.empty()) {
+ if (d.size() < sizeof(Elf_Mips_Options)) {
+ error(filename + ": invalid size of .MIPS.options section");
break;
}
- auto *Opt = reinterpret_cast<const Elf_Mips_Options *>(D.data());
- if (Opt->kind == ODK_REGINFO) {
- Reginfo.ri_gprmask |= Opt->getRegInfo().ri_gprmask;
- Sec->getFile<ELFT>()->MipsGp0 = Opt->getRegInfo().ri_gp_value;
+ auto *opt = reinterpret_cast<const Elf_Mips_Options *>(d.data());
+ if (opt->kind == ODK_REGINFO) {
+ reginfo.ri_gprmask |= opt->getRegInfo().ri_gprmask;
+ sec->getFile<ELFT>()->mipsGp0 = opt->getRegInfo().ri_gp_value;
break;
}
- if (!Opt->size)
- fatal(Filename + ": zero option descriptor size");
- D = D.slice(Opt->size);
+ if (!opt->size)
+ fatal(filename + ": zero option descriptor size");
+ d = d.slice(opt->size);
}
};
- return make<MipsOptionsSection<ELFT>>(Reginfo);
+ return make<MipsOptionsSection<ELFT>>(reginfo);
}
// MIPS .reginfo section.
template <class ELFT>
-MipsReginfoSection<ELFT>::MipsReginfoSection(Elf_Mips_RegInfo Reginfo)
+MipsReginfoSection<ELFT>::MipsReginfoSection(Elf_Mips_RegInfo reginfo)
: SyntheticSection(SHF_ALLOC, SHT_MIPS_REGINFO, 4, ".reginfo"),
- Reginfo(Reginfo) {
- this->Entsize = sizeof(Elf_Mips_RegInfo);
+ reginfo(reginfo) {
+ this->entsize = sizeof(Elf_Mips_RegInfo);
}
-template <class ELFT> void MipsReginfoSection<ELFT>::writeTo(uint8_t *Buf) {
- if (!Config->Relocatable)
- Reginfo.ri_gp_value = In.MipsGot->getGp();
- memcpy(Buf, &Reginfo, sizeof(Reginfo));
+template <class ELFT> void MipsReginfoSection<ELFT>::writeTo(uint8_t *buf) {
+ if (!config->relocatable)
+ reginfo.ri_gp_value = in.mipsGot->getGp();
+ memcpy(buf, &reginfo, sizeof(reginfo));
}
template <class ELFT>
@@ -221,53 +227,53 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
if (ELFT::Is64Bits)
return nullptr;
- std::vector<InputSectionBase *> Sections;
- for (InputSectionBase *Sec : InputSections)
- if (Sec->Type == SHT_MIPS_REGINFO)
- Sections.push_back(Sec);
+ std::vector<InputSectionBase *> sections;
+ for (InputSectionBase *sec : inputSections)
+ if (sec->type == SHT_MIPS_REGINFO)
+ sections.push_back(sec);
- if (Sections.empty())
+ if (sections.empty())
return nullptr;
- Elf_Mips_RegInfo Reginfo = {};
- for (InputSectionBase *Sec : Sections) {
- Sec->Live = false;
+ Elf_Mips_RegInfo reginfo = {};
+ for (InputSectionBase *sec : sections) {
+ sec->markDead();
- if (Sec->data().size() != sizeof(Elf_Mips_RegInfo)) {
- error(toString(Sec->File) + ": invalid size of .reginfo section");
+ if (sec->data().size() != sizeof(Elf_Mips_RegInfo)) {
+ error(toString(sec->file) + ": invalid size of .reginfo section");
return nullptr;
}
- auto *R = reinterpret_cast<const Elf_Mips_RegInfo *>(Sec->data().data());
- Reginfo.ri_gprmask |= R->ri_gprmask;
- Sec->getFile<ELFT>()->MipsGp0 = R->ri_gp_value;
+ auto *r = reinterpret_cast<const Elf_Mips_RegInfo *>(sec->data().data());
+ reginfo.ri_gprmask |= r->ri_gprmask;
+ sec->getFile<ELFT>()->mipsGp0 = r->ri_gp_value;
};
- return make<MipsReginfoSection<ELFT>>(Reginfo);
+ return make<MipsReginfoSection<ELFT>>(reginfo);
}
InputSection *elf::createInterpSection() {
// StringSaver guarantees that the returned string ends with '\0'.
- StringRef S = Saver.save(Config->DynamicLinker);
- ArrayRef<uint8_t> Contents = {(const uint8_t *)S.data(), S.size() + 1};
+ StringRef s = saver.save(config->dynamicLinker);
+ ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
- auto *Sec = make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, Contents,
+ auto *sec = make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents,
".interp");
- Sec->Live = true;
- return Sec;
+ sec->markLive();
+ return sec;
}
-Defined *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
- uint64_t Size, InputSectionBase &Section) {
- auto *S = make<Defined>(Section.File, Name, STB_LOCAL, STV_DEFAULT, Type,
- Value, Size, &Section);
- if (In.SymTab)
- In.SymTab->addSymbol(S);
- return S;
+Defined *elf::addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
+ uint64_t size, InputSectionBase &section) {
+ auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type,
+ value, size, &section);
+ if (in.symTab)
+ in.symTab->addSymbol(s);
+ return s;
}
static size_t getHashSize() {
- switch (Config->BuildId) {
+ switch (config->buildId) {
case BuildIdKind::Fast:
return 8;
case BuildIdKind::Md5:
@@ -276,89 +282,67 @@ static size_t getHashSize() {
case BuildIdKind::Sha1:
return 20;
case BuildIdKind::Hexstring:
- return Config->BuildIdVector.size();
+ return config->buildIdVector.size();
default:
llvm_unreachable("unknown BuildIdKind");
}
}
+// This class represents a linker-synthesized .note.gnu.property section.
+//
+// In x86 and AArch64, object files may contain feature flags indicating the
+// features that they have used. The flags are stored in a .note.gnu.property
+// section.
+//
+// lld reads the sections from input files and merges them by computing AND of
+// the flags. The result is written as a new .note.gnu.property section.
+//
+// If the flag is zero (which indicates that the intersection of the feature
+// sets is empty, or some input files didn't have .note.gnu.property sections),
+// we don't create this section.
+GnuPropertySection::GnuPropertySection()
+ : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, 4,
+ ".note.gnu.property") {}
+
+void GnuPropertySection::writeTo(uint8_t *buf) {
+ uint32_t featureAndType = config->emachine == EM_AARCH64
+ ? GNU_PROPERTY_AARCH64_FEATURE_1_AND
+ : GNU_PROPERTY_X86_FEATURE_1_AND;
+
+ write32(buf, 4); // Name size
+ write32(buf + 4, config->is64 ? 16 : 12); // Content size
+ write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
+ memcpy(buf + 12, "GNU", 4); // Name string
+ write32(buf + 16, featureAndType); // Feature type
+ write32(buf + 20, 4); // Feature size
+ write32(buf + 24, config->andFeatures); // Feature flags
+ if (config->is64)
+ write32(buf + 28, 0); // Padding
+}
+
+size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; }
+
BuildIdSection::BuildIdSection()
: SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"),
- HashSize(getHashSize()) {}
-
-void BuildIdSection::writeTo(uint8_t *Buf) {
- write32(Buf, 4); // Name size
- write32(Buf + 4, HashSize); // Content size
- write32(Buf + 8, NT_GNU_BUILD_ID); // Type
- memcpy(Buf + 12, "GNU", 4); // Name string
- HashBuf = Buf + 16;
-}
-
-// Split one uint8 array into small pieces of uint8 arrays.
-static std::vector<ArrayRef<uint8_t>> split(ArrayRef<uint8_t> Arr,
- size_t ChunkSize) {
- std::vector<ArrayRef<uint8_t>> Ret;
- while (Arr.size() > ChunkSize) {
- Ret.push_back(Arr.take_front(ChunkSize));
- Arr = Arr.drop_front(ChunkSize);
- }
- if (!Arr.empty())
- Ret.push_back(Arr);
- return Ret;
-}
-
-// Computes a hash value of Data using a given hash function.
-// In order to utilize multiple cores, we first split data into 1MB
-// chunks, compute a hash for each chunk, and then compute a hash value
-// of the hash values.
-void BuildIdSection::computeHash(
- llvm::ArrayRef<uint8_t> Data,
- std::function<void(uint8_t *Dest, ArrayRef<uint8_t> Arr)> HashFn) {
- std::vector<ArrayRef<uint8_t>> Chunks = split(Data, 1024 * 1024);
- std::vector<uint8_t> Hashes(Chunks.size() * HashSize);
-
- // Compute hash values.
- parallelForEachN(0, Chunks.size(), [&](size_t I) {
- HashFn(Hashes.data() + I * HashSize, Chunks[I]);
- });
+ hashSize(getHashSize()) {}
- // Write to the final output buffer.
- HashFn(HashBuf, Hashes);
+void BuildIdSection::writeTo(uint8_t *buf) {
+ write32(buf, 4); // Name size
+ write32(buf + 4, hashSize); // Content size
+ write32(buf + 8, NT_GNU_BUILD_ID); // Type
+ memcpy(buf + 12, "GNU", 4); // Name string
+ hashBuf = buf + 16;
}
-BssSection::BssSection(StringRef Name, uint64_t Size, uint32_t Alignment)
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, Alignment, Name) {
- this->Bss = true;
- this->Size = Size;
+void BuildIdSection::writeBuildId(ArrayRef<uint8_t> buf) {
+ assert(buf.size() == hashSize);
+ memcpy(hashBuf, buf.data(), hashSize);
}
-void BuildIdSection::writeBuildId(ArrayRef<uint8_t> Buf) {
- switch (Config->BuildId) {
- case BuildIdKind::Fast:
- computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
- write64le(Dest, xxHash64(Arr));
- });
- break;
- case BuildIdKind::Md5:
- computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
- memcpy(Dest, MD5::hash(Arr).data(), 16);
- });
- break;
- case BuildIdKind::Sha1:
- computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
- memcpy(Dest, SHA1::hash(Arr).data(), 20);
- });
- break;
- case BuildIdKind::Uuid:
- if (auto EC = getRandomBytes(HashBuf, HashSize))
- error("entropy source failure: " + EC.message());
- break;
- case BuildIdKind::Hexstring:
- memcpy(HashBuf, Config->BuildIdVector.data(), Config->BuildIdVector.size());
- break;
- default:
- llvm_unreachable("unknown BuildIdKind");
- }
+BssSection::BssSection(StringRef name, uint64_t size, uint32_t alignment)
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, alignment, name) {
+ this->bss = true;
+ this->size = size;
}
EhFrameSection::EhFrameSection()
@@ -368,47 +352,48 @@ EhFrameSection::EhFrameSection()
// CIE records from input object files are uniquified by their contents
// and where their relocations point to.
template <class ELFT, class RelTy>
-CieRecord *EhFrameSection::addCie(EhSectionPiece &Cie, ArrayRef<RelTy> Rels) {
- Symbol *Personality = nullptr;
- unsigned FirstRelI = Cie.FirstRelocation;
- if (FirstRelI != (unsigned)-1)
- Personality =
- &Cie.Sec->template getFile<ELFT>()->getRelocTargetSym(Rels[FirstRelI]);
+CieRecord *EhFrameSection::addCie(EhSectionPiece &cie, ArrayRef<RelTy> rels) {
+ Symbol *personality = nullptr;
+ unsigned firstRelI = cie.firstRelocation;
+ if (firstRelI != (unsigned)-1)
+ personality =
+ &cie.sec->template getFile<ELFT>()->getRelocTargetSym(rels[firstRelI]);
// Search for an existing CIE by CIE contents/relocation target pair.
- CieRecord *&Rec = CieMap[{Cie.data(), Personality}];
+ CieRecord *&rec = cieMap[{cie.data(), personality}];
// If not found, create a new one.
- if (!Rec) {
- Rec = make<CieRecord>();
- Rec->Cie = &Cie;
- CieRecords.push_back(Rec);
+ if (!rec) {
+ rec = make<CieRecord>();
+ rec->cie = &cie;
+ cieRecords.push_back(rec);
}
- return Rec;
+ return rec;
}
// There is one FDE per function. Returns true if a given FDE
// points to a live function.
template <class ELFT, class RelTy>
-bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, ArrayRef<RelTy> Rels) {
- auto *Sec = cast<EhInputSection>(Fde.Sec);
- unsigned FirstRelI = Fde.FirstRelocation;
+bool EhFrameSection::isFdeLive(EhSectionPiece &fde, ArrayRef<RelTy> rels) {
+ auto *sec = cast<EhInputSection>(fde.sec);
+ unsigned firstRelI = fde.firstRelocation;
// An FDE should point to some function because FDEs are to describe
// functions. That's however not always the case due to an issue of
// ld.gold with -r. ld.gold may discard only functions and leave their
// corresponding FDEs, which results in creating bad .eh_frame sections.
// To deal with that, we ignore such FDEs.
- if (FirstRelI == (unsigned)-1)
+ if (firstRelI == (unsigned)-1)
return false;
- const RelTy &Rel = Rels[FirstRelI];
- Symbol &B = Sec->template getFile<ELFT>()->getRelocTargetSym(Rel);
+ const RelTy &rel = rels[firstRelI];
+ Symbol &b = sec->template getFile<ELFT>()->getRelocTargetSym(rel);
- // FDEs for garbage-collected or merged-by-ICF sections are dead.
- if (auto *D = dyn_cast<Defined>(&B))
- if (SectionBase *Sec = D->Section)
- return Sec->Live;
+ // FDEs for garbage-collected or merged-by-ICF sections, or sections in
+ // another partition, are dead.
+ if (auto *d = dyn_cast<Defined>(&b))
+ if (SectionBase *sec = d->section)
+ return sec->partition == partition;
return false;
}
@@ -417,73 +402,73 @@ bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, ArrayRef<RelTy> Rels) {
// a list of FDEs. This function searches an existing CIE or create a new
// one and associates FDEs to the CIE.
template <class ELFT, class RelTy>
-void EhFrameSection::addSectionAux(EhInputSection *Sec, ArrayRef<RelTy> Rels) {
- OffsetToCie.clear();
- for (EhSectionPiece &Piece : Sec->Pieces) {
+void EhFrameSection::addSectionAux(EhInputSection *sec, ArrayRef<RelTy> rels) {
+ offsetToCie.clear();
+ for (EhSectionPiece &piece : sec->pieces) {
// The empty record is the end marker.
- if (Piece.Size == 4)
+ if (piece.size == 4)
return;
- size_t Offset = Piece.InputOff;
- uint32_t ID = read32(Piece.data().data() + 4);
- if (ID == 0) {
- OffsetToCie[Offset] = addCie<ELFT>(Piece, Rels);
+ size_t offset = piece.inputOff;
+ uint32_t id = read32(piece.data().data() + 4);
+ if (id == 0) {
+ offsetToCie[offset] = addCie<ELFT>(piece, rels);
continue;
}
- uint32_t CieOffset = Offset + 4 - ID;
- CieRecord *Rec = OffsetToCie[CieOffset];
- if (!Rec)
- fatal(toString(Sec) + ": invalid CIE reference");
+ uint32_t cieOffset = offset + 4 - id;
+ CieRecord *rec = offsetToCie[cieOffset];
+ if (!rec)
+ fatal(toString(sec) + ": invalid CIE reference");
- if (!isFdeLive<ELFT>(Piece, Rels))
+ if (!isFdeLive<ELFT>(piece, rels))
continue;
- Rec->Fdes.push_back(&Piece);
- NumFdes++;
+ rec->fdes.push_back(&piece);
+ numFdes++;
}
}
-template <class ELFT> void EhFrameSection::addSection(InputSectionBase *C) {
- auto *Sec = cast<EhInputSection>(C);
- Sec->Parent = this;
+template <class ELFT> void EhFrameSection::addSection(InputSectionBase *c) {
+ auto *sec = cast<EhInputSection>(c);
+ sec->parent = this;
- Alignment = std::max(Alignment, Sec->Alignment);
- Sections.push_back(Sec);
+ alignment = std::max(alignment, sec->alignment);
+ sections.push_back(sec);
- for (auto *DS : Sec->DependentSections)
- DependentSections.push_back(DS);
+ for (auto *ds : sec->dependentSections)
+ dependentSections.push_back(ds);
- if (Sec->Pieces.empty())
+ if (sec->pieces.empty())
return;
- if (Sec->AreRelocsRela)
- addSectionAux<ELFT>(Sec, Sec->template relas<ELFT>());
+ if (sec->areRelocsRela)
+ addSectionAux<ELFT>(sec, sec->template relas<ELFT>());
else
- addSectionAux<ELFT>(Sec, Sec->template rels<ELFT>());
+ addSectionAux<ELFT>(sec, sec->template rels<ELFT>());
}
-static void writeCieFde(uint8_t *Buf, ArrayRef<uint8_t> D) {
- memcpy(Buf, D.data(), D.size());
+static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
+ memcpy(buf, d.data(), d.size());
- size_t Aligned = alignTo(D.size(), Config->Wordsize);
+ size_t aligned = alignTo(d.size(), config->wordsize);
// Zero-clear trailing padding if it exists.
- memset(Buf + D.size(), 0, Aligned - D.size());
+ memset(buf + d.size(), 0, aligned - d.size());
// Fix the size field. -4 since size does not include the size field itself.
- write32(Buf, Aligned - 4);
+ write32(buf, aligned - 4);
}
void EhFrameSection::finalizeContents() {
- assert(!this->Size); // Not finalized.
- size_t Off = 0;
- for (CieRecord *Rec : CieRecords) {
- Rec->Cie->OutputOff = Off;
- Off += alignTo(Rec->Cie->Size, Config->Wordsize);
-
- for (EhSectionPiece *Fde : Rec->Fdes) {
- Fde->OutputOff = Off;
- Off += alignTo(Fde->Size, Config->Wordsize);
+ assert(!this->size); // Not finalized.
+ size_t off = 0;
+ for (CieRecord *rec : cieRecords) {
+ rec->cie->outputOff = off;
+ off += alignTo(rec->cie->size, config->wordsize);
+
+ for (EhSectionPiece *fde : rec->fdes) {
+ fde->outputOff = off;
+ off += alignTo(fde->size, config->wordsize);
}
}
@@ -491,375 +476,374 @@ void EhFrameSection::finalizeContents() {
// Call Frame Information records. glibc unwind-dw2-fde.c
// classify_object_over_fdes expects there is a CIE record length 0 as a
// terminator. Thus we add one unconditionally.
- Off += 4;
+ off += 4;
- this->Size = Off;
+ this->size = off;
}
// Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table
// to get an FDE from an address to which FDE is applied. This function
// returns a list of such pairs.
std::vector<EhFrameSection::FdeData> EhFrameSection::getFdeData() const {
- uint8_t *Buf = getParent()->Loc + OutSecOff;
- std::vector<FdeData> Ret;
-
- uint64_t VA = In.EhFrameHdr->getVA();
- for (CieRecord *Rec : CieRecords) {
- uint8_t Enc = getFdeEncoding(Rec->Cie);
- for (EhSectionPiece *Fde : Rec->Fdes) {
- uint64_t Pc = getFdePc(Buf, Fde->OutputOff, Enc);
- uint64_t FdeVA = getParent()->Addr + Fde->OutputOff;
- if (!isInt<32>(Pc - VA))
- fatal(toString(Fde->Sec) + ": PC offset is too large: 0x" +
- Twine::utohexstr(Pc - VA));
- Ret.push_back({uint32_t(Pc - VA), uint32_t(FdeVA - VA)});
+ uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff;
+ std::vector<FdeData> ret;
+
+ uint64_t va = getPartition().ehFrameHdr->getVA();
+ for (CieRecord *rec : cieRecords) {
+ uint8_t enc = getFdeEncoding(rec->cie);
+ for (EhSectionPiece *fde : rec->fdes) {
+ uint64_t pc = getFdePc(buf, fde->outputOff, enc);
+ uint64_t fdeVA = getParent()->addr + fde->outputOff;
+ if (!isInt<32>(pc - va))
+ fatal(toString(fde->sec) + ": PC offset is too large: 0x" +
+ Twine::utohexstr(pc - va));
+ ret.push_back({uint32_t(pc - va), uint32_t(fdeVA - va)});
}
}
// Sort the FDE list by their PC and uniqueify. Usually there is only
// one FDE for a PC (i.e. function), but if ICF merges two functions
// into one, there can be more than one FDEs pointing to the address.
- auto Less = [](const FdeData &A, const FdeData &B) {
- return A.PcRel < B.PcRel;
+ auto less = [](const FdeData &a, const FdeData &b) {
+ return a.pcRel < b.pcRel;
};
- std::stable_sort(Ret.begin(), Ret.end(), Less);
- auto Eq = [](const FdeData &A, const FdeData &B) {
- return A.PcRel == B.PcRel;
+ llvm::stable_sort(ret, less);
+ auto eq = [](const FdeData &a, const FdeData &b) {
+ return a.pcRel == b.pcRel;
};
- Ret.erase(std::unique(Ret.begin(), Ret.end(), Eq), Ret.end());
+ ret.erase(std::unique(ret.begin(), ret.end(), eq), ret.end());
- return Ret;
+ return ret;
}
-static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
- switch (Size) {
+static uint64_t readFdeAddr(uint8_t *buf, int size) {
+ switch (size) {
case DW_EH_PE_udata2:
- return read16(Buf);
+ return read16(buf);
case DW_EH_PE_sdata2:
- return (int16_t)read16(Buf);
+ return (int16_t)read16(buf);
case DW_EH_PE_udata4:
- return read32(Buf);
+ return read32(buf);
case DW_EH_PE_sdata4:
- return (int32_t)read32(Buf);
+ return (int32_t)read32(buf);
case DW_EH_PE_udata8:
case DW_EH_PE_sdata8:
- return read64(Buf);
+ return read64(buf);
case DW_EH_PE_absptr:
- return readUint(Buf);
+ return readUint(buf);
}
fatal("unknown FDE size encoding");
}
// Returns the VA to which a given FDE (on a mmap'ed buffer) is applied to.
// We need it to create .eh_frame_hdr section.
-uint64_t EhFrameSection::getFdePc(uint8_t *Buf, size_t FdeOff,
- uint8_t Enc) const {
+uint64_t EhFrameSection::getFdePc(uint8_t *buf, size_t fdeOff,
+ uint8_t enc) const {
// The starting address to which this FDE applies is
// stored at FDE + 8 byte.
- size_t Off = FdeOff + 8;
- uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0xf);
- if ((Enc & 0x70) == DW_EH_PE_absptr)
- return Addr;
- if ((Enc & 0x70) == DW_EH_PE_pcrel)
- return Addr + getParent()->Addr + Off;
+ size_t off = fdeOff + 8;
+ uint64_t addr = readFdeAddr(buf + off, enc & 0xf);
+ if ((enc & 0x70) == DW_EH_PE_absptr)
+ return addr;
+ if ((enc & 0x70) == DW_EH_PE_pcrel)
+ return addr + getParent()->addr + off;
fatal("unknown FDE size relative encoding");
}
-void EhFrameSection::writeTo(uint8_t *Buf) {
+void EhFrameSection::writeTo(uint8_t *buf) {
// Write CIE and FDE records.
- for (CieRecord *Rec : CieRecords) {
- size_t CieOffset = Rec->Cie->OutputOff;
- writeCieFde(Buf + CieOffset, Rec->Cie->data());
+ for (CieRecord *rec : cieRecords) {
+ size_t cieOffset = rec->cie->outputOff;
+ writeCieFde(buf + cieOffset, rec->cie->data());
- for (EhSectionPiece *Fde : Rec->Fdes) {
- size_t Off = Fde->OutputOff;
- writeCieFde(Buf + Off, Fde->data());
+ for (EhSectionPiece *fde : rec->fdes) {
+ size_t off = fde->outputOff;
+ writeCieFde(buf + off, fde->data());
// FDE's second word should have the offset to an associated CIE.
// Write it.
- write32(Buf + Off + 4, Off + 4 - CieOffset);
+ write32(buf + off + 4, off + 4 - cieOffset);
}
}
// Apply relocations. .eh_frame section contents are not contiguous
// in the output buffer, but relocateAlloc() still works because
// getOffset() takes care of discontiguous section pieces.
- for (EhInputSection *S : Sections)
- S->relocateAlloc(Buf, nullptr);
+ for (EhInputSection *s : sections)
+ s->relocateAlloc(buf, nullptr);
+
+ if (getPartition().ehFrameHdr && getPartition().ehFrameHdr->getParent())
+ getPartition().ehFrameHdr->write();
}
GotSection::GotSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- Target->GotEntrySize, ".got") {
- // PPC64 saves the ElfSym::GlobalOffsetTable .TOC. as the first entry in the
- // .got. If there are no references to .TOC. in the symbol table,
- // ElfSym::GlobalOffsetTable will not be defined and we won't need to save
- // .TOC. in the .got. When it is defined, we increase NumEntries by the number
- // of entries used to emit ElfSym::GlobalOffsetTable.
- if (ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt)
- NumEntries += Target->GotHeaderEntriesNum;
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
+ ".got") {
+ // If ElfSym::globalOffsetTable is relative to .got and is referenced,
+ // increase numEntries by the number of entries used to emit
+ // ElfSym::globalOffsetTable.
+ if (ElfSym::globalOffsetTable && !target->gotBaseSymInGotPlt)
+ numEntries += target->gotHeaderEntriesNum;
}
-void GotSection::addEntry(Symbol &Sym) {
- Sym.GotIndex = NumEntries;
- ++NumEntries;
+void GotSection::addEntry(Symbol &sym) {
+ sym.gotIndex = numEntries;
+ ++numEntries;
}
-bool GotSection::addDynTlsEntry(Symbol &Sym) {
- if (Sym.GlobalDynIndex != -1U)
+bool GotSection::addDynTlsEntry(Symbol &sym) {
+ if (sym.globalDynIndex != -1U)
return false;
- Sym.GlobalDynIndex = NumEntries;
+ sym.globalDynIndex = numEntries;
// Global Dynamic TLS entries take two GOT slots.
- NumEntries += 2;
+ numEntries += 2;
return true;
}
// Reserves TLS entries for a TLS module ID and a TLS block offset.
// In total it takes two GOT slots.
bool GotSection::addTlsIndex() {
- if (TlsIndexOff != uint32_t(-1))
+ if (tlsIndexOff != uint32_t(-1))
return false;
- TlsIndexOff = NumEntries * Config->Wordsize;
- NumEntries += 2;
+ tlsIndexOff = numEntries * config->wordsize;
+ numEntries += 2;
return true;
}
-uint64_t GotSection::getGlobalDynAddr(const Symbol &B) const {
- return this->getVA() + B.GlobalDynIndex * Config->Wordsize;
+uint64_t GotSection::getGlobalDynAddr(const Symbol &b) const {
+ return this->getVA() + b.globalDynIndex * config->wordsize;
}
-uint64_t GotSection::getGlobalDynOffset(const Symbol &B) const {
- return B.GlobalDynIndex * Config->Wordsize;
+uint64_t GotSection::getGlobalDynOffset(const Symbol &b) const {
+ return b.globalDynIndex * config->wordsize;
}
void GotSection::finalizeContents() {
- Size = NumEntries * Config->Wordsize;
+ size = numEntries * config->wordsize;
}
-bool GotSection::empty() const {
+bool GotSection::isNeeded() const {
// We need to emit a GOT even if it's empty if there's a relocation that is
- // relative to GOT(such as GOTOFFREL) or there's a symbol that points to a GOT
- // (i.e. _GLOBAL_OFFSET_TABLE_) that the target defines relative to the .got.
- return NumEntries == 0 && !HasGotOffRel &&
- !(ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt);
+ // relative to GOT(such as GOTOFFREL).
+ return numEntries || hasGotOffRel;
}
-void GotSection::writeTo(uint8_t *Buf) {
+void GotSection::writeTo(uint8_t *buf) {
// Buf points to the start of this section's buffer,
// whereas InputSectionBase::relocateAlloc() expects its argument
// to point to the start of the output section.
- Target->writeGotHeader(Buf);
- relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + Size);
+ target->writeGotHeader(buf);
+ relocateAlloc(buf - outSecOff, buf - outSecOff + size);
}
-static uint64_t getMipsPageAddr(uint64_t Addr) {
- return (Addr + 0x8000) & ~0xffff;
+static uint64_t getMipsPageAddr(uint64_t addr) {
+ return (addr + 0x8000) & ~0xffff;
}
-static uint64_t getMipsPageCount(uint64_t Size) {
- return (Size + 0xfffe) / 0xffff + 1;
+static uint64_t getMipsPageCount(uint64_t size) {
+ return (size + 0xfffe) / 0xffff + 1;
}
MipsGotSection::MipsGotSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, SHT_PROGBITS, 16,
".got") {}
-void MipsGotSection::addEntry(InputFile &File, Symbol &Sym, int64_t Addend,
- RelExpr Expr) {
- FileGot &G = getGot(File);
- if (Expr == R_MIPS_GOT_LOCAL_PAGE) {
- if (const OutputSection *OS = Sym.getOutputSection())
- G.PagesMap.insert({OS, {}});
+void MipsGotSection::addEntry(InputFile &file, Symbol &sym, int64_t addend,
+ RelExpr expr) {
+ FileGot &g = getGot(file);
+ if (expr == R_MIPS_GOT_LOCAL_PAGE) {
+ if (const OutputSection *os = sym.getOutputSection())
+ g.pagesMap.insert({os, {}});
else
- G.Local16.insert({{nullptr, getMipsPageAddr(Sym.getVA(Addend))}, 0});
- } else if (Sym.isTls())
- G.Tls.insert({&Sym, 0});
- else if (Sym.IsPreemptible && Expr == R_ABS)
- G.Relocs.insert({&Sym, 0});
- else if (Sym.IsPreemptible)
- G.Global.insert({&Sym, 0});
- else if (Expr == R_MIPS_GOT_OFF32)
- G.Local32.insert({{&Sym, Addend}, 0});
+ g.local16.insert({{nullptr, getMipsPageAddr(sym.getVA(addend))}, 0});
+ } else if (sym.isTls())
+ g.tls.insert({&sym, 0});
+ else if (sym.isPreemptible && expr == R_ABS)
+ g.relocs.insert({&sym, 0});
+ else if (sym.isPreemptible)
+ g.global.insert({&sym, 0});
+ else if (expr == R_MIPS_GOT_OFF32)
+ g.local32.insert({{&sym, addend}, 0});
else
- G.Local16.insert({{&Sym, Addend}, 0});
+ g.local16.insert({{&sym, addend}, 0});
}
-void MipsGotSection::addDynTlsEntry(InputFile &File, Symbol &Sym) {
- getGot(File).DynTlsSymbols.insert({&Sym, 0});
+void MipsGotSection::addDynTlsEntry(InputFile &file, Symbol &sym) {
+ getGot(file).dynTlsSymbols.insert({&sym, 0});
}
-void MipsGotSection::addTlsIndex(InputFile &File) {
- getGot(File).DynTlsSymbols.insert({nullptr, 0});
+void MipsGotSection::addTlsIndex(InputFile &file) {
+ getGot(file).dynTlsSymbols.insert({nullptr, 0});
}
size_t MipsGotSection::FileGot::getEntriesNum() const {
- return getPageEntriesNum() + Local16.size() + Global.size() + Relocs.size() +
- Tls.size() + DynTlsSymbols.size() * 2;
+ return getPageEntriesNum() + local16.size() + global.size() + relocs.size() +
+ tls.size() + dynTlsSymbols.size() * 2;
}
size_t MipsGotSection::FileGot::getPageEntriesNum() const {
- size_t Num = 0;
- for (const std::pair<const OutputSection *, FileGot::PageBlock> &P : PagesMap)
- Num += P.second.Count;
- return Num;
+ size_t num = 0;
+ for (const std::pair<const OutputSection *, FileGot::PageBlock> &p : pagesMap)
+ num += p.second.count;
+ return num;
}
size_t MipsGotSection::FileGot::getIndexedEntriesNum() const {
- size_t Count = getPageEntriesNum() + Local16.size() + Global.size();
+ size_t count = getPageEntriesNum() + local16.size() + global.size();
// If there are relocation-only entries in the GOT, TLS entries
// are allocated after them. TLS entries should be addressable
// by 16-bit index so count both reloc-only and TLS entries.
- if (!Tls.empty() || !DynTlsSymbols.empty())
- Count += Relocs.size() + Tls.size() + DynTlsSymbols.size() * 2;
- return Count;
+ if (!tls.empty() || !dynTlsSymbols.empty())
+ count += relocs.size() + tls.size() + dynTlsSymbols.size() * 2;
+ return count;
}
-MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &F) {
- if (!F.MipsGotIndex.hasValue()) {
- Gots.emplace_back();
- Gots.back().File = &F;
- F.MipsGotIndex = Gots.size() - 1;
+MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &f) {
+ if (!f.mipsGotIndex.hasValue()) {
+ gots.emplace_back();
+ gots.back().file = &f;
+ f.mipsGotIndex = gots.size() - 1;
}
- return Gots[*F.MipsGotIndex];
+ return gots[*f.mipsGotIndex];
}
-uint64_t MipsGotSection::getPageEntryOffset(const InputFile *F,
- const Symbol &Sym,
- int64_t Addend) const {
- const FileGot &G = Gots[*F->MipsGotIndex];
- uint64_t Index = 0;
- if (const OutputSection *OutSec = Sym.getOutputSection()) {
- uint64_t SecAddr = getMipsPageAddr(OutSec->Addr);
- uint64_t SymAddr = getMipsPageAddr(Sym.getVA(Addend));
- Index = G.PagesMap.lookup(OutSec).FirstIndex + (SymAddr - SecAddr) / 0xffff;
+uint64_t MipsGotSection::getPageEntryOffset(const InputFile *f,
+ const Symbol &sym,
+ int64_t addend) const {
+ const FileGot &g = gots[*f->mipsGotIndex];
+ uint64_t index = 0;
+ if (const OutputSection *outSec = sym.getOutputSection()) {
+ uint64_t secAddr = getMipsPageAddr(outSec->addr);
+ uint64_t symAddr = getMipsPageAddr(sym.getVA(addend));
+ index = g.pagesMap.lookup(outSec).firstIndex + (symAddr - secAddr) / 0xffff;
} else {
- Index = G.Local16.lookup({nullptr, getMipsPageAddr(Sym.getVA(Addend))});
+ index = g.local16.lookup({nullptr, getMipsPageAddr(sym.getVA(addend))});
}
- return Index * Config->Wordsize;
+ return index * config->wordsize;
}
-uint64_t MipsGotSection::getSymEntryOffset(const InputFile *F, const Symbol &S,
- int64_t Addend) const {
- const FileGot &G = Gots[*F->MipsGotIndex];
- Symbol *Sym = const_cast<Symbol *>(&S);
- if (Sym->isTls())
- return G.Tls.lookup(Sym) * Config->Wordsize;
- if (Sym->IsPreemptible)
- return G.Global.lookup(Sym) * Config->Wordsize;
- return G.Local16.lookup({Sym, Addend}) * Config->Wordsize;
+uint64_t MipsGotSection::getSymEntryOffset(const InputFile *f, const Symbol &s,
+ int64_t addend) const {
+ const FileGot &g = gots[*f->mipsGotIndex];
+ Symbol *sym = const_cast<Symbol *>(&s);
+ if (sym->isTls())
+ return g.tls.lookup(sym) * config->wordsize;
+ if (sym->isPreemptible)
+ return g.global.lookup(sym) * config->wordsize;
+ return g.local16.lookup({sym, addend}) * config->wordsize;
}
-uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *F) const {
- const FileGot &G = Gots[*F->MipsGotIndex];
- return G.DynTlsSymbols.lookup(nullptr) * Config->Wordsize;
+uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *f) const {
+ const FileGot &g = gots[*f->mipsGotIndex];
+ return g.dynTlsSymbols.lookup(nullptr) * config->wordsize;
}
-uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *F,
- const Symbol &S) const {
- const FileGot &G = Gots[*F->MipsGotIndex];
- Symbol *Sym = const_cast<Symbol *>(&S);
- return G.DynTlsSymbols.lookup(Sym) * Config->Wordsize;
+uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *f,
+ const Symbol &s) const {
+ const FileGot &g = gots[*f->mipsGotIndex];
+ Symbol *sym = const_cast<Symbol *>(&s);
+ return g.dynTlsSymbols.lookup(sym) * config->wordsize;
}
const Symbol *MipsGotSection::getFirstGlobalEntry() const {
- if (Gots.empty())
+ if (gots.empty())
return nullptr;
- const FileGot &PrimGot = Gots.front();
- if (!PrimGot.Global.empty())
- return PrimGot.Global.front().first;
- if (!PrimGot.Relocs.empty())
- return PrimGot.Relocs.front().first;
+ const FileGot &primGot = gots.front();
+ if (!primGot.global.empty())
+ return primGot.global.front().first;
+ if (!primGot.relocs.empty())
+ return primGot.relocs.front().first;
return nullptr;
}
unsigned MipsGotSection::getLocalEntriesNum() const {
- if (Gots.empty())
- return HeaderEntriesNum;
- return HeaderEntriesNum + Gots.front().getPageEntriesNum() +
- Gots.front().Local16.size();
+ if (gots.empty())
+ return headerEntriesNum;
+ return headerEntriesNum + gots.front().getPageEntriesNum() +
+ gots.front().local16.size();
}
-bool MipsGotSection::tryMergeGots(FileGot &Dst, FileGot &Src, bool IsPrimary) {
- FileGot Tmp = Dst;
- set_union(Tmp.PagesMap, Src.PagesMap);
- set_union(Tmp.Local16, Src.Local16);
- set_union(Tmp.Global, Src.Global);
- set_union(Tmp.Relocs, Src.Relocs);
- set_union(Tmp.Tls, Src.Tls);
- set_union(Tmp.DynTlsSymbols, Src.DynTlsSymbols);
+bool MipsGotSection::tryMergeGots(FileGot &dst, FileGot &src, bool isPrimary) {
+ FileGot tmp = dst;
+ set_union(tmp.pagesMap, src.pagesMap);
+ set_union(tmp.local16, src.local16);
+ set_union(tmp.global, src.global);
+ set_union(tmp.relocs, src.relocs);
+ set_union(tmp.tls, src.tls);
+ set_union(tmp.dynTlsSymbols, src.dynTlsSymbols);
- size_t Count = IsPrimary ? HeaderEntriesNum : 0;
- Count += Tmp.getIndexedEntriesNum();
+ size_t count = isPrimary ? headerEntriesNum : 0;
+ count += tmp.getIndexedEntriesNum();
- if (Count * Config->Wordsize > Config->MipsGotSize)
+ if (count * config->wordsize > config->mipsGotSize)
return false;
- std::swap(Tmp, Dst);
+ std::swap(tmp, dst);
return true;
}
void MipsGotSection::finalizeContents() { updateAllocSize(); }
bool MipsGotSection::updateAllocSize() {
- Size = HeaderEntriesNum * Config->Wordsize;
- for (const FileGot &G : Gots)
- Size += G.getEntriesNum() * Config->Wordsize;
+ size = headerEntriesNum * config->wordsize;
+ for (const FileGot &g : gots)
+ size += g.getEntriesNum() * config->wordsize;
return false;
}
-template <class ELFT> void MipsGotSection::build() {
- if (Gots.empty())
+void MipsGotSection::build() {
+ if (gots.empty())
return;
- std::vector<FileGot> MergedGots(1);
+ std::vector<FileGot> mergedGots(1);
// For each GOT move non-preemptible symbols from the `Global`
// to `Local16` list. Preemptible symbol might become non-preemptible
// one if, for example, it gets a related copy relocation.
- for (FileGot &Got : Gots) {
- for (auto &P: Got.Global)
- if (!P.first->IsPreemptible)
- Got.Local16.insert({{P.first, 0}, 0});
- Got.Global.remove_if([&](const std::pair<Symbol *, size_t> &P) {
- return !P.first->IsPreemptible;
+ for (FileGot &got : gots) {
+ for (auto &p: got.global)
+ if (!p.first->isPreemptible)
+ got.local16.insert({{p.first, 0}, 0});
+ got.global.remove_if([&](const std::pair<Symbol *, size_t> &p) {
+ return !p.first->isPreemptible;
});
}
// For each GOT remove "reloc-only" entry if there is "global"
// entry for the same symbol. And add local entries which indexed
// using 32-bit value at the end of 16-bit entries.
- for (FileGot &Got : Gots) {
- Got.Relocs.remove_if([&](const std::pair<Symbol *, size_t> &P) {
- return Got.Global.count(P.first);
+ for (FileGot &got : gots) {
+ got.relocs.remove_if([&](const std::pair<Symbol *, size_t> &p) {
+ return got.global.count(p.first);
});
- set_union(Got.Local16, Got.Local32);
- Got.Local32.clear();
+ set_union(got.local16, got.local32);
+ got.local32.clear();
}
// Evaluate number of "reloc-only" entries in the resulting GOT.
// To do that put all unique "reloc-only" and "global" entries
// from all GOTs to the future primary GOT.
- FileGot *PrimGot = &MergedGots.front();
- for (FileGot &Got : Gots) {
- set_union(PrimGot->Relocs, Got.Global);
- set_union(PrimGot->Relocs, Got.Relocs);
- Got.Relocs.clear();
+ FileGot *primGot = &mergedGots.front();
+ for (FileGot &got : gots) {
+ set_union(primGot->relocs, got.global);
+ set_union(primGot->relocs, got.relocs);
+ got.relocs.clear();
}
// Evaluate number of "page" entries in each GOT.
- for (FileGot &Got : Gots) {
- for (std::pair<const OutputSection *, FileGot::PageBlock> &P :
- Got.PagesMap) {
- const OutputSection *OS = P.first;
- uint64_t SecSize = 0;
- for (BaseCommand *Cmd : OS->SectionCommands) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
- for (InputSection *IS : ISD->Sections) {
- uint64_t Off = alignTo(SecSize, IS->Alignment);
- SecSize = Off + IS->getSize();
+ for (FileGot &got : gots) {
+ for (std::pair<const OutputSection *, FileGot::PageBlock> &p :
+ got.pagesMap) {
+ const OutputSection *os = p.first;
+ uint64_t secSize = 0;
+ for (BaseCommand *cmd : os->sectionCommands) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
+ for (InputSection *isec : isd->sections) {
+ uint64_t off = alignTo(secSize, isec->alignment);
+ secSize = off + isec->getSize();
}
}
- P.second.Count = getMipsPageCount(SecSize);
+ p.second.count = getMipsPageCount(secSize);
}
}
@@ -868,149 +852,149 @@ template <class ELFT> void MipsGotSection::build() {
// the primary GOT can be accessed in the most effective way. If it
// is not possible, try to fill the last GOT in the list, and finally
// create a new GOT if both attempts failed.
- for (FileGot &SrcGot : Gots) {
- InputFile *File = SrcGot.File;
- if (tryMergeGots(MergedGots.front(), SrcGot, true)) {
- File->MipsGotIndex = 0;
+ for (FileGot &srcGot : gots) {
+ InputFile *file = srcGot.file;
+ if (tryMergeGots(mergedGots.front(), srcGot, true)) {
+ file->mipsGotIndex = 0;
} else {
// If this is the first time we failed to merge with the primary GOT,
// MergedGots.back() will also be the primary GOT. We must make sure not
- // to try to merge again with IsPrimary=false, as otherwise, if the
+ // to try to merge again with isPrimary=false, as otherwise, if the
// inputs are just right, we could allow the primary GOT to become 1 or 2
- // words too big due to ignoring the header size.
- if (MergedGots.size() == 1 ||
- !tryMergeGots(MergedGots.back(), SrcGot, false)) {
- MergedGots.emplace_back();
- std::swap(MergedGots.back(), SrcGot);
+ // words bigger due to ignoring the header size.
+ if (mergedGots.size() == 1 ||
+ !tryMergeGots(mergedGots.back(), srcGot, false)) {
+ mergedGots.emplace_back();
+ std::swap(mergedGots.back(), srcGot);
}
- File->MipsGotIndex = MergedGots.size() - 1;
+ file->mipsGotIndex = mergedGots.size() - 1;
}
}
- std::swap(Gots, MergedGots);
+ std::swap(gots, mergedGots);
// Reduce number of "reloc-only" entries in the primary GOT
// by substracting "global" entries exist in the primary GOT.
- PrimGot = &Gots.front();
- PrimGot->Relocs.remove_if([&](const std::pair<Symbol *, size_t> &P) {
- return PrimGot->Global.count(P.first);
+ primGot = &gots.front();
+ primGot->relocs.remove_if([&](const std::pair<Symbol *, size_t> &p) {
+ return primGot->global.count(p.first);
});
// Calculate indexes for each GOT entry.
- size_t Index = HeaderEntriesNum;
- for (FileGot &Got : Gots) {
- Got.StartIndex = &Got == PrimGot ? 0 : Index;
- for (std::pair<const OutputSection *, FileGot::PageBlock> &P :
- Got.PagesMap) {
+ size_t index = headerEntriesNum;
+ for (FileGot &got : gots) {
+ got.startIndex = &got == primGot ? 0 : index;
+ for (std::pair<const OutputSection *, FileGot::PageBlock> &p :
+ got.pagesMap) {
// For each output section referenced by GOT page relocations calculate
- // and save into PagesMap an upper bound of MIPS GOT entries required
+ // and save into pagesMap an upper bound of MIPS GOT entries required
// to store page addresses of local symbols. We assume the worst case -
// each 64kb page of the output section has at least one GOT relocation
// against it. And take in account the case when the section intersects
// page boundaries.
- P.second.FirstIndex = Index;
- Index += P.second.Count;
+ p.second.firstIndex = index;
+ index += p.second.count;
}
- for (auto &P: Got.Local16)
- P.second = Index++;
- for (auto &P: Got.Global)
- P.second = Index++;
- for (auto &P: Got.Relocs)
- P.second = Index++;
- for (auto &P: Got.Tls)
- P.second = Index++;
- for (auto &P: Got.DynTlsSymbols) {
- P.second = Index;
- Index += 2;
+ for (auto &p: got.local16)
+ p.second = index++;
+ for (auto &p: got.global)
+ p.second = index++;
+ for (auto &p: got.relocs)
+ p.second = index++;
+ for (auto &p: got.tls)
+ p.second = index++;
+ for (auto &p: got.dynTlsSymbols) {
+ p.second = index;
+ index += 2;
}
}
- // Update Symbol::GotIndex field to use this
+ // Update Symbol::gotIndex field to use this
// value later in the `sortMipsSymbols` function.
- for (auto &P : PrimGot->Global)
- P.first->GotIndex = P.second;
- for (auto &P : PrimGot->Relocs)
- P.first->GotIndex = P.second;
+ for (auto &p : primGot->global)
+ p.first->gotIndex = p.second;
+ for (auto &p : primGot->relocs)
+ p.first->gotIndex = p.second;
// Create dynamic relocations.
- for (FileGot &Got : Gots) {
+ for (FileGot &got : gots) {
// Create dynamic relocations for TLS entries.
- for (std::pair<Symbol *, size_t> &P : Got.Tls) {
- Symbol *S = P.first;
- uint64_t Offset = P.second * Config->Wordsize;
- if (S->IsPreemptible)
- In.RelaDyn->addReloc(Target->TlsGotRel, this, Offset, S);
+ for (std::pair<Symbol *, size_t> &p : got.tls) {
+ Symbol *s = p.first;
+ uint64_t offset = p.second * config->wordsize;
+ if (s->isPreemptible)
+ mainPart->relaDyn->addReloc(target->tlsGotRel, this, offset, s);
}
- for (std::pair<Symbol *, size_t> &P : Got.DynTlsSymbols) {
- Symbol *S = P.first;
- uint64_t Offset = P.second * Config->Wordsize;
- if (S == nullptr) {
- if (!Config->Pic)
+ for (std::pair<Symbol *, size_t> &p : got.dynTlsSymbols) {
+ Symbol *s = p.first;
+ uint64_t offset = p.second * config->wordsize;
+ if (s == nullptr) {
+ if (!config->isPic)
continue;
- In.RelaDyn->addReloc(Target->TlsModuleIndexRel, this, Offset, S);
+ mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s);
} else {
// When building a shared library we still need a dynamic relocation
// for the module index. Therefore only checking for
- // S->IsPreemptible is not sufficient (this happens e.g. for
+ // S->isPreemptible is not sufficient (this happens e.g. for
// thread-locals that have been marked as local through a linker script)
- if (!S->IsPreemptible && !Config->Pic)
+ if (!s->isPreemptible && !config->isPic)
continue;
- In.RelaDyn->addReloc(Target->TlsModuleIndexRel, this, Offset, S);
+ mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s);
// However, we can skip writing the TLS offset reloc for non-preemptible
// symbols since it is known even in shared libraries
- if (!S->IsPreemptible)
+ if (!s->isPreemptible)
continue;
- Offset += Config->Wordsize;
- In.RelaDyn->addReloc(Target->TlsOffsetRel, this, Offset, S);
+ offset += config->wordsize;
+ mainPart->relaDyn->addReloc(target->tlsOffsetRel, this, offset, s);
}
}
// Do not create dynamic relocations for non-TLS
// entries in the primary GOT.
- if (&Got == PrimGot)
+ if (&got == primGot)
continue;
// Dynamic relocations for "global" entries.
- for (const std::pair<Symbol *, size_t> &P : Got.Global) {
- uint64_t Offset = P.second * Config->Wordsize;
- In.RelaDyn->addReloc(Target->RelativeRel, this, Offset, P.first);
+ for (const std::pair<Symbol *, size_t> &p : got.global) {
+ uint64_t offset = p.second * config->wordsize;
+ mainPart->relaDyn->addReloc(target->relativeRel, this, offset, p.first);
}
- if (!Config->Pic)
+ if (!config->isPic)
continue;
// Dynamic relocations for "local" entries in case of PIC.
- for (const std::pair<const OutputSection *, FileGot::PageBlock> &L :
- Got.PagesMap) {
- size_t PageCount = L.second.Count;
- for (size_t PI = 0; PI < PageCount; ++PI) {
- uint64_t Offset = (L.second.FirstIndex + PI) * Config->Wordsize;
- In.RelaDyn->addReloc({Target->RelativeRel, this, Offset, L.first,
- int64_t(PI * 0x10000)});
+ for (const std::pair<const OutputSection *, FileGot::PageBlock> &l :
+ got.pagesMap) {
+ size_t pageCount = l.second.count;
+ for (size_t pi = 0; pi < pageCount; ++pi) {
+ uint64_t offset = (l.second.firstIndex + pi) * config->wordsize;
+ mainPart->relaDyn->addReloc({target->relativeRel, this, offset, l.first,
+ int64_t(pi * 0x10000)});
}
}
- for (const std::pair<GotEntry, size_t> &P : Got.Local16) {
- uint64_t Offset = P.second * Config->Wordsize;
- In.RelaDyn->addReloc({Target->RelativeRel, this, Offset, true,
- P.first.first, P.first.second});
+ for (const std::pair<GotEntry, size_t> &p : got.local16) {
+ uint64_t offset = p.second * config->wordsize;
+ mainPart->relaDyn->addReloc({target->relativeRel, this, offset, true,
+ p.first.first, p.first.second});
}
}
}
-bool MipsGotSection::empty() const {
+bool MipsGotSection::isNeeded() const {
// We add the .got section to the result for dynamic MIPS target because
// its address and properties are mentioned in the .dynamic section.
- return Config->Relocatable;
+ return !config->relocatable;
}
-uint64_t MipsGotSection::getGp(const InputFile *F) const {
+uint64_t MipsGotSection::getGp(const InputFile *f) const {
// For files without related GOT or files refer a primary GOT
// returns "common" _gp value. For secondary GOTs calculate
// individual _gp values.
- if (!F || !F->MipsGotIndex.hasValue() || *F->MipsGotIndex == 0)
- return ElfSym::MipsGp->getVA(0);
- return getVA() + Gots[*F->MipsGotIndex].StartIndex * Config->Wordsize +
+ if (!f || !f->mipsGotIndex.hasValue() || *f->mipsGotIndex == 0)
+ return ElfSym::mipsGp->getVA(0);
+ return getVA() + gots[*f->mipsGotIndex].startIndex * config->wordsize +
0x7ff0;
}
-void MipsGotSection::writeTo(uint8_t *Buf) {
+void MipsGotSection::writeTo(uint8_t *buf) {
// Set the MSB of the second GOT slot. This is not required by any
// MIPS ABI documentation, though.
//
@@ -1025,51 +1009,48 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
// we've been doing this for years, it is probably a safe bet to
// keep doing this for now. We really need to revisit this to see
// if we had to do this.
- writeUint(Buf + Config->Wordsize, (uint64_t)1 << (Config->Wordsize * 8 - 1));
- for (const FileGot &G : Gots) {
- auto Write = [&](size_t I, const Symbol *S, int64_t A) {
- uint64_t VA = A;
- if (S) {
- VA = S->getVA(A);
- if (S->StOther & STO_MIPS_MICROMIPS)
- VA |= 1;
- }
- writeUint(Buf + I * Config->Wordsize, VA);
+ writeUint(buf + config->wordsize, (uint64_t)1 << (config->wordsize * 8 - 1));
+ for (const FileGot &g : gots) {
+ auto write = [&](size_t i, const Symbol *s, int64_t a) {
+ uint64_t va = a;
+ if (s)
+ va = s->getVA(a);
+ writeUint(buf + i * config->wordsize, va);
};
// Write 'page address' entries to the local part of the GOT.
- for (const std::pair<const OutputSection *, FileGot::PageBlock> &L :
- G.PagesMap) {
- size_t PageCount = L.second.Count;
- uint64_t FirstPageAddr = getMipsPageAddr(L.first->Addr);
- for (size_t PI = 0; PI < PageCount; ++PI)
- Write(L.second.FirstIndex + PI, nullptr, FirstPageAddr + PI * 0x10000);
+ for (const std::pair<const OutputSection *, FileGot::PageBlock> &l :
+ g.pagesMap) {
+ size_t pageCount = l.second.count;
+ uint64_t firstPageAddr = getMipsPageAddr(l.first->addr);
+ for (size_t pi = 0; pi < pageCount; ++pi)
+ write(l.second.firstIndex + pi, nullptr, firstPageAddr + pi * 0x10000);
}
// Local, global, TLS, reloc-only entries.
// If TLS entry has a corresponding dynamic relocations, leave it
// initialized by zero. Write down adjusted TLS symbol's values otherwise.
// To calculate the adjustments use offsets for thread-local storage.
// https://www.linux-mips.org/wiki/NPTL
- for (const std::pair<GotEntry, size_t> &P : G.Local16)
- Write(P.second, P.first.first, P.first.second);
+ for (const std::pair<GotEntry, size_t> &p : g.local16)
+ write(p.second, p.first.first, p.first.second);
// Write VA to the primary GOT only. For secondary GOTs that
// will be done by REL32 dynamic relocations.
- if (&G == &Gots.front())
- for (const std::pair<const Symbol *, size_t> &P : G.Global)
- Write(P.second, P.first, 0);
- for (const std::pair<Symbol *, size_t> &P : G.Relocs)
- Write(P.second, P.first, 0);
- for (const std::pair<Symbol *, size_t> &P : G.Tls)
- Write(P.second, P.first, P.first->IsPreemptible ? 0 : -0x7000);
- for (const std::pair<Symbol *, size_t> &P : G.DynTlsSymbols) {
- if (P.first == nullptr && !Config->Pic)
- Write(P.second, nullptr, 1);
- else if (P.first && !P.first->IsPreemptible) {
+ if (&g == &gots.front())
+ for (const std::pair<const Symbol *, size_t> &p : g.global)
+ write(p.second, p.first, 0);
+ for (const std::pair<Symbol *, size_t> &p : g.relocs)
+ write(p.second, p.first, 0);
+ for (const std::pair<Symbol *, size_t> &p : g.tls)
+ write(p.second, p.first, p.first->isPreemptible ? 0 : -0x7000);
+ for (const std::pair<Symbol *, size_t> &p : g.dynTlsSymbols) {
+ if (p.first == nullptr && !config->isPic)
+ write(p.second, nullptr, 1);
+ else if (p.first && !p.first->isPreemptible) {
// If we are emitting PIC code with relocations we mustn't write
// anything to the GOT here. When using Elf_Rel relocations the value
// one will be treated as an addend and will cause crashes at runtime
- if (!Config->Pic)
- Write(P.second, nullptr, 1);
- Write(P.second + 1, P.first, -0x8000);
+ if (!config->isPic)
+ write(p.second, nullptr, 1);
+ write(p.second + 1, p.first, -0x8000);
}
}
}
@@ -1080,46 +1061,48 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
// section. I don't know why we have a BSS style type for the section but it is
// consitent across both 64-bit PowerPC ABIs as well as the 32-bit PowerPC ABI.
GotPltSection::GotPltSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE,
- Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
- Target->GotPltEntrySize,
- Config->EMachine == EM_PPC64 ? ".plt" : ".got.plt") {}
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
+ ".got.plt") {
+ if (config->emachine == EM_PPC) {
+ name = ".plt";
+ } else if (config->emachine == EM_PPC64) {
+ type = SHT_NOBITS;
+ name = ".plt";
+ }
+}
-void GotPltSection::addEntry(Symbol &Sym) {
- assert(Sym.PltIndex == Entries.size());
- Entries.push_back(&Sym);
+void GotPltSection::addEntry(Symbol &sym) {
+ assert(sym.pltIndex == entries.size());
+ entries.push_back(&sym);
}
size_t GotPltSection::getSize() const {
- return (Target->GotPltHeaderEntriesNum + Entries.size()) *
- Target->GotPltEntrySize;
+ return (target->gotPltHeaderEntriesNum + entries.size()) * config->wordsize;
}
-void GotPltSection::writeTo(uint8_t *Buf) {
- Target->writeGotPltHeader(Buf);
- Buf += Target->GotPltHeaderEntriesNum * Target->GotPltEntrySize;
- for (const Symbol *B : Entries) {
- Target->writeGotPlt(Buf, *B);
- Buf += Config->Wordsize;
+void GotPltSection::writeTo(uint8_t *buf) {
+ target->writeGotPltHeader(buf);
+ buf += target->gotPltHeaderEntriesNum * config->wordsize;
+ for (const Symbol *b : entries) {
+ target->writeGotPlt(buf, *b);
+ buf += config->wordsize;
}
}
-bool GotPltSection::empty() const {
- // We need to emit a GOT.PLT even if it's empty if there's a symbol that
- // references the _GLOBAL_OFFSET_TABLE_ and the Target defines the symbol
- // relative to the .got.plt section.
- return Entries.empty() &&
- !(ElfSym::GlobalOffsetTable && Target->GotBaseSymInGotPlt);
+bool GotPltSection::isNeeded() const {
+ // We need to emit GOTPLT even if it's empty if there's a relocation relative
+ // to it.
+ return !entries.empty() || hasGotPltOffRel;
}
static StringRef getIgotPltName() {
// On ARM the IgotPltSection is part of the GotSection.
- if (Config->EMachine == EM_ARM)
+ if (config->emachine == EM_ARM)
return ".got";
// On PowerPC64 the GotPltSection is renamed to '.plt' so the IgotPltSection
// needs to be named the same.
- if (Config->EMachine == EM_PPC64)
+ if (config->emachine == EM_PPC64)
return ".plt";
return ".got.plt";
@@ -1129,130 +1112,110 @@ static StringRef getIgotPltName() {
// with the IgotPltSection.
IgotPltSection::IgotPltSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE,
- Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
- Target->GotPltEntrySize, getIgotPltName()) {}
+ config->emachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
+ config->wordsize, getIgotPltName()) {}
-void IgotPltSection::addEntry(Symbol &Sym) {
- Sym.IsInIgot = true;
- assert(Sym.PltIndex == Entries.size());
- Entries.push_back(&Sym);
+void IgotPltSection::addEntry(Symbol &sym) {
+ assert(sym.pltIndex == entries.size());
+ entries.push_back(&sym);
}
size_t IgotPltSection::getSize() const {
- return Entries.size() * Target->GotPltEntrySize;
+ return entries.size() * config->wordsize;
}
-void IgotPltSection::writeTo(uint8_t *Buf) {
- for (const Symbol *B : Entries) {
- Target->writeIgotPlt(Buf, *B);
- Buf += Config->Wordsize;
+void IgotPltSection::writeTo(uint8_t *buf) {
+ for (const Symbol *b : entries) {
+ target->writeIgotPlt(buf, *b);
+ buf += config->wordsize;
}
}
-StringTableSection::StringTableSection(StringRef Name, bool Dynamic)
- : SyntheticSection(Dynamic ? (uint64_t)SHF_ALLOC : 0, SHT_STRTAB, 1, Name),
- Dynamic(Dynamic) {
+StringTableSection::StringTableSection(StringRef name, bool dynamic)
+ : SyntheticSection(dynamic ? (uint64_t)SHF_ALLOC : 0, SHT_STRTAB, 1, name),
+ dynamic(dynamic) {
// ELF string tables start with a NUL byte.
addString("");
}
-// Adds a string to the string table. If HashIt is true we hash and check for
+// Adds a string to the string table. If `hashIt` is true we hash and check for
// duplicates. It is optional because the name of global symbols are already
// uniqued and hashing them again has a big cost for a small value: uniquing
// them with some other string that happens to be the same.
-unsigned StringTableSection::addString(StringRef S, bool HashIt) {
- if (HashIt) {
- auto R = StringMap.insert(std::make_pair(S, this->Size));
- if (!R.second)
- return R.first->second;
+unsigned StringTableSection::addString(StringRef s, bool hashIt) {
+ if (hashIt) {
+ auto r = stringMap.insert(std::make_pair(s, this->size));
+ if (!r.second)
+ return r.first->second;
}
- unsigned Ret = this->Size;
- this->Size = this->Size + S.size() + 1;
- Strings.push_back(S);
- return Ret;
+ unsigned ret = this->size;
+ this->size = this->size + s.size() + 1;
+ strings.push_back(s);
+ return ret;
}
-void StringTableSection::writeTo(uint8_t *Buf) {
- for (StringRef S : Strings) {
- memcpy(Buf, S.data(), S.size());
- Buf[S.size()] = '\0';
- Buf += S.size() + 1;
+void StringTableSection::writeTo(uint8_t *buf) {
+ for (StringRef s : strings) {
+ memcpy(buf, s.data(), s.size());
+ buf[s.size()] = '\0';
+ buf += s.size() + 1;
}
}
// Returns the number of version definition entries. Because the first entry
// is for the version definition itself, it is the number of versioned symbols
// plus one. Note that we don't support multiple versions yet.
-static unsigned getVerDefNum() { return Config->VersionDefinitions.size() + 1; }
+static unsigned getVerDefNum() { return config->versionDefinitions.size() + 1; }
template <class ELFT>
DynamicSection<ELFT>::DynamicSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_DYNAMIC, Config->Wordsize,
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_DYNAMIC, config->wordsize,
".dynamic") {
- this->Entsize = ELFT::Is64Bits ? 16 : 8;
+ this->entsize = ELFT::Is64Bits ? 16 : 8;
// .dynamic section is not writable on MIPS and on Fuchsia OS
// which passes -z rodynamic.
// See "Special Section" in Chapter 4 in the following document:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- if (Config->EMachine == EM_MIPS || Config->ZRodynamic)
- this->Flags = SHF_ALLOC;
-
- // Add strings to .dynstr early so that .dynstr's size will be
- // fixed early.
- for (StringRef S : Config->FilterList)
- addInt(DT_FILTER, In.DynStrTab->addString(S));
- for (StringRef S : Config->AuxiliaryList)
- addInt(DT_AUXILIARY, In.DynStrTab->addString(S));
-
- if (!Config->Rpath.empty())
- addInt(Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH,
- In.DynStrTab->addString(Config->Rpath));
-
- for (InputFile *File : SharedFiles) {
- SharedFile<ELFT> *F = cast<SharedFile<ELFT>>(File);
- if (F->IsNeeded)
- addInt(DT_NEEDED, In.DynStrTab->addString(F->SoName));
- }
- if (!Config->SoName.empty())
- addInt(DT_SONAME, In.DynStrTab->addString(Config->SoName));
+ if (config->emachine == EM_MIPS || config->zRodynamic)
+ this->flags = SHF_ALLOC;
}
template <class ELFT>
-void DynamicSection<ELFT>::add(int32_t Tag, std::function<uint64_t()> Fn) {
- Entries.push_back({Tag, Fn});
+void DynamicSection<ELFT>::add(int32_t tag, std::function<uint64_t()> fn) {
+ entries.push_back({tag, fn});
}
template <class ELFT>
-void DynamicSection<ELFT>::addInt(int32_t Tag, uint64_t Val) {
- Entries.push_back({Tag, [=] { return Val; }});
+void DynamicSection<ELFT>::addInt(int32_t tag, uint64_t val) {
+ entries.push_back({tag, [=] { return val; }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addInSec(int32_t Tag, InputSection *Sec) {
- Entries.push_back({Tag, [=] { return Sec->getVA(0); }});
+void DynamicSection<ELFT>::addInSec(int32_t tag, InputSection *sec) {
+ entries.push_back({tag, [=] { return sec->getVA(0); }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addInSecRelative(int32_t Tag, InputSection *Sec) {
- size_t TagOffset = Entries.size() * Entsize;
- Entries.push_back(
- {Tag, [=] { return Sec->getVA(0) - (getVA() + TagOffset); }});
+void DynamicSection<ELFT>::addInSecRelative(int32_t tag, InputSection *sec) {
+ size_t tagOffset = entries.size() * entsize;
+ entries.push_back(
+ {tag, [=] { return sec->getVA(0) - (getVA() + tagOffset); }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addOutSec(int32_t Tag, OutputSection *Sec) {
- Entries.push_back({Tag, [=] { return Sec->Addr; }});
+void DynamicSection<ELFT>::addOutSec(int32_t tag, OutputSection *sec) {
+ entries.push_back({tag, [=] { return sec->addr; }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addSize(int32_t Tag, OutputSection *Sec) {
- Entries.push_back({Tag, [=] { return Sec->Size; }});
+void DynamicSection<ELFT>::addSize(int32_t tag, OutputSection *sec) {
+ entries.push_back({tag, [=] { return sec->size; }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addSym(int32_t Tag, Symbol *Sym) {
- Entries.push_back({Tag, [=] { return Sym->getVA(); }});
+void DynamicSection<ELFT>::addSym(int32_t tag, Symbol *sym) {
+ entries.push_back({tag, [=] { return sym->getVA(); }});
}
// A Linker script may assign the RELA relocation sections to the same
@@ -1260,47 +1223,74 @@ void DynamicSection<ELFT>::addSym(int32_t Tag, Symbol *Sym) {
// Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to
// overlap with the [DT_RELA, DT_RELA + DT_RELASZ).
static uint64_t addPltRelSz() {
- size_t Size = In.RelaPlt->getSize();
- if (In.RelaIplt->getParent() == In.RelaPlt->getParent() &&
- In.RelaIplt->Name == In.RelaPlt->Name)
- Size += In.RelaIplt->getSize();
- return Size;
+ size_t size = in.relaPlt->getSize();
+ if (in.relaIplt->getParent() == in.relaPlt->getParent() &&
+ in.relaIplt->name == in.relaPlt->name)
+ size += in.relaIplt->getSize();
+ return size;
}
// Add remaining entries to complete .dynamic contents.
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
+ elf::Partition &part = getPartition();
+ bool isMain = part.name.empty();
+
+ for (StringRef s : config->filterList)
+ addInt(DT_FILTER, part.dynStrTab->addString(s));
+ for (StringRef s : config->auxiliaryList)
+ addInt(DT_AUXILIARY, part.dynStrTab->addString(s));
+
+ if (!config->rpath.empty())
+ addInt(config->enableNewDtags ? DT_RUNPATH : DT_RPATH,
+ part.dynStrTab->addString(config->rpath));
+
+ for (SharedFile *file : sharedFiles)
+ if (file->isNeeded)
+ addInt(DT_NEEDED, part.dynStrTab->addString(file->soName));
+
+ if (isMain) {
+ if (!config->soName.empty())
+ addInt(DT_SONAME, part.dynStrTab->addString(config->soName));
+ } else {
+ if (!config->soName.empty())
+ addInt(DT_NEEDED, part.dynStrTab->addString(config->soName));
+ addInt(DT_SONAME, part.dynStrTab->addString(part.name));
+ }
+
// Set DT_FLAGS and DT_FLAGS_1.
- uint32_t DtFlags = 0;
- uint32_t DtFlags1 = 0;
- if (Config->Bsymbolic)
- DtFlags |= DF_SYMBOLIC;
- if (Config->ZGlobal)
- DtFlags1 |= DF_1_GLOBAL;
- if (Config->ZInitfirst)
- DtFlags1 |= DF_1_INITFIRST;
- if (Config->ZInterpose)
- DtFlags1 |= DF_1_INTERPOSE;
- if (Config->ZNodefaultlib)
- DtFlags1 |= DF_1_NODEFLIB;
- if (Config->ZNodelete)
- DtFlags1 |= DF_1_NODELETE;
- if (Config->ZNodlopen)
- DtFlags1 |= DF_1_NOOPEN;
- if (Config->ZNow) {
- DtFlags |= DF_BIND_NOW;
- DtFlags1 |= DF_1_NOW;
- }
- if (Config->ZOrigin) {
- DtFlags |= DF_ORIGIN;
- DtFlags1 |= DF_1_ORIGIN;
- }
- if (!Config->ZText)
- DtFlags |= DF_TEXTREL;
-
- if (DtFlags)
- addInt(DT_FLAGS, DtFlags);
- if (DtFlags1)
- addInt(DT_FLAGS_1, DtFlags1);
+ uint32_t dtFlags = 0;
+ uint32_t dtFlags1 = 0;
+ if (config->bsymbolic)
+ dtFlags |= DF_SYMBOLIC;
+ if (config->zGlobal)
+ dtFlags1 |= DF_1_GLOBAL;
+ if (config->zInitfirst)
+ dtFlags1 |= DF_1_INITFIRST;
+ if (config->zInterpose)
+ dtFlags1 |= DF_1_INTERPOSE;
+ if (config->zNodefaultlib)
+ dtFlags1 |= DF_1_NODEFLIB;
+ if (config->zNodelete)
+ dtFlags1 |= DF_1_NODELETE;
+ if (config->zNodlopen)
+ dtFlags1 |= DF_1_NOOPEN;
+ if (config->zNow) {
+ dtFlags |= DF_BIND_NOW;
+ dtFlags1 |= DF_1_NOW;
+ }
+ if (config->zOrigin) {
+ dtFlags |= DF_ORIGIN;
+ dtFlags1 |= DF_1_ORIGIN;
+ }
+ if (!config->zText)
+ dtFlags |= DF_TEXTREL;
+ if (config->hasStaticTlsModel)
+ dtFlags |= DF_STATIC_TLS;
+
+ if (dtFlags)
+ addInt(DT_FLAGS, dtFlags);
+ if (dtFlags1)
+ addInt(DT_FLAGS_1, dtFlags1);
// DT_DEBUG is a pointer to debug informaion used by debuggers at runtime. We
// need it for each process, so we don't write it for DSOs. The loader writes
@@ -1310,266 +1300,287 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
// systems (currently only Fuchsia OS) provide other means to give the
// debugger this information. Such systems may choose make .dynamic read-only.
// If the target is such a system (used -z rodynamic) don't write DT_DEBUG.
- if (!Config->Shared && !Config->Relocatable && !Config->ZRodynamic)
+ if (!config->shared && !config->relocatable && !config->zRodynamic)
addInt(DT_DEBUG, 0);
- if (OutputSection *Sec = In.DynStrTab->getParent())
- this->Link = Sec->SectionIndex;
+ if (OutputSection *sec = part.dynStrTab->getParent())
+ this->link = sec->sectionIndex;
- if (!In.RelaDyn->empty()) {
- addInSec(In.RelaDyn->DynamicTag, In.RelaDyn);
- addSize(In.RelaDyn->SizeDynamicTag, In.RelaDyn->getParent());
+ if (part.relaDyn->isNeeded()) {
+ addInSec(part.relaDyn->dynamicTag, part.relaDyn);
+ addSize(part.relaDyn->sizeDynamicTag, part.relaDyn->getParent());
- bool IsRela = Config->IsRela;
- addInt(IsRela ? DT_RELAENT : DT_RELENT,
- IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel));
+ bool isRela = config->isRela;
+ addInt(isRela ? DT_RELAENT : DT_RELENT,
+ isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel));
// MIPS dynamic loader does not support RELCOUNT tag.
// The problem is in the tight relation between dynamic
// relocations and GOT. So do not emit this tag on MIPS.
- if (Config->EMachine != EM_MIPS) {
- size_t NumRelativeRels = In.RelaDyn->getRelativeRelocCount();
- if (Config->ZCombreloc && NumRelativeRels)
- addInt(IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels);
+ if (config->emachine != EM_MIPS) {
+ size_t numRelativeRels = part.relaDyn->getRelativeRelocCount();
+ if (config->zCombreloc && numRelativeRels)
+ addInt(isRela ? DT_RELACOUNT : DT_RELCOUNT, numRelativeRels);
}
}
- if (In.RelrDyn && !In.RelrDyn->Relocs.empty()) {
- addInSec(Config->UseAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR,
- In.RelrDyn);
- addSize(Config->UseAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ,
- In.RelrDyn->getParent());
- addInt(Config->UseAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT,
+ if (part.relrDyn && !part.relrDyn->relocs.empty()) {
+ addInSec(config->useAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR,
+ part.relrDyn);
+ addSize(config->useAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ,
+ part.relrDyn->getParent());
+ addInt(config->useAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT,
sizeof(Elf_Relr));
}
// .rel[a].plt section usually consists of two parts, containing plt and
// iplt relocations. It is possible to have only iplt relocations in the
- // output. In that case RelaPlt is empty and have zero offset, the same offset
- // as RelaIplt have. And we still want to emit proper dynamic tags for that
- // case, so here we always use RelaPlt as marker for the begining of
+ // output. In that case relaPlt is empty and have zero offset, the same offset
+ // as relaIplt has. And we still want to emit proper dynamic tags for that
+ // case, so here we always use relaPlt as marker for the begining of
// .rel[a].plt section.
- if (In.RelaPlt->getParent()->Live) {
- addInSec(DT_JMPREL, In.RelaPlt);
- Entries.push_back({DT_PLTRELSZ, addPltRelSz});
- switch (Config->EMachine) {
+ if (isMain && (in.relaPlt->isNeeded() || in.relaIplt->isNeeded())) {
+ addInSec(DT_JMPREL, in.relaPlt);
+ entries.push_back({DT_PLTRELSZ, addPltRelSz});
+ switch (config->emachine) {
case EM_MIPS:
- addInSec(DT_MIPS_PLTGOT, In.GotPlt);
+ addInSec(DT_MIPS_PLTGOT, in.gotPlt);
break;
case EM_SPARCV9:
- addInSec(DT_PLTGOT, In.Plt);
+ addInSec(DT_PLTGOT, in.plt);
break;
default:
- addInSec(DT_PLTGOT, In.GotPlt);
+ addInSec(DT_PLTGOT, in.gotPlt);
break;
}
- addInt(DT_PLTREL, Config->IsRela ? DT_RELA : DT_REL);
+ addInt(DT_PLTREL, config->isRela ? DT_RELA : DT_REL);
+ }
+
+ if (config->emachine == EM_AARCH64) {
+ if (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
+ addInt(DT_AARCH64_BTI_PLT, 0);
+ if (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC)
+ addInt(DT_AARCH64_PAC_PLT, 0);
}
- addInSec(DT_SYMTAB, In.DynSymTab);
+ addInSec(DT_SYMTAB, part.dynSymTab);
addInt(DT_SYMENT, sizeof(Elf_Sym));
- addInSec(DT_STRTAB, In.DynStrTab);
- addInt(DT_STRSZ, In.DynStrTab->getSize());
- if (!Config->ZText)
+ addInSec(DT_STRTAB, part.dynStrTab);
+ addInt(DT_STRSZ, part.dynStrTab->getSize());
+ if (!config->zText)
addInt(DT_TEXTREL, 0);
- if (In.GnuHashTab)
- addInSec(DT_GNU_HASH, In.GnuHashTab);
- if (In.HashTab)
- addInSec(DT_HASH, In.HashTab);
-
- if (Out::PreinitArray) {
- addOutSec(DT_PREINIT_ARRAY, Out::PreinitArray);
- addSize(DT_PREINIT_ARRAYSZ, Out::PreinitArray);
- }
- if (Out::InitArray) {
- addOutSec(DT_INIT_ARRAY, Out::InitArray);
- addSize(DT_INIT_ARRAYSZ, Out::InitArray);
- }
- if (Out::FiniArray) {
- addOutSec(DT_FINI_ARRAY, Out::FiniArray);
- addSize(DT_FINI_ARRAYSZ, Out::FiniArray);
- }
-
- if (Symbol *B = Symtab->find(Config->Init))
- if (B->isDefined())
- addSym(DT_INIT, B);
- if (Symbol *B = Symtab->find(Config->Fini))
- if (B->isDefined())
- addSym(DT_FINI, B);
-
- bool HasVerNeed = InX<ELFT>::VerNeed->getNeedNum() != 0;
- if (HasVerNeed || In.VerDef)
- addInSec(DT_VERSYM, InX<ELFT>::VerSym);
- if (In.VerDef) {
- addInSec(DT_VERDEF, In.VerDef);
+ if (part.gnuHashTab)
+ addInSec(DT_GNU_HASH, part.gnuHashTab);
+ if (part.hashTab)
+ addInSec(DT_HASH, part.hashTab);
+
+ if (isMain) {
+ if (Out::preinitArray) {
+ addOutSec(DT_PREINIT_ARRAY, Out::preinitArray);
+ addSize(DT_PREINIT_ARRAYSZ, Out::preinitArray);
+ }
+ if (Out::initArray) {
+ addOutSec(DT_INIT_ARRAY, Out::initArray);
+ addSize(DT_INIT_ARRAYSZ, Out::initArray);
+ }
+ if (Out::finiArray) {
+ addOutSec(DT_FINI_ARRAY, Out::finiArray);
+ addSize(DT_FINI_ARRAYSZ, Out::finiArray);
+ }
+
+ if (Symbol *b = symtab->find(config->init))
+ if (b->isDefined())
+ addSym(DT_INIT, b);
+ if (Symbol *b = symtab->find(config->fini))
+ if (b->isDefined())
+ addSym(DT_FINI, b);
+ }
+
+ bool hasVerNeed = SharedFile::vernauxNum != 0;
+ if (hasVerNeed || part.verDef)
+ addInSec(DT_VERSYM, part.verSym);
+ if (part.verDef) {
+ addInSec(DT_VERDEF, part.verDef);
addInt(DT_VERDEFNUM, getVerDefNum());
}
- if (HasVerNeed) {
- addInSec(DT_VERNEED, InX<ELFT>::VerNeed);
- addInt(DT_VERNEEDNUM, InX<ELFT>::VerNeed->getNeedNum());
+ if (hasVerNeed) {
+ addInSec(DT_VERNEED, part.verNeed);
+ unsigned needNum = 0;
+ for (SharedFile *f : sharedFiles)
+ if (!f->vernauxs.empty())
+ ++needNum;
+ addInt(DT_VERNEEDNUM, needNum);
}
- if (Config->EMachine == EM_MIPS) {
+ if (config->emachine == EM_MIPS) {
addInt(DT_MIPS_RLD_VERSION, 1);
addInt(DT_MIPS_FLAGS, RHF_NOTPOT);
- addInt(DT_MIPS_BASE_ADDRESS, Target->getImageBase());
- addInt(DT_MIPS_SYMTABNO, In.DynSymTab->getNumSymbols());
+ addInt(DT_MIPS_BASE_ADDRESS, target->getImageBase());
+ addInt(DT_MIPS_SYMTABNO, part.dynSymTab->getNumSymbols());
- add(DT_MIPS_LOCAL_GOTNO, [] { return In.MipsGot->getLocalEntriesNum(); });
+ add(DT_MIPS_LOCAL_GOTNO, [] { return in.mipsGot->getLocalEntriesNum(); });
- if (const Symbol *B = In.MipsGot->getFirstGlobalEntry())
- addInt(DT_MIPS_GOTSYM, B->DynsymIndex);
+ if (const Symbol *b = in.mipsGot->getFirstGlobalEntry())
+ addInt(DT_MIPS_GOTSYM, b->dynsymIndex);
else
- addInt(DT_MIPS_GOTSYM, In.DynSymTab->getNumSymbols());
- addInSec(DT_PLTGOT, In.MipsGot);
- if (In.MipsRldMap) {
- if (!Config->Pie)
- addInSec(DT_MIPS_RLD_MAP, In.MipsRldMap);
+ addInt(DT_MIPS_GOTSYM, part.dynSymTab->getNumSymbols());
+ addInSec(DT_PLTGOT, in.mipsGot);
+ if (in.mipsRldMap) {
+ if (!config->pie)
+ addInSec(DT_MIPS_RLD_MAP, in.mipsRldMap);
// Store the offset to the .rld_map section
// relative to the address of the tag.
- addInSecRelative(DT_MIPS_RLD_MAP_REL, In.MipsRldMap);
+ addInSecRelative(DT_MIPS_RLD_MAP_REL, in.mipsRldMap);
}
}
+ // DT_PPC_GOT indicates to glibc Secure PLT is used. If DT_PPC_GOT is absent,
+ // glibc assumes the old-style BSS PLT layout which we don't support.
+ if (config->emachine == EM_PPC)
+ add(DT_PPC_GOT, [] { return in.got->getVA(); });
+
// Glink dynamic tag is required by the V2 abi if the plt section isn't empty.
- if (Config->EMachine == EM_PPC64 && !In.Plt->empty()) {
+ if (config->emachine == EM_PPC64 && in.plt->isNeeded()) {
// The Glink tag points to 32 bytes before the first lazy symbol resolution
// stub, which starts directly after the header.
- Entries.push_back({DT_PPC64_GLINK, [=] {
- unsigned Offset = Target->PltHeaderSize - 32;
- return In.Plt->getVA(0) + Offset;
+ entries.push_back({DT_PPC64_GLINK, [=] {
+ unsigned offset = target->pltHeaderSize - 32;
+ return in.plt->getVA(0) + offset;
}});
}
addInt(DT_NULL, 0);
- getParent()->Link = this->Link;
- this->Size = Entries.size() * this->Entsize;
+ getParent()->link = this->link;
+ this->size = entries.size() * this->entsize;
}
-template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
- auto *P = reinterpret_cast<Elf_Dyn *>(Buf);
+template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *buf) {
+ auto *p = reinterpret_cast<Elf_Dyn *>(buf);
- for (std::pair<int32_t, std::function<uint64_t()>> &KV : Entries) {
- P->d_tag = KV.first;
- P->d_un.d_val = KV.second();
- ++P;
+ for (std::pair<int32_t, std::function<uint64_t()>> &kv : entries) {
+ p->d_tag = kv.first;
+ p->d_un.d_val = kv.second();
+ ++p;
}
}
uint64_t DynamicReloc::getOffset() const {
- return InputSec->getVA(OffsetInSec);
+ return inputSec->getVA(offsetInSec);
}
int64_t DynamicReloc::computeAddend() const {
- if (UseSymVA)
- return Sym->getVA(Addend);
- if (!OutputSec)
- return Addend;
+ if (useSymVA)
+ return sym->getVA(addend);
+ if (!outputSec)
+ return addend;
// See the comment in the DynamicReloc ctor.
- return getMipsPageAddr(OutputSec->Addr) + Addend;
+ return getMipsPageAddr(outputSec->addr) + addend;
}
-uint32_t DynamicReloc::getSymIndex() const {
- if (Sym && !UseSymVA)
- return Sym->DynsymIndex;
+uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const {
+ if (sym && !useSymVA)
+ return symTab->getSymbolIndex(sym);
return 0;
}
-RelocationBaseSection::RelocationBaseSection(StringRef Name, uint32_t Type,
- int32_t DynamicTag,
- int32_t SizeDynamicTag)
- : SyntheticSection(SHF_ALLOC, Type, Config->Wordsize, Name),
- DynamicTag(DynamicTag), SizeDynamicTag(SizeDynamicTag) {}
+RelocationBaseSection::RelocationBaseSection(StringRef name, uint32_t type,
+ int32_t dynamicTag,
+ int32_t sizeDynamicTag)
+ : SyntheticSection(SHF_ALLOC, type, config->wordsize, name),
+ dynamicTag(dynamicTag), sizeDynamicTag(sizeDynamicTag) {}
-void RelocationBaseSection::addReloc(RelType DynType, InputSectionBase *IS,
- uint64_t OffsetInSec, Symbol *Sym) {
- addReloc({DynType, IS, OffsetInSec, false, Sym, 0});
+void RelocationBaseSection::addReloc(RelType dynType, InputSectionBase *isec,
+ uint64_t offsetInSec, Symbol *sym) {
+ addReloc({dynType, isec, offsetInSec, false, sym, 0});
}
-void RelocationBaseSection::addReloc(RelType DynType,
- InputSectionBase *InputSec,
- uint64_t OffsetInSec, Symbol *Sym,
- int64_t Addend, RelExpr Expr,
- RelType Type) {
+void RelocationBaseSection::addReloc(RelType dynType,
+ InputSectionBase *inputSec,
+ uint64_t offsetInSec, Symbol *sym,
+ int64_t addend, RelExpr expr,
+ RelType type) {
// Write the addends to the relocated address if required. We skip
// it if the written value would be zero.
- if (Config->WriteAddends && (Expr != R_ADDEND || Addend != 0))
- InputSec->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym});
- addReloc({DynType, InputSec, OffsetInSec, Expr != R_ADDEND, Sym, Addend});
+ if (config->writeAddends && (expr != R_ADDEND || addend != 0))
+ inputSec->relocations.push_back({expr, type, offsetInSec, addend, sym});
+ addReloc({dynType, inputSec, offsetInSec, expr != R_ADDEND, sym, addend});
}
-void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) {
- if (Reloc.Type == Target->RelativeRel)
- ++NumRelativeRelocs;
- Relocs.push_back(Reloc);
+void RelocationBaseSection::addReloc(const DynamicReloc &reloc) {
+ if (reloc.type == target->relativeRel)
+ ++numRelativeRelocs;
+ relocs.push_back(reloc);
}
void RelocationBaseSection::finalizeContents() {
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab;
+
// When linking glibc statically, .rel{,a}.plt contains R_*_IRELATIVE
// relocations due to IFUNC (e.g. strcpy). sh_link will be set to 0 in that
// case.
- InputSection *SymTab = Config->Relocatable ? In.SymTab : In.DynSymTab;
- if (SymTab && SymTab->getParent())
- getParent()->Link = SymTab->getParent()->SectionIndex;
+ if (symTab && symTab->getParent())
+ getParent()->link = symTab->getParent()->sectionIndex;
else
- getParent()->Link = 0;
+ getParent()->link = 0;
- if (In.RelaIplt == this || In.RelaPlt == this)
- getParent()->Info = In.GotPlt->getParent()->SectionIndex;
+ if (in.relaPlt == this)
+ getParent()->info = in.gotPlt->getParent()->sectionIndex;
+ if (in.relaIplt == this)
+ getParent()->info = in.igotPlt->getParent()->sectionIndex;
}
RelrBaseSection::RelrBaseSection()
: SyntheticSection(SHF_ALLOC,
- Config->UseAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR,
- Config->Wordsize, ".relr.dyn") {}
+ config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR,
+ config->wordsize, ".relr.dyn") {}
template <class ELFT>
-static void encodeDynamicReloc(typename ELFT::Rela *P,
- const DynamicReloc &Rel) {
- if (Config->IsRela)
- P->r_addend = Rel.computeAddend();
- P->r_offset = Rel.getOffset();
- P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL);
+static void encodeDynamicReloc(SymbolTableBaseSection *symTab,
+ typename ELFT::Rela *p,
+ const DynamicReloc &rel) {
+ if (config->isRela)
+ p->r_addend = rel.computeAddend();
+ p->r_offset = rel.getOffset();
+ p->setSymbolAndType(rel.getSymIndex(symTab), rel.type, config->isMips64EL);
}
template <class ELFT>
-RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
- : RelocationBaseSection(Name, Config->IsRela ? SHT_RELA : SHT_REL,
- Config->IsRela ? DT_RELA : DT_REL,
- Config->IsRela ? DT_RELASZ : DT_RELSZ),
- Sort(Sort) {
- this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
+RelocationSection<ELFT>::RelocationSection(StringRef name, bool sort)
+ : RelocationBaseSection(name, config->isRela ? SHT_RELA : SHT_REL,
+ config->isRela ? DT_RELA : DT_REL,
+ config->isRela ? DT_RELASZ : DT_RELSZ),
+ sort(sort) {
+ this->entsize = config->isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
}
-static bool compRelocations(const DynamicReloc &A, const DynamicReloc &B) {
- bool AIsRel = A.Type == Target->RelativeRel;
- bool BIsRel = B.Type == Target->RelativeRel;
- if (AIsRel != BIsRel)
- return AIsRel;
- return A.getSymIndex() < B.getSymIndex();
-}
+template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *buf) {
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab;
-template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
- if (Sort)
- std::stable_sort(Relocs.begin(), Relocs.end(), compRelocations);
+ // Sort by (!IsRelative,SymIndex,r_offset). DT_REL[A]COUNT requires us to
+ // place R_*_RELATIVE first. SymIndex is to improve locality, while r_offset
+ // is to make results easier to read.
+ if (sort)
+ llvm::stable_sort(
+ relocs, [&](const DynamicReloc &a, const DynamicReloc &b) {
+ return std::make_tuple(a.type != target->relativeRel,
+ a.getSymIndex(symTab), a.getOffset()) <
+ std::make_tuple(b.type != target->relativeRel,
+ b.getSymIndex(symTab), b.getOffset());
+ });
- for (const DynamicReloc &Rel : Relocs) {
- encodeDynamicReloc<ELFT>(reinterpret_cast<Elf_Rela *>(Buf), Rel);
- Buf += Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
+ for (const DynamicReloc &rel : relocs) {
+ encodeDynamicReloc<ELFT>(symTab, reinterpret_cast<Elf_Rela *>(buf), rel);
+ buf += config->isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
}
}
-template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
- return this->Entsize * Relocs.size();
-}
-
template <class ELFT>
AndroidPackedRelocationSection<ELFT>::AndroidPackedRelocationSection(
- StringRef Name)
+ StringRef name)
: RelocationBaseSection(
- Name, Config->IsRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL,
- Config->IsRela ? DT_ANDROID_RELA : DT_ANDROID_REL,
- Config->IsRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) {
- this->Entsize = 1;
+ name, config->isRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL,
+ config->isRela ? DT_ANDROID_RELA : DT_ANDROID_REL,
+ config->isRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) {
+ this->entsize = 1;
}
template <class ELFT>
@@ -1619,32 +1630,32 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
// RELOCATION_GROUPED_BY_ADDEND_FLAG is not set) the r_addend delta for
// this relocation.
- size_t OldSize = RelocData.size();
+ size_t oldSize = relocData.size();
- RelocData = {'A', 'P', 'S', '2'};
- raw_svector_ostream OS(RelocData);
- auto Add = [&](int64_t V) { encodeSLEB128(V, OS); };
+ relocData = {'A', 'P', 'S', '2'};
+ raw_svector_ostream os(relocData);
+ auto add = [&](int64_t v) { encodeSLEB128(v, os); };
// The format header includes the number of relocations and the initial
// offset (we set this to zero because the first relocation group will
// perform the initial adjustment).
- Add(Relocs.size());
- Add(0);
+ add(relocs.size());
+ add(0);
- std::vector<Elf_Rela> Relatives, NonRelatives;
+ std::vector<Elf_Rela> relatives, nonRelatives;
- for (const DynamicReloc &Rel : Relocs) {
- Elf_Rela R;
- encodeDynamicReloc<ELFT>(&R, Rel);
+ for (const DynamicReloc &rel : relocs) {
+ Elf_Rela r;
+ encodeDynamicReloc<ELFT>(getPartition().dynSymTab, &r, rel);
- if (R.getType(Config->IsMips64EL) == Target->RelativeRel)
- Relatives.push_back(R);
+ if (r.getType(config->isMips64EL) == target->relativeRel)
+ relatives.push_back(r);
else
- NonRelatives.push_back(R);
+ nonRelatives.push_back(r);
}
- llvm::sort(Relatives, [](const Elf_Rel &A, const Elf_Rel &B) {
- return A.r_offset < B.r_offset;
+ llvm::sort(relatives, [](const Elf_Rel &a, const Elf_Rel &b) {
+ return a.r_offset < b.r_offset;
});
// Try to find groups of relative relocations which are spaced one word
@@ -1653,108 +1664,108 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
// encoding, but each group will cost 7 bytes in addition to the offset from
// the previous group, so it is only profitable to do this for groups of
// size 8 or larger.
- std::vector<Elf_Rela> UngroupedRelatives;
- std::vector<std::vector<Elf_Rela>> RelativeGroups;
- for (auto I = Relatives.begin(), E = Relatives.end(); I != E;) {
- std::vector<Elf_Rela> Group;
+ std::vector<Elf_Rela> ungroupedRelatives;
+ std::vector<std::vector<Elf_Rela>> relativeGroups;
+ for (auto i = relatives.begin(), e = relatives.end(); i != e;) {
+ std::vector<Elf_Rela> group;
do {
- Group.push_back(*I++);
- } while (I != E && (I - 1)->r_offset + Config->Wordsize == I->r_offset);
+ group.push_back(*i++);
+ } while (i != e && (i - 1)->r_offset + config->wordsize == i->r_offset);
- if (Group.size() < 8)
- UngroupedRelatives.insert(UngroupedRelatives.end(), Group.begin(),
- Group.end());
+ if (group.size() < 8)
+ ungroupedRelatives.insert(ungroupedRelatives.end(), group.begin(),
+ group.end());
else
- RelativeGroups.emplace_back(std::move(Group));
+ relativeGroups.emplace_back(std::move(group));
}
- unsigned HasAddendIfRela =
- Config->IsRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
+ unsigned hasAddendIfRela =
+ config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
- uint64_t Offset = 0;
- uint64_t Addend = 0;
+ uint64_t offset = 0;
+ uint64_t addend = 0;
// Emit the run-length encoding for the groups of adjacent relative
// relocations. Each group is represented using two groups in the packed
// format. The first is used to set the current offset to the start of the
// group (and also encodes the first relocation), and the second encodes the
// remaining relocations.
- for (std::vector<Elf_Rela> &G : RelativeGroups) {
+ for (std::vector<Elf_Rela> &g : relativeGroups) {
// The first relocation in the group.
- Add(1);
- Add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
- RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
- Add(G[0].r_offset - Offset);
- Add(Target->RelativeRel);
- if (Config->IsRela) {
- Add(G[0].r_addend - Addend);
- Addend = G[0].r_addend;
+ add(1);
+ add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
+ RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
+ add(g[0].r_offset - offset);
+ add(target->relativeRel);
+ if (config->isRela) {
+ add(g[0].r_addend - addend);
+ addend = g[0].r_addend;
}
// The remaining relocations.
- Add(G.size() - 1);
- Add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
- RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
- Add(Config->Wordsize);
- Add(Target->RelativeRel);
- if (Config->IsRela) {
- for (auto I = G.begin() + 1, E = G.end(); I != E; ++I) {
- Add(I->r_addend - Addend);
- Addend = I->r_addend;
+ add(g.size() - 1);
+ add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
+ RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
+ add(config->wordsize);
+ add(target->relativeRel);
+ if (config->isRela) {
+ for (auto i = g.begin() + 1, e = g.end(); i != e; ++i) {
+ add(i->r_addend - addend);
+ addend = i->r_addend;
}
}
- Offset = G.back().r_offset;
+ offset = g.back().r_offset;
}
// Now the ungrouped relatives.
- if (!UngroupedRelatives.empty()) {
- Add(UngroupedRelatives.size());
- Add(RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
- Add(Target->RelativeRel);
- for (Elf_Rela &R : UngroupedRelatives) {
- Add(R.r_offset - Offset);
- Offset = R.r_offset;
- if (Config->IsRela) {
- Add(R.r_addend - Addend);
- Addend = R.r_addend;
+ if (!ungroupedRelatives.empty()) {
+ add(ungroupedRelatives.size());
+ add(RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
+ add(target->relativeRel);
+ for (Elf_Rela &r : ungroupedRelatives) {
+ add(r.r_offset - offset);
+ offset = r.r_offset;
+ if (config->isRela) {
+ add(r.r_addend - addend);
+ addend = r.r_addend;
}
}
}
// Finally the non-relative relocations.
- llvm::sort(NonRelatives, [](const Elf_Rela &A, const Elf_Rela &B) {
- return A.r_offset < B.r_offset;
+ llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
+ return a.r_offset < b.r_offset;
});
- if (!NonRelatives.empty()) {
- Add(NonRelatives.size());
- Add(HasAddendIfRela);
- for (Elf_Rela &R : NonRelatives) {
- Add(R.r_offset - Offset);
- Offset = R.r_offset;
- Add(R.r_info);
- if (Config->IsRela) {
- Add(R.r_addend - Addend);
- Addend = R.r_addend;
+ if (!nonRelatives.empty()) {
+ add(nonRelatives.size());
+ add(hasAddendIfRela);
+ for (Elf_Rela &r : nonRelatives) {
+ add(r.r_offset - offset);
+ offset = r.r_offset;
+ add(r.r_info);
+ if (config->isRela) {
+ add(r.r_addend - addend);
+ addend = r.r_addend;
}
}
}
// Don't allow the section to shrink; otherwise the size of the section can
// oscillate infinitely.
- if (RelocData.size() < OldSize)
- RelocData.append(OldSize - RelocData.size(), 0);
+ if (relocData.size() < oldSize)
+ relocData.append(oldSize - relocData.size(), 0);
// Returns whether the section size changed. We need to keep recomputing both
// section layout and the contents of this section until the size converges
// because changing this section's size can affect section layout, which in
// turn can affect the sizes of the LEB-encoded integers stored in this
// section.
- return RelocData.size() != OldSize;
+ return relocData.size() != oldSize;
}
template <class ELFT> RelrSection<ELFT>::RelrSection() {
- this->Entsize = Config->Wordsize;
+ this->entsize = config->wordsize;
}
template <class ELFT> bool RelrSection<ELFT>::updateAllocSize() {
@@ -1788,90 +1799,90 @@ template <class ELFT> bool RelrSection<ELFT>::updateAllocSize() {
// even means address, odd means bitmap.
// 2. Just a simple list of addresses is a valid encoding.
- size_t OldSize = RelrRelocs.size();
- RelrRelocs.clear();
+ size_t oldSize = relrRelocs.size();
+ relrRelocs.clear();
// Same as Config->Wordsize but faster because this is a compile-time
// constant.
- const size_t Wordsize = sizeof(typename ELFT::uint);
+ const size_t wordsize = sizeof(typename ELFT::uint);
// Number of bits to use for the relocation offsets bitmap.
// Must be either 63 or 31.
- const size_t NBits = Wordsize * 8 - 1;
+ const size_t nBits = wordsize * 8 - 1;
// Get offsets for all relative relocations and sort them.
- std::vector<uint64_t> Offsets;
- for (const RelativeReloc &Rel : Relocs)
- Offsets.push_back(Rel.getOffset());
- llvm::sort(Offsets.begin(), Offsets.end());
+ std::vector<uint64_t> offsets;
+ for (const RelativeReloc &rel : relocs)
+ offsets.push_back(rel.getOffset());
+ llvm::sort(offsets);
// For each leading relocation, find following ones that can be folded
// as a bitmap and fold them.
- for (size_t I = 0, E = Offsets.size(); I < E;) {
+ for (size_t i = 0, e = offsets.size(); i < e;) {
// Add a leading relocation.
- RelrRelocs.push_back(Elf_Relr(Offsets[I]));
- uint64_t Base = Offsets[I] + Wordsize;
- ++I;
+ relrRelocs.push_back(Elf_Relr(offsets[i]));
+ uint64_t base = offsets[i] + wordsize;
+ ++i;
// Find foldable relocations to construct bitmaps.
- while (I < E) {
- uint64_t Bitmap = 0;
+ while (i < e) {
+ uint64_t bitmap = 0;
- while (I < E) {
- uint64_t Delta = Offsets[I] - Base;
+ while (i < e) {
+ uint64_t delta = offsets[i] - base;
// If it is too far, it cannot be folded.
- if (Delta >= NBits * Wordsize)
+ if (delta >= nBits * wordsize)
break;
// If it is not a multiple of wordsize away, it cannot be folded.
- if (Delta % Wordsize)
+ if (delta % wordsize)
break;
// Fold it.
- Bitmap |= 1ULL << (Delta / Wordsize);
- ++I;
+ bitmap |= 1ULL << (delta / wordsize);
+ ++i;
}
- if (!Bitmap)
+ if (!bitmap)
break;
- RelrRelocs.push_back(Elf_Relr((Bitmap << 1) | 1));
- Base += NBits * Wordsize;
+ relrRelocs.push_back(Elf_Relr((bitmap << 1) | 1));
+ base += nBits * wordsize;
}
}
- return RelrRelocs.size() != OldSize;
+ return relrRelocs.size() != oldSize;
}
-SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec)
- : SyntheticSection(StrTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0,
- StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB,
- Config->Wordsize,
- StrTabSec.isDynamic() ? ".dynsym" : ".symtab"),
- StrTabSec(StrTabSec) {}
+SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &strTabSec)
+ : SyntheticSection(strTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0,
+ strTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB,
+ config->wordsize,
+ strTabSec.isDynamic() ? ".dynsym" : ".symtab"),
+ strTabSec(strTabSec) {}
// Orders symbols according to their positions in the GOT,
// in compliance with MIPS ABI rules.
// See "Global Offset Table" in Chapter 5 in the following document
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
-static bool sortMipsSymbols(const SymbolTableEntry &L,
- const SymbolTableEntry &R) {
+static bool sortMipsSymbols(const SymbolTableEntry &l,
+ const SymbolTableEntry &r) {
// Sort entries related to non-local preemptible symbols by GOT indexes.
// All other entries go to the beginning of a dynsym in arbitrary order.
- if (L.Sym->isInGot() && R.Sym->isInGot())
- return L.Sym->GotIndex < R.Sym->GotIndex;
- if (!L.Sym->isInGot() && !R.Sym->isInGot())
+ if (l.sym->isInGot() && r.sym->isInGot())
+ return l.sym->gotIndex < r.sym->gotIndex;
+ if (!l.sym->isInGot() && !r.sym->isInGot())
return false;
- return !L.Sym->isInGot();
+ return !l.sym->isInGot();
}
void SymbolTableBaseSection::finalizeContents() {
- if (OutputSection *Sec = StrTabSec.getParent())
- getParent()->Link = Sec->SectionIndex;
+ if (OutputSection *sec = strTabSec.getParent())
+ getParent()->link = sec->sectionIndex;
- if (this->Type != SHT_DYNSYM) {
+ if (this->type != SHT_DYNSYM) {
sortSymTabSymbols();
return;
}
@@ -1881,18 +1892,22 @@ void SymbolTableBaseSection::finalizeContents() {
// Section's Info field has the index of the first non-local symbol.
// Because the first symbol entry is a null entry, 1 is the first.
- getParent()->Info = 1;
+ getParent()->info = 1;
- if (In.GnuHashTab) {
+ if (getPartition().gnuHashTab) {
// NB: It also sorts Symbols to meet the GNU hash table requirements.
- In.GnuHashTab->addSymbols(Symbols);
- } else if (Config->EMachine == EM_MIPS) {
- std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols);
+ getPartition().gnuHashTab->addSymbols(symbols);
+ } else if (config->emachine == EM_MIPS) {
+ llvm::stable_sort(symbols, sortMipsSymbols);
}
- size_t I = 0;
- for (const SymbolTableEntry &S : Symbols)
- S.Sym->DynsymIndex = ++I;
+ // Only the main partition's dynsym indexes are stored in the symbols
+ // themselves. All other partitions use a lookup table.
+ if (this == mainPart->dynSymTab) {
+ size_t i = 0;
+ for (const SymbolTableEntry &s : symbols)
+ s.sym->dynsymIndex = ++i;
+ }
}
// The ELF spec requires that all local symbols precede global symbols, so we
@@ -1904,193 +1919,210 @@ void SymbolTableBaseSection::finalizeContents() {
// coming from.
void SymbolTableBaseSection::sortSymTabSymbols() {
// Move all local symbols before global symbols.
- auto E = std::stable_partition(
- Symbols.begin(), Symbols.end(), [](const SymbolTableEntry &S) {
- return S.Sym->isLocal() || S.Sym->computeBinding() == STB_LOCAL;
+ auto e = std::stable_partition(
+ symbols.begin(), symbols.end(), [](const SymbolTableEntry &s) {
+ return s.sym->isLocal() || s.sym->computeBinding() == STB_LOCAL;
});
- size_t NumLocals = E - Symbols.begin();
- getParent()->Info = NumLocals + 1;
+ size_t numLocals = e - symbols.begin();
+ getParent()->info = numLocals + 1;
// We want to group the local symbols by file. For that we rebuild the local
// part of the symbols vector. We do not need to care about the STT_FILE
// symbols, they are already naturally placed first in each group. That
// happens because STT_FILE is always the first symbol in the object and hence
// precede all other local symbols we add for a file.
- MapVector<InputFile *, std::vector<SymbolTableEntry>> Arr;
- for (const SymbolTableEntry &S : llvm::make_range(Symbols.begin(), E))
- Arr[S.Sym->File].push_back(S);
+ MapVector<InputFile *, std::vector<SymbolTableEntry>> arr;
+ for (const SymbolTableEntry &s : llvm::make_range(symbols.begin(), e))
+ arr[s.sym->file].push_back(s);
- auto I = Symbols.begin();
- for (std::pair<InputFile *, std::vector<SymbolTableEntry>> &P : Arr)
- for (SymbolTableEntry &Entry : P.second)
- *I++ = Entry;
+ auto i = symbols.begin();
+ for (std::pair<InputFile *, std::vector<SymbolTableEntry>> &p : arr)
+ for (SymbolTableEntry &entry : p.second)
+ *i++ = entry;
}
-void SymbolTableBaseSection::addSymbol(Symbol *B) {
+void SymbolTableBaseSection::addSymbol(Symbol *b) {
// Adding a local symbol to a .dynsym is a bug.
- assert(this->Type != SHT_DYNSYM || !B->isLocal());
+ assert(this->type != SHT_DYNSYM || !b->isLocal());
- bool HashIt = B->isLocal();
- Symbols.push_back({B, StrTabSec.addString(B->getName(), HashIt)});
+ bool hashIt = b->isLocal();
+ symbols.push_back({b, strTabSec.addString(b->getName(), hashIt)});
}
-size_t SymbolTableBaseSection::getSymbolIndex(Symbol *Sym) {
- // Initializes symbol lookup tables lazily. This is used only
- // for -r or -emit-relocs.
- llvm::call_once(OnceFlag, [&] {
- SymbolIndexMap.reserve(Symbols.size());
- size_t I = 0;
- for (const SymbolTableEntry &E : Symbols) {
- if (E.Sym->Type == STT_SECTION)
- SectionIndexMap[E.Sym->getOutputSection()] = ++I;
+size_t SymbolTableBaseSection::getSymbolIndex(Symbol *sym) {
+ if (this == mainPart->dynSymTab)
+ return sym->dynsymIndex;
+
+ // Initializes symbol lookup tables lazily. This is used only for -r,
+ // -emit-relocs and dynsyms in partitions other than the main one.
+ llvm::call_once(onceFlag, [&] {
+ symbolIndexMap.reserve(symbols.size());
+ size_t i = 0;
+ for (const SymbolTableEntry &e : symbols) {
+ if (e.sym->type == STT_SECTION)
+ sectionIndexMap[e.sym->getOutputSection()] = ++i;
else
- SymbolIndexMap[E.Sym] = ++I;
+ symbolIndexMap[e.sym] = ++i;
}
});
// Section symbols are mapped based on their output sections
// to maintain their semantics.
- if (Sym->Type == STT_SECTION)
- return SectionIndexMap.lookup(Sym->getOutputSection());
- return SymbolIndexMap.lookup(Sym);
+ if (sym->type == STT_SECTION)
+ return sectionIndexMap.lookup(sym->getOutputSection());
+ return symbolIndexMap.lookup(sym);
}
template <class ELFT>
-SymbolTableSection<ELFT>::SymbolTableSection(StringTableSection &StrTabSec)
- : SymbolTableBaseSection(StrTabSec) {
- this->Entsize = sizeof(Elf_Sym);
+SymbolTableSection<ELFT>::SymbolTableSection(StringTableSection &strTabSec)
+ : SymbolTableBaseSection(strTabSec) {
+ this->entsize = sizeof(Elf_Sym);
}
-static BssSection *getCommonSec(Symbol *Sym) {
- if (!Config->DefineCommon)
- if (auto *D = dyn_cast<Defined>(Sym))
- return dyn_cast_or_null<BssSection>(D->Section);
+static BssSection *getCommonSec(Symbol *sym) {
+ if (!config->defineCommon)
+ if (auto *d = dyn_cast<Defined>(sym))
+ return dyn_cast_or_null<BssSection>(d->section);
return nullptr;
}
-static uint32_t getSymSectionIndex(Symbol *Sym) {
- if (getCommonSec(Sym))
+static uint32_t getSymSectionIndex(Symbol *sym) {
+ if (getCommonSec(sym))
return SHN_COMMON;
- if (!isa<Defined>(Sym) || Sym->NeedsPltAddr)
+ if (!isa<Defined>(sym) || sym->needsPltAddr)
return SHN_UNDEF;
- if (const OutputSection *OS = Sym->getOutputSection())
- return OS->SectionIndex >= SHN_LORESERVE ? (uint32_t)SHN_XINDEX
- : OS->SectionIndex;
+ if (const OutputSection *os = sym->getOutputSection())
+ return os->sectionIndex >= SHN_LORESERVE ? (uint32_t)SHN_XINDEX
+ : os->sectionIndex;
return SHN_ABS;
}
// Write the internal symbol table contents to the output symbol table.
-template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
+template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *buf) {
// The first entry is a null entry as per the ELF spec.
- memset(Buf, 0, sizeof(Elf_Sym));
- Buf += sizeof(Elf_Sym);
+ memset(buf, 0, sizeof(Elf_Sym));
+ buf += sizeof(Elf_Sym);
- auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
+ auto *eSym = reinterpret_cast<Elf_Sym *>(buf);
- for (SymbolTableEntry &Ent : Symbols) {
- Symbol *Sym = Ent.Sym;
+ for (SymbolTableEntry &ent : symbols) {
+ Symbol *sym = ent.sym;
+ bool isDefinedHere = type == SHT_SYMTAB || sym->partition == partition;
// Set st_info and st_other.
- ESym->st_other = 0;
- if (Sym->isLocal()) {
- ESym->setBindingAndType(STB_LOCAL, Sym->Type);
+ eSym->st_other = 0;
+ if (sym->isLocal()) {
+ eSym->setBindingAndType(STB_LOCAL, sym->type);
} else {
- ESym->setBindingAndType(Sym->computeBinding(), Sym->Type);
- ESym->setVisibility(Sym->Visibility);
+ eSym->setBindingAndType(sym->computeBinding(), sym->type);
+ eSym->setVisibility(sym->visibility);
}
- ESym->st_name = Ent.StrTabOffset;
- ESym->st_shndx = getSymSectionIndex(Ent.Sym);
+ // The 3 most significant bits of st_other are used by OpenPOWER ABI.
+ // See getPPC64GlobalEntryToLocalEntryOffset() for more details.
+ if (config->emachine == EM_PPC64)
+ eSym->st_other |= sym->stOther & 0xe0;
+
+ eSym->st_name = ent.strTabOffset;
+ if (isDefinedHere)
+ eSym->st_shndx = getSymSectionIndex(ent.sym);
+ else
+ eSym->st_shndx = 0;
// Copy symbol size if it is a defined symbol. st_size is not significant
// for undefined symbols, so whether copying it or not is up to us if that's
// the case. We'll leave it as zero because by not setting a value, we can
// get the exact same outputs for two sets of input files that differ only
// in undefined symbol size in DSOs.
- if (ESym->st_shndx == SHN_UNDEF)
- ESym->st_size = 0;
+ if (eSym->st_shndx == SHN_UNDEF || !isDefinedHere)
+ eSym->st_size = 0;
else
- ESym->st_size = Sym->getSize();
+ eSym->st_size = sym->getSize();
// st_value is usually an address of a symbol, but that has a
// special meaining for uninstantiated common symbols (this can
// occur if -r is given).
- if (BssSection *CommonSec = getCommonSec(Ent.Sym))
- ESym->st_value = CommonSec->Alignment;
+ if (BssSection *commonSec = getCommonSec(ent.sym))
+ eSym->st_value = commonSec->alignment;
+ else if (isDefinedHere)
+ eSym->st_value = sym->getVA();
else
- ESym->st_value = Sym->getVA();
+ eSym->st_value = 0;
- ++ESym;
+ ++eSym;
}
// On MIPS we need to mark symbol which has a PLT entry and requires
// pointer equality by STO_MIPS_PLT flag. That is necessary to help
// dynamic linker distinguish such symbols and MIPS lazy-binding stubs.
// https://sourceware.org/ml/binutils/2008-07/txt00000.txt
- if (Config->EMachine == EM_MIPS) {
- auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
+ if (config->emachine == EM_MIPS) {
+ auto *eSym = reinterpret_cast<Elf_Sym *>(buf);
- for (SymbolTableEntry &Ent : Symbols) {
- Symbol *Sym = Ent.Sym;
- if (Sym->isInPlt() && Sym->NeedsPltAddr)
- ESym->st_other |= STO_MIPS_PLT;
+ for (SymbolTableEntry &ent : symbols) {
+ Symbol *sym = ent.sym;
+ if (sym->isInPlt() && sym->needsPltAddr)
+ eSym->st_other |= STO_MIPS_PLT;
if (isMicroMips()) {
- // Set STO_MIPS_MICROMIPS flag and less-significant bit for
- // a defined microMIPS symbol and symbol should point to its
- // PLT entry (in case of microMIPS, PLT entries always contain
- // microMIPS code).
- if (Sym->isDefined() &&
- ((Sym->StOther & STO_MIPS_MICROMIPS) || Sym->NeedsPltAddr)) {
- if (StrTabSec.isDynamic())
- ESym->st_value |= 1;
- ESym->st_other |= STO_MIPS_MICROMIPS;
+ // We already set the less-significant bit for symbols
+ // marked by the `STO_MIPS_MICROMIPS` flag and for microMIPS PLT
+ // records. That allows us to distinguish such symbols in
+ // the `MIPS<ELFT>::relocateOne()` routine. Now we should
+ // clear that bit for non-dynamic symbol table, so tools
+ // like `objdump` will be able to deal with a correct
+ // symbol position.
+ if (sym->isDefined() &&
+ ((sym->stOther & STO_MIPS_MICROMIPS) || sym->needsPltAddr)) {
+ if (!strTabSec.isDynamic())
+ eSym->st_value &= ~1;
+ eSym->st_other |= STO_MIPS_MICROMIPS;
}
}
- if (Config->Relocatable)
- if (auto *D = dyn_cast<Defined>(Sym))
- if (isMipsPIC<ELFT>(D))
- ESym->st_other |= STO_MIPS_PIC;
- ++ESym;
+ if (config->relocatable)
+ if (auto *d = dyn_cast<Defined>(sym))
+ if (isMipsPIC<ELFT>(d))
+ eSym->st_other |= STO_MIPS_PIC;
+ ++eSym;
}
}
}
SymtabShndxSection::SymtabShndxSection()
- : SyntheticSection(0, SHT_SYMTAB_SHNDX, 4, ".symtab_shndxr") {
- this->Entsize = 4;
+ : SyntheticSection(0, SHT_SYMTAB_SHNDX, 4, ".symtab_shndx") {
+ this->entsize = 4;
}
-void SymtabShndxSection::writeTo(uint8_t *Buf) {
+void SymtabShndxSection::writeTo(uint8_t *buf) {
// We write an array of 32 bit values, where each value has 1:1 association
// with an entry in .symtab. If the corresponding entry contains SHN_XINDEX,
// we need to write actual index, otherwise, we must write SHN_UNDEF(0).
- Buf += 4; // Ignore .symtab[0] entry.
- for (const SymbolTableEntry &Entry : In.SymTab->getSymbols()) {
- if (getSymSectionIndex(Entry.Sym) == SHN_XINDEX)
- write32(Buf, Entry.Sym->getOutputSection()->SectionIndex);
- Buf += 4;
+ buf += 4; // Ignore .symtab[0] entry.
+ for (const SymbolTableEntry &entry : in.symTab->getSymbols()) {
+ if (getSymSectionIndex(entry.sym) == SHN_XINDEX)
+ write32(buf, entry.sym->getOutputSection()->sectionIndex);
+ buf += 4;
}
}
-bool SymtabShndxSection::empty() const {
+bool SymtabShndxSection::isNeeded() const {
// SHT_SYMTAB can hold symbols with section indices values up to
// SHN_LORESERVE. If we need more, we want to use extension SHT_SYMTAB_SHNDX
// section. Problem is that we reveal the final section indices a bit too
// late, and we do not know them here. For simplicity, we just always create
- // a .symtab_shndxr section when the amount of output sections is huge.
- size_t Size = 0;
- for (BaseCommand *Base : Script->SectionCommands)
- if (isa<OutputSection>(Base))
- ++Size;
- return Size < SHN_LORESERVE;
+ // a .symtab_shndx section when the amount of output sections is huge.
+ size_t size = 0;
+ for (BaseCommand *base : script->sectionCommands)
+ if (isa<OutputSection>(base))
+ ++size;
+ return size >= SHN_LORESERVE;
}
void SymtabShndxSection::finalizeContents() {
- getParent()->Link = In.SymTab->getParent()->SectionIndex;
+ getParent()->link = in.symTab->getParent()->sectionIndex;
}
size_t SymtabShndxSection::getSize() const {
- return In.SymTab->getNumSymbols() * 4;
+ return in.symTab->getNumSymbols() * 4;
}
// .hash and .gnu.hash sections contain on-disk hash tables that map
@@ -2125,45 +2157,45 @@ size_t SymtabShndxSection::getSize() const {
// about .gnu.hash, you want to specify -hash-style=gnu. Otherwise, a
// safe bet is to specify -hash-style=both for backward compatibilty.
GnuHashTableSection::GnuHashTableSection()
- : SyntheticSection(SHF_ALLOC, SHT_GNU_HASH, Config->Wordsize, ".gnu.hash") {
+ : SyntheticSection(SHF_ALLOC, SHT_GNU_HASH, config->wordsize, ".gnu.hash") {
}
void GnuHashTableSection::finalizeContents() {
- if (OutputSection *Sec = In.DynSymTab->getParent())
- getParent()->Link = Sec->SectionIndex;
+ if (OutputSection *sec = getPartition().dynSymTab->getParent())
+ getParent()->link = sec->sectionIndex;
// Computes bloom filter size in word size. We want to allocate 12
// bits for each symbol. It must be a power of two.
- if (Symbols.empty()) {
- MaskWords = 1;
+ if (symbols.empty()) {
+ maskWords = 1;
} else {
- uint64_t NumBits = Symbols.size() * 12;
- MaskWords = NextPowerOf2(NumBits / (Config->Wordsize * 8));
+ uint64_t numBits = symbols.size() * 12;
+ maskWords = NextPowerOf2(numBits / (config->wordsize * 8));
}
- Size = 16; // Header
- Size += Config->Wordsize * MaskWords; // Bloom filter
- Size += NBuckets * 4; // Hash buckets
- Size += Symbols.size() * 4; // Hash values
+ size = 16; // Header
+ size += config->wordsize * maskWords; // Bloom filter
+ size += nBuckets * 4; // Hash buckets
+ size += symbols.size() * 4; // Hash values
}
-void GnuHashTableSection::writeTo(uint8_t *Buf) {
+void GnuHashTableSection::writeTo(uint8_t *buf) {
// The output buffer is not guaranteed to be zero-cleared because we pre-
// fill executable sections with trap instructions. This is a precaution
// for that case, which happens only when -no-rosegment is given.
- memset(Buf, 0, Size);
+ memset(buf, 0, size);
// Write a header.
- write32(Buf, NBuckets);
- write32(Buf + 4, In.DynSymTab->getNumSymbols() - Symbols.size());
- write32(Buf + 8, MaskWords);
- write32(Buf + 12, Shift2);
- Buf += 16;
+ write32(buf, nBuckets);
+ write32(buf + 4, getPartition().dynSymTab->getNumSymbols() - symbols.size());
+ write32(buf + 8, maskWords);
+ write32(buf + 12, Shift2);
+ buf += 16;
// Write a bloom filter and a hash table.
- writeBloomFilter(Buf);
- Buf += Config->Wordsize * MaskWords;
- writeHashTable(Buf);
+ writeBloomFilter(buf);
+ buf += config->wordsize * maskWords;
+ writeHashTable(buf);
}
// This function writes a 2-bit bloom filter. This bloom filter alone
@@ -2173,57 +2205,58 @@ void GnuHashTableSection::writeTo(uint8_t *Buf) {
//
// [1] Ulrich Drepper (2011), "How To Write Shared Libraries" (Ver. 4.1.2),
// p.9, https://www.akkadia.org/drepper/dsohowto.pdf
-void GnuHashTableSection::writeBloomFilter(uint8_t *Buf) {
- unsigned C = Config->Is64 ? 64 : 32;
- for (const Entry &Sym : Symbols) {
+void GnuHashTableSection::writeBloomFilter(uint8_t *buf) {
+ unsigned c = config->is64 ? 64 : 32;
+ for (const Entry &sym : symbols) {
// When C = 64, we choose a word with bits [6:...] and set 1 to two bits in
// the word using bits [0:5] and [26:31].
- size_t I = (Sym.Hash / C) & (MaskWords - 1);
- uint64_t Val = readUint(Buf + I * Config->Wordsize);
- Val |= uint64_t(1) << (Sym.Hash % C);
- Val |= uint64_t(1) << ((Sym.Hash >> Shift2) % C);
- writeUint(Buf + I * Config->Wordsize, Val);
+ size_t i = (sym.hash / c) & (maskWords - 1);
+ uint64_t val = readUint(buf + i * config->wordsize);
+ val |= uint64_t(1) << (sym.hash % c);
+ val |= uint64_t(1) << ((sym.hash >> Shift2) % c);
+ writeUint(buf + i * config->wordsize, val);
}
}
-void GnuHashTableSection::writeHashTable(uint8_t *Buf) {
- uint32_t *Buckets = reinterpret_cast<uint32_t *>(Buf);
- uint32_t OldBucket = -1;
- uint32_t *Values = Buckets + NBuckets;
- for (auto I = Symbols.begin(), E = Symbols.end(); I != E; ++I) {
+void GnuHashTableSection::writeHashTable(uint8_t *buf) {
+ uint32_t *buckets = reinterpret_cast<uint32_t *>(buf);
+ uint32_t oldBucket = -1;
+ uint32_t *values = buckets + nBuckets;
+ for (auto i = symbols.begin(), e = symbols.end(); i != e; ++i) {
// Write a hash value. It represents a sequence of chains that share the
// same hash modulo value. The last element of each chain is terminated by
// LSB 1.
- uint32_t Hash = I->Hash;
- bool IsLastInChain = (I + 1) == E || I->BucketIdx != (I + 1)->BucketIdx;
- Hash = IsLastInChain ? Hash | 1 : Hash & ~1;
- write32(Values++, Hash);
+ uint32_t hash = i->hash;
+ bool isLastInChain = (i + 1) == e || i->bucketIdx != (i + 1)->bucketIdx;
+ hash = isLastInChain ? hash | 1 : hash & ~1;
+ write32(values++, hash);
- if (I->BucketIdx == OldBucket)
+ if (i->bucketIdx == oldBucket)
continue;
// Write a hash bucket. Hash buckets contain indices in the following hash
// value table.
- write32(Buckets + I->BucketIdx, I->Sym->DynsymIndex);
- OldBucket = I->BucketIdx;
+ write32(buckets + i->bucketIdx,
+ getPartition().dynSymTab->getSymbolIndex(i->sym));
+ oldBucket = i->bucketIdx;
}
}
-static uint32_t hashGnu(StringRef Name) {
- uint32_t H = 5381;
- for (uint8_t C : Name)
- H = (H << 5) + H + C;
- return H;
+static uint32_t hashGnu(StringRef name) {
+ uint32_t h = 5381;
+ for (uint8_t c : name)
+ h = (h << 5) + h + c;
+ return h;
}
// Add symbols to this symbol hash table. Note that this function
// destructively sort a given vector -- which is needed because
// GNU-style hash table places some sorting requirements.
-void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &V) {
+void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &v) {
// We cannot use 'auto' for Mid because GCC 6.1 cannot deduce
// its type correctly.
- std::vector<SymbolTableEntry>::iterator Mid =
- std::stable_partition(V.begin(), V.end(), [](const SymbolTableEntry &S) {
- return !S.Sym->isDefined();
+ std::vector<SymbolTableEntry>::iterator mid =
+ std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) {
+ return !s.sym->isDefined() || s.sym->partition != partition;
});
// We chose load factor 4 for the on-disk hash table. For each hash
@@ -2235,138 +2268,143 @@ void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &V) {
// Android loader as of 2018 doesn't like a .gnu.hash containing such
// table. If that's the case, we create a hash table with one unused
// dummy slot.
- NBuckets = std::max<size_t>((V.end() - Mid) / 4, 1);
+ nBuckets = std::max<size_t>((v.end() - mid) / 4, 1);
- if (Mid == V.end())
+ if (mid == v.end())
return;
- for (SymbolTableEntry &Ent : llvm::make_range(Mid, V.end())) {
- Symbol *B = Ent.Sym;
- uint32_t Hash = hashGnu(B->getName());
- uint32_t BucketIdx = Hash % NBuckets;
- Symbols.push_back({B, Ent.StrTabOffset, Hash, BucketIdx});
+ for (SymbolTableEntry &ent : llvm::make_range(mid, v.end())) {
+ Symbol *b = ent.sym;
+ uint32_t hash = hashGnu(b->getName());
+ uint32_t bucketIdx = hash % nBuckets;
+ symbols.push_back({b, ent.strTabOffset, hash, bucketIdx});
}
- std::stable_sort(
- Symbols.begin(), Symbols.end(),
- [](const Entry &L, const Entry &R) { return L.BucketIdx < R.BucketIdx; });
+ llvm::stable_sort(symbols, [](const Entry &l, const Entry &r) {
+ return l.bucketIdx < r.bucketIdx;
+ });
- V.erase(Mid, V.end());
- for (const Entry &Ent : Symbols)
- V.push_back({Ent.Sym, Ent.StrTabOffset});
+ v.erase(mid, v.end());
+ for (const Entry &ent : symbols)
+ v.push_back({ent.sym, ent.strTabOffset});
}
HashTableSection::HashTableSection()
: SyntheticSection(SHF_ALLOC, SHT_HASH, 4, ".hash") {
- this->Entsize = 4;
+ this->entsize = 4;
}
void HashTableSection::finalizeContents() {
- if (OutputSection *Sec = In.DynSymTab->getParent())
- getParent()->Link = Sec->SectionIndex;
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab;
- unsigned NumEntries = 2; // nbucket and nchain.
- NumEntries += In.DynSymTab->getNumSymbols(); // The chain entries.
+ if (OutputSection *sec = symTab->getParent())
+ getParent()->link = sec->sectionIndex;
+
+ unsigned numEntries = 2; // nbucket and nchain.
+ numEntries += symTab->getNumSymbols(); // The chain entries.
// Create as many buckets as there are symbols.
- NumEntries += In.DynSymTab->getNumSymbols();
- this->Size = NumEntries * 4;
+ numEntries += symTab->getNumSymbols();
+ this->size = numEntries * 4;
}
-void HashTableSection::writeTo(uint8_t *Buf) {
+void HashTableSection::writeTo(uint8_t *buf) {
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab;
+
// See comment in GnuHashTableSection::writeTo.
- memset(Buf, 0, Size);
+ memset(buf, 0, size);
- unsigned NumSymbols = In.DynSymTab->getNumSymbols();
+ unsigned numSymbols = symTab->getNumSymbols();
- uint32_t *P = reinterpret_cast<uint32_t *>(Buf);
- write32(P++, NumSymbols); // nbucket
- write32(P++, NumSymbols); // nchain
+ uint32_t *p = reinterpret_cast<uint32_t *>(buf);
+ write32(p++, numSymbols); // nbucket
+ write32(p++, numSymbols); // nchain
- uint32_t *Buckets = P;
- uint32_t *Chains = P + NumSymbols;
+ uint32_t *buckets = p;
+ uint32_t *chains = p + numSymbols;
- for (const SymbolTableEntry &S : In.DynSymTab->getSymbols()) {
- Symbol *Sym = S.Sym;
- StringRef Name = Sym->getName();
- unsigned I = Sym->DynsymIndex;
- uint32_t Hash = hashSysV(Name) % NumSymbols;
- Chains[I] = Buckets[Hash];
- write32(Buckets + Hash, I);
+ for (const SymbolTableEntry &s : symTab->getSymbols()) {
+ Symbol *sym = s.sym;
+ StringRef name = sym->getName();
+ unsigned i = sym->dynsymIndex;
+ uint32_t hash = hashSysV(name) % numSymbols;
+ chains[i] = buckets[hash];
+ write32(buckets + hash, i);
}
}
// On PowerPC64 the lazy symbol resolvers go into the `global linkage table`
// in the .glink section, rather then the typical .plt section.
-PltSection::PltSection(bool IsIplt)
- : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16,
- Config->EMachine == EM_PPC64 ? ".glink" : ".plt"),
- HeaderSize(!IsIplt || Config->ZRetpolineplt ? Target->PltHeaderSize : 0),
- IsIplt(IsIplt) {
+PltSection::PltSection(bool isIplt)
+ : SyntheticSection(
+ SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16,
+ (config->emachine == EM_PPC || config->emachine == EM_PPC64)
+ ? ".glink"
+ : ".plt"),
+ headerSize(!isIplt || config->zRetpolineplt ? target->pltHeaderSize : 0),
+ isIplt(isIplt) {
// The PLT needs to be writable on SPARC as the dynamic linker will
// modify the instructions in the PLT entries.
- if (Config->EMachine == EM_SPARCV9)
- this->Flags |= SHF_WRITE;
+ if (config->emachine == EM_SPARCV9)
+ this->flags |= SHF_WRITE;
}
-void PltSection::writeTo(uint8_t *Buf) {
+void PltSection::writeTo(uint8_t *buf) {
+ if (config->emachine == EM_PPC) {
+ writePPC32GlinkSection(buf, entries.size());
+ return;
+ }
+
// At beginning of PLT or retpoline IPLT, we have code to call the dynamic
// linker to resolve dynsyms at runtime. Write such code.
- if (HeaderSize > 0)
- Target->writePltHeader(Buf);
- size_t Off = HeaderSize;
- // The IPlt is immediately after the Plt, account for this in RelOff
- unsigned PltOff = getPltRelocOff();
+ if (headerSize)
+ target->writePltHeader(buf);
+ size_t off = headerSize;
+
+ RelocationBaseSection *relSec = isIplt ? in.relaIplt : in.relaPlt;
- for (auto &I : Entries) {
- const Symbol *B = I.first;
- unsigned RelOff = I.second + PltOff;
- uint64_t Got = B->getGotPltVA();
- uint64_t Plt = this->getVA() + Off;
- Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff);
- Off += Target->PltEntrySize;
+ // The IPlt is immediately after the Plt, account for this in relOff
+ size_t pltOff = isIplt ? in.plt->getSize() : 0;
+
+ for (size_t i = 0, e = entries.size(); i != e; ++i) {
+ const Symbol *b = entries[i];
+ unsigned relOff = relSec->entsize * i + pltOff;
+ uint64_t got = b->getGotPltVA();
+ uint64_t plt = this->getVA() + off;
+ target->writePlt(buf + off, got, plt, b->pltIndex, relOff);
+ off += target->pltEntrySize;
}
}
-template <class ELFT> void PltSection::addEntry(Symbol &Sym) {
- Sym.PltIndex = Entries.size();
- RelocationBaseSection *PltRelocSection = In.RelaPlt;
- if (IsIplt) {
- PltRelocSection = In.RelaIplt;
- Sym.IsInIplt = true;
- }
- unsigned RelOff =
- static_cast<RelocationSection<ELFT> *>(PltRelocSection)->getRelocOffset();
- Entries.push_back(std::make_pair(&Sym, RelOff));
+template <class ELFT> void PltSection::addEntry(Symbol &sym) {
+ sym.pltIndex = entries.size();
+ entries.push_back(&sym);
}
size_t PltSection::getSize() const {
- return HeaderSize + Entries.size() * Target->PltEntrySize;
+ return headerSize + entries.size() * target->pltEntrySize;
}
// Some architectures such as additional symbols in the PLT section. For
// example ARM uses mapping symbols to aid disassembly
void PltSection::addSymbols() {
// The PLT may have symbols defined for the Header, the IPLT has no header
- if (!IsIplt)
- Target->addPltHeaderSymbols(*this);
- size_t Off = HeaderSize;
- for (size_t I = 0; I < Entries.size(); ++I) {
- Target->addPltSymbols(*this, Off);
- Off += Target->PltEntrySize;
- }
-}
+ if (!isIplt)
+ target->addPltHeaderSymbols(*this);
-unsigned PltSection::getPltRelocOff() const {
- return IsIplt ? In.Plt->getSize() : 0;
+ size_t off = headerSize;
+ for (size_t i = 0; i < entries.size(); ++i) {
+ target->addPltSymbols(*this, off);
+ off += target->pltEntrySize;
+ }
}
// The string hash function for .gdb_index.
-static uint32_t computeGdbHash(StringRef S) {
- uint32_t H = 0;
- for (uint8_t C : S)
- H = H * 67 + toLower(C) - 113;
- return H;
+static uint32_t computeGdbHash(StringRef s) {
+ uint32_t h = 0;
+ for (uint8_t c : s)
+ h = h * 67 + toLower(c) - 113;
+ return h;
}
GdbIndexSection::GdbIndexSection()
@@ -2375,367 +2413,384 @@ GdbIndexSection::GdbIndexSection()
// Returns the desired size of an on-disk hash table for a .gdb_index section.
// There's a tradeoff between size and collision rate. We aim 75% utilization.
size_t GdbIndexSection::computeSymtabSize() const {
- return std::max<size_t>(NextPowerOf2(Symbols.size() * 4 / 3), 1024);
+ return std::max<size_t>(NextPowerOf2(symbols.size() * 4 / 3), 1024);
}
// Compute the output section size.
void GdbIndexSection::initOutputSize() {
- Size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8;
+ size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8;
- for (GdbChunk &Chunk : Chunks)
- Size += Chunk.CompilationUnits.size() * 16 + Chunk.AddressAreas.size() * 20;
+ for (GdbChunk &chunk : chunks)
+ size += chunk.compilationUnits.size() * 16 + chunk.addressAreas.size() * 20;
// Add the constant pool size if exists.
- if (!Symbols.empty()) {
- GdbSymbol &Sym = Symbols.back();
- Size += Sym.NameOff + Sym.Name.size() + 1;
+ if (!symbols.empty()) {
+ GdbSymbol &sym = symbols.back();
+ size += sym.nameOff + sym.name.size() + 1;
}
}
static std::vector<InputSection *> getDebugInfoSections() {
- std::vector<InputSection *> Ret;
- for (InputSectionBase *S : InputSections)
- if (InputSection *IS = dyn_cast<InputSection>(S))
- if (IS->Name == ".debug_info")
- Ret.push_back(IS);
- return Ret;
+ std::vector<InputSection *> ret;
+ for (InputSectionBase *s : inputSections)
+ if (InputSection *isec = dyn_cast<InputSection>(s))
+ if (isec->name == ".debug_info")
+ ret.push_back(isec);
+ return ret;
}
-static std::vector<GdbIndexSection::CuEntry> readCuList(DWARFContext &Dwarf) {
- std::vector<GdbIndexSection::CuEntry> Ret;
- for (std::unique_ptr<DWARFUnit> &Cu : Dwarf.compile_units())
- Ret.push_back({Cu->getOffset(), Cu->getLength() + 4});
- return Ret;
+static std::vector<GdbIndexSection::CuEntry> readCuList(DWARFContext &dwarf) {
+ std::vector<GdbIndexSection::CuEntry> ret;
+ for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units())
+ ret.push_back({cu->getOffset(), cu->getLength() + 4});
+ return ret;
}
static std::vector<GdbIndexSection::AddressEntry>
-readAddressAreas(DWARFContext &Dwarf, InputSection *Sec) {
- std::vector<GdbIndexSection::AddressEntry> Ret;
-
- uint32_t CuIdx = 0;
- for (std::unique_ptr<DWARFUnit> &Cu : Dwarf.compile_units()) {
- Expected<DWARFAddressRangesVector> Ranges = Cu->collectAddressRanges();
- if (!Ranges) {
- error(toString(Sec) + ": " + toString(Ranges.takeError()));
+readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
+ std::vector<GdbIndexSection::AddressEntry> ret;
+
+ uint32_t cuIdx = 0;
+ for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) {
+ Expected<DWARFAddressRangesVector> ranges = cu->collectAddressRanges();
+ if (!ranges) {
+ error(toString(sec) + ": " + toString(ranges.takeError()));
return {};
}
- ArrayRef<InputSectionBase *> Sections = Sec->File->getSections();
- for (DWARFAddressRange &R : *Ranges) {
- InputSectionBase *S = Sections[R.SectionIndex];
- if (!S || S == &InputSection::Discarded || !S->Live)
+ ArrayRef<InputSectionBase *> sections = sec->file->getSections();
+ for (DWARFAddressRange &r : *ranges) {
+ if (r.SectionIndex == -1ULL)
+ continue;
+ InputSectionBase *s = sections[r.SectionIndex];
+ if (!s || s == &InputSection::discarded || !s->isLive())
continue;
// Range list with zero size has no effect.
- if (R.LowPC == R.HighPC)
+ if (r.LowPC == r.HighPC)
continue;
- auto *IS = cast<InputSection>(S);
- uint64_t Offset = IS->getOffsetInFile();
- Ret.push_back({IS, R.LowPC - Offset, R.HighPC - Offset, CuIdx});
+ auto *isec = cast<InputSection>(s);
+ uint64_t offset = isec->getOffsetInFile();
+ ret.push_back({isec, r.LowPC - offset, r.HighPC - offset, cuIdx});
}
- ++CuIdx;
+ ++cuIdx;
}
- return Ret;
+ return ret;
}
template <class ELFT>
static std::vector<GdbIndexSection::NameAttrEntry>
-readPubNamesAndTypes(const LLDDwarfObj<ELFT> &Obj,
- const std::vector<GdbIndexSection::CuEntry> &CUs) {
- const DWARFSection &PubNames = Obj.getGnuPubNamesSection();
- const DWARFSection &PubTypes = Obj.getGnuPubTypesSection();
-
- std::vector<GdbIndexSection::NameAttrEntry> Ret;
- for (const DWARFSection *Pub : {&PubNames, &PubTypes}) {
- DWARFDebugPubTable Table(Obj, *Pub, Config->IsLE, true);
- for (const DWARFDebugPubTable::Set &Set : Table.getData()) {
- // The value written into the constant pool is Kind << 24 | CuIndex. As we
+readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
+ const std::vector<GdbIndexSection::CuEntry> &cUs) {
+ const DWARFSection &pubNames = obj.getGnuPubNamesSection();
+ const DWARFSection &pubTypes = obj.getGnuPubTypesSection();
+
+ std::vector<GdbIndexSection::NameAttrEntry> ret;
+ for (const DWARFSection *pub : {&pubNames, &pubTypes}) {
+ DWARFDebugPubTable table(obj, *pub, config->isLE, true);
+ for (const DWARFDebugPubTable::Set &set : table.getData()) {
+ // The value written into the constant pool is kind << 24 | cuIndex. As we
// don't know how many compilation units precede this object to compute
- // CuIndex, we compute (Kind << 24 | CuIndexInThisObject) instead, and add
+ // cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add
// the number of preceding compilation units later.
- uint32_t I =
- lower_bound(CUs, Set.Offset,
- [](GdbIndexSection::CuEntry CU, uint32_t Offset) {
- return CU.CuOffset < Offset;
+ uint32_t i =
+ lower_bound(cUs, set.Offset,
+ [](GdbIndexSection::CuEntry cu, uint32_t offset) {
+ return cu.cuOffset < offset;
}) -
- CUs.begin();
- for (const DWARFDebugPubTable::Entry &Ent : Set.Entries)
- Ret.push_back({{Ent.Name, computeGdbHash(Ent.Name)},
- (Ent.Descriptor.toBits() << 24) | I});
+ cUs.begin();
+ for (const DWARFDebugPubTable::Entry &ent : set.Entries)
+ ret.push_back({{ent.Name, computeGdbHash(ent.Name)},
+ (ent.Descriptor.toBits() << 24) | i});
}
}
- return Ret;
+ return ret;
}
// Create a list of symbols from a given list of symbol names and types
// by uniquifying them by name.
static std::vector<GdbIndexSection::GdbSymbol>
-createSymbols(ArrayRef<std::vector<GdbIndexSection::NameAttrEntry>> NameAttrs,
- const std::vector<GdbIndexSection::GdbChunk> &Chunks) {
- typedef GdbIndexSection::GdbSymbol GdbSymbol;
- typedef GdbIndexSection::NameAttrEntry NameAttrEntry;
+createSymbols(ArrayRef<std::vector<GdbIndexSection::NameAttrEntry>> nameAttrs,
+ const std::vector<GdbIndexSection::GdbChunk> &chunks) {
+ using GdbSymbol = GdbIndexSection::GdbSymbol;
+ using NameAttrEntry = GdbIndexSection::NameAttrEntry;
// For each chunk, compute the number of compilation units preceding it.
- uint32_t CuIdx = 0;
- std::vector<uint32_t> CuIdxs(Chunks.size());
- for (uint32_t I = 0, E = Chunks.size(); I != E; ++I) {
- CuIdxs[I] = CuIdx;
- CuIdx += Chunks[I].CompilationUnits.size();
+ uint32_t cuIdx = 0;
+ std::vector<uint32_t> cuIdxs(chunks.size());
+ for (uint32_t i = 0, e = chunks.size(); i != e; ++i) {
+ cuIdxs[i] = cuIdx;
+ cuIdx += chunks[i].compilationUnits.size();
}
// The number of symbols we will handle in this function is of the order
// of millions for very large executables, so we use multi-threading to
// speed it up.
- size_t NumShards = 32;
- size_t Concurrency = 1;
- if (ThreadsEnabled)
- Concurrency =
- std::min<size_t>(PowerOf2Floor(hardware_concurrency()), NumShards);
+ size_t numShards = 32;
+ size_t concurrency = 1;
+ if (threadsEnabled)
+ concurrency =
+ std::min<size_t>(PowerOf2Floor(hardware_concurrency()), numShards);
// A sharded map to uniquify symbols by name.
- std::vector<DenseMap<CachedHashStringRef, size_t>> Map(NumShards);
- size_t Shift = 32 - countTrailingZeros(NumShards);
+ std::vector<DenseMap<CachedHashStringRef, size_t>> map(numShards);
+ size_t shift = 32 - countTrailingZeros(numShards);
// Instantiate GdbSymbols while uniqufying them by name.
- std::vector<std::vector<GdbSymbol>> Symbols(NumShards);
- parallelForEachN(0, Concurrency, [&](size_t ThreadId) {
- uint32_t I = 0;
- for (ArrayRef<NameAttrEntry> Entries : NameAttrs) {
- for (const NameAttrEntry &Ent : Entries) {
- size_t ShardId = Ent.Name.hash() >> Shift;
- if ((ShardId & (Concurrency - 1)) != ThreadId)
+ std::vector<std::vector<GdbSymbol>> symbols(numShards);
+ parallelForEachN(0, concurrency, [&](size_t threadId) {
+ uint32_t i = 0;
+ for (ArrayRef<NameAttrEntry> entries : nameAttrs) {
+ for (const NameAttrEntry &ent : entries) {
+ size_t shardId = ent.name.hash() >> shift;
+ if ((shardId & (concurrency - 1)) != threadId)
continue;
- uint32_t V = Ent.CuIndexAndAttrs + CuIdxs[I];
- size_t &Idx = Map[ShardId][Ent.Name];
- if (Idx) {
- Symbols[ShardId][Idx - 1].CuVector.push_back(V);
+ uint32_t v = ent.cuIndexAndAttrs + cuIdxs[i];
+ size_t &idx = map[shardId][ent.name];
+ if (idx) {
+ symbols[shardId][idx - 1].cuVector.push_back(v);
continue;
}
- Idx = Symbols[ShardId].size() + 1;
- Symbols[ShardId].push_back({Ent.Name, {V}, 0, 0});
+ idx = symbols[shardId].size() + 1;
+ symbols[shardId].push_back({ent.name, {v}, 0, 0});
}
- ++I;
+ ++i;
}
});
- size_t NumSymbols = 0;
- for (ArrayRef<GdbSymbol> V : Symbols)
- NumSymbols += V.size();
+ size_t numSymbols = 0;
+ for (ArrayRef<GdbSymbol> v : symbols)
+ numSymbols += v.size();
// The return type is a flattened vector, so we'll copy each vector
// contents to Ret.
- std::vector<GdbSymbol> Ret;
- Ret.reserve(NumSymbols);
- for (std::vector<GdbSymbol> &Vec : Symbols)
- for (GdbSymbol &Sym : Vec)
- Ret.push_back(std::move(Sym));
+ std::vector<GdbSymbol> ret;
+ ret.reserve(numSymbols);
+ for (std::vector<GdbSymbol> &vec : symbols)
+ for (GdbSymbol &sym : vec)
+ ret.push_back(std::move(sym));
// CU vectors and symbol names are adjacent in the output file.
// We can compute their offsets in the output file now.
- size_t Off = 0;
- for (GdbSymbol &Sym : Ret) {
- Sym.CuVectorOff = Off;
- Off += (Sym.CuVector.size() + 1) * 4;
+ size_t off = 0;
+ for (GdbSymbol &sym : ret) {
+ sym.cuVectorOff = off;
+ off += (sym.cuVector.size() + 1) * 4;
}
- for (GdbSymbol &Sym : Ret) {
- Sym.NameOff = Off;
- Off += Sym.Name.size() + 1;
+ for (GdbSymbol &sym : ret) {
+ sym.nameOff = off;
+ off += sym.name.size() + 1;
}
- return Ret;
+ return ret;
}
// Returns a newly-created .gdb_index section.
template <class ELFT> GdbIndexSection *GdbIndexSection::create() {
- std::vector<InputSection *> Sections = getDebugInfoSections();
+ std::vector<InputSection *> sections = getDebugInfoSections();
// .debug_gnu_pub{names,types} are useless in executables.
// They are present in input object files solely for creating
// a .gdb_index. So we can remove them from the output.
- for (InputSectionBase *S : InputSections)
- if (S->Name == ".debug_gnu_pubnames" || S->Name == ".debug_gnu_pubtypes")
- S->Live = false;
-
- std::vector<GdbChunk> Chunks(Sections.size());
- std::vector<std::vector<NameAttrEntry>> NameAttrs(Sections.size());
-
- parallelForEachN(0, Sections.size(), [&](size_t I) {
- ObjFile<ELFT> *File = Sections[I]->getFile<ELFT>();
- DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(File));
-
- Chunks[I].Sec = Sections[I];
- Chunks[I].CompilationUnits = readCuList(Dwarf);
- Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]);
- NameAttrs[I] = readPubNamesAndTypes<ELFT>(
- static_cast<const LLDDwarfObj<ELFT> &>(Dwarf.getDWARFObj()),
- Chunks[I].CompilationUnits);
+ for (InputSectionBase *s : inputSections)
+ if (s->name == ".debug_gnu_pubnames" || s->name == ".debug_gnu_pubtypes")
+ s->markDead();
+
+ std::vector<GdbChunk> chunks(sections.size());
+ std::vector<std::vector<NameAttrEntry>> nameAttrs(sections.size());
+
+ parallelForEachN(0, sections.size(), [&](size_t i) {
+ ObjFile<ELFT> *file = sections[i]->getFile<ELFT>();
+ DWARFContext dwarf(make_unique<LLDDwarfObj<ELFT>>(file));
+
+ chunks[i].sec = sections[i];
+ chunks[i].compilationUnits = readCuList(dwarf);
+ chunks[i].addressAreas = readAddressAreas(dwarf, sections[i]);
+ nameAttrs[i] = readPubNamesAndTypes<ELFT>(
+ static_cast<const LLDDwarfObj<ELFT> &>(dwarf.getDWARFObj()),
+ chunks[i].compilationUnits);
});
- auto *Ret = make<GdbIndexSection>();
- Ret->Chunks = std::move(Chunks);
- Ret->Symbols = createSymbols(NameAttrs, Ret->Chunks);
- Ret->initOutputSize();
- return Ret;
+ auto *ret = make<GdbIndexSection>();
+ ret->chunks = std::move(chunks);
+ ret->symbols = createSymbols(nameAttrs, ret->chunks);
+ ret->initOutputSize();
+ return ret;
}
-void GdbIndexSection::writeTo(uint8_t *Buf) {
+void GdbIndexSection::writeTo(uint8_t *buf) {
// Write the header.
- auto *Hdr = reinterpret_cast<GdbIndexHeader *>(Buf);
- uint8_t *Start = Buf;
- Hdr->Version = 7;
- Buf += sizeof(*Hdr);
+ auto *hdr = reinterpret_cast<GdbIndexHeader *>(buf);
+ uint8_t *start = buf;
+ hdr->version = 7;
+ buf += sizeof(*hdr);
// Write the CU list.
- Hdr->CuListOff = Buf - Start;
- for (GdbChunk &Chunk : Chunks) {
- for (CuEntry &Cu : Chunk.CompilationUnits) {
- write64le(Buf, Chunk.Sec->OutSecOff + Cu.CuOffset);
- write64le(Buf + 8, Cu.CuLength);
- Buf += 16;
+ hdr->cuListOff = buf - start;
+ for (GdbChunk &chunk : chunks) {
+ for (CuEntry &cu : chunk.compilationUnits) {
+ write64le(buf, chunk.sec->outSecOff + cu.cuOffset);
+ write64le(buf + 8, cu.cuLength);
+ buf += 16;
}
}
// Write the address area.
- Hdr->CuTypesOff = Buf - Start;
- Hdr->AddressAreaOff = Buf - Start;
- uint32_t CuOff = 0;
- for (GdbChunk &Chunk : Chunks) {
- for (AddressEntry &E : Chunk.AddressAreas) {
- uint64_t BaseAddr = E.Section->getVA(0);
- write64le(Buf, BaseAddr + E.LowAddress);
- write64le(Buf + 8, BaseAddr + E.HighAddress);
- write32le(Buf + 16, E.CuIndex + CuOff);
- Buf += 20;
+ hdr->cuTypesOff = buf - start;
+ hdr->addressAreaOff = buf - start;
+ uint32_t cuOff = 0;
+ for (GdbChunk &chunk : chunks) {
+ for (AddressEntry &e : chunk.addressAreas) {
+ uint64_t baseAddr = e.section->getVA(0);
+ write64le(buf, baseAddr + e.lowAddress);
+ write64le(buf + 8, baseAddr + e.highAddress);
+ write32le(buf + 16, e.cuIndex + cuOff);
+ buf += 20;
}
- CuOff += Chunk.CompilationUnits.size();
+ cuOff += chunk.compilationUnits.size();
}
// Write the on-disk open-addressing hash table containing symbols.
- Hdr->SymtabOff = Buf - Start;
- size_t SymtabSize = computeSymtabSize();
- uint32_t Mask = SymtabSize - 1;
+ hdr->symtabOff = buf - start;
+ size_t symtabSize = computeSymtabSize();
+ uint32_t mask = symtabSize - 1;
- for (GdbSymbol &Sym : Symbols) {
- uint32_t H = Sym.Name.hash();
- uint32_t I = H & Mask;
- uint32_t Step = ((H * 17) & Mask) | 1;
+ for (GdbSymbol &sym : symbols) {
+ uint32_t h = sym.name.hash();
+ uint32_t i = h & mask;
+ uint32_t step = ((h * 17) & mask) | 1;
- while (read32le(Buf + I * 8))
- I = (I + Step) & Mask;
+ while (read32le(buf + i * 8))
+ i = (i + step) & mask;
- write32le(Buf + I * 8, Sym.NameOff);
- write32le(Buf + I * 8 + 4, Sym.CuVectorOff);
+ write32le(buf + i * 8, sym.nameOff);
+ write32le(buf + i * 8 + 4, sym.cuVectorOff);
}
- Buf += SymtabSize * 8;
+ buf += symtabSize * 8;
// Write the string pool.
- Hdr->ConstantPoolOff = Buf - Start;
- parallelForEach(Symbols, [&](GdbSymbol &Sym) {
- memcpy(Buf + Sym.NameOff, Sym.Name.data(), Sym.Name.size());
+ hdr->constantPoolOff = buf - start;
+ parallelForEach(symbols, [&](GdbSymbol &sym) {
+ memcpy(buf + sym.nameOff, sym.name.data(), sym.name.size());
});
// Write the CU vectors.
- for (GdbSymbol &Sym : Symbols) {
- write32le(Buf, Sym.CuVector.size());
- Buf += 4;
- for (uint32_t Val : Sym.CuVector) {
- write32le(Buf, Val);
- Buf += 4;
+ for (GdbSymbol &sym : symbols) {
+ write32le(buf, sym.cuVector.size());
+ buf += 4;
+ for (uint32_t val : sym.cuVector) {
+ write32le(buf, val);
+ buf += 4;
}
}
}
-bool GdbIndexSection::empty() const { return Chunks.empty(); }
+bool GdbIndexSection::isNeeded() const { return !chunks.empty(); }
EhFrameHeader::EhFrameHeader()
: SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".eh_frame_hdr") {}
+void EhFrameHeader::writeTo(uint8_t *buf) {
+ // Unlike most sections, the EhFrameHeader section is written while writing
+ // another section, namely EhFrameSection, which calls the write() function
+ // below from its writeTo() function. This is necessary because the contents
+ // of EhFrameHeader depend on the relocated contents of EhFrameSection and we
+ // don't know which order the sections will be written in.
+}
+
// .eh_frame_hdr contains a binary search table of pointers to FDEs.
// Each entry of the search table consists of two values,
// the starting PC from where FDEs covers, and the FDE's address.
// It is sorted by PC.
-void EhFrameHeader::writeTo(uint8_t *Buf) {
- typedef EhFrameSection::FdeData FdeData;
+void EhFrameHeader::write() {
+ uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff;
+ using FdeData = EhFrameSection::FdeData;
- std::vector<FdeData> Fdes = In.EhFrame->getFdeData();
+ std::vector<FdeData> fdes = getPartition().ehFrame->getFdeData();
- Buf[0] = 1;
- Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
- Buf[2] = DW_EH_PE_udata4;
- Buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
- write32(Buf + 4, In.EhFrame->getParent()->Addr - this->getVA() - 4);
- write32(Buf + 8, Fdes.size());
- Buf += 12;
+ buf[0] = 1;
+ buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
+ buf[2] = DW_EH_PE_udata4;
+ buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
+ write32(buf + 4,
+ getPartition().ehFrame->getParent()->addr - this->getVA() - 4);
+ write32(buf + 8, fdes.size());
+ buf += 12;
- for (FdeData &Fde : Fdes) {
- write32(Buf, Fde.PcRel);
- write32(Buf + 4, Fde.FdeVARel);
- Buf += 8;
+ for (FdeData &fde : fdes) {
+ write32(buf, fde.pcRel);
+ write32(buf + 4, fde.fdeVARel);
+ buf += 8;
}
}
size_t EhFrameHeader::getSize() const {
// .eh_frame_hdr has a 12 bytes header followed by an array of FDEs.
- return 12 + In.EhFrame->NumFdes * 8;
+ return 12 + getPartition().ehFrame->numFdes * 8;
}
-bool EhFrameHeader::empty() const { return In.EhFrame->empty(); }
+bool EhFrameHeader::isNeeded() const {
+ return isLive() && getPartition().ehFrame->isNeeded();
+}
VersionDefinitionSection::VersionDefinitionSection()
: SyntheticSection(SHF_ALLOC, SHT_GNU_verdef, sizeof(uint32_t),
".gnu.version_d") {}
-static StringRef getFileDefName() {
- if (!Config->SoName.empty())
- return Config->SoName;
- return Config->OutputFile;
+StringRef VersionDefinitionSection::getFileDefName() {
+ if (!getPartition().name.empty())
+ return getPartition().name;
+ if (!config->soName.empty())
+ return config->soName;
+ return config->outputFile;
}
void VersionDefinitionSection::finalizeContents() {
- FileDefNameOff = In.DynStrTab->addString(getFileDefName());
- for (VersionDefinition &V : Config->VersionDefinitions)
- V.NameOff = In.DynStrTab->addString(V.Name);
+ fileDefNameOff = getPartition().dynStrTab->addString(getFileDefName());
+ for (VersionDefinition &v : config->versionDefinitions)
+ verDefNameOffs.push_back(getPartition().dynStrTab->addString(v.name));
- if (OutputSection *Sec = In.DynStrTab->getParent())
- getParent()->Link = Sec->SectionIndex;
+ if (OutputSection *sec = getPartition().dynStrTab->getParent())
+ getParent()->link = sec->sectionIndex;
// sh_info should be set to the number of definitions. This fact is missed in
// documentation, but confirmed by binutils community:
// https://sourceware.org/ml/binutils/2014-11/msg00355.html
- getParent()->Info = getVerDefNum();
+ getParent()->info = getVerDefNum();
}
-void VersionDefinitionSection::writeOne(uint8_t *Buf, uint32_t Index,
- StringRef Name, size_t NameOff) {
- uint16_t Flags = Index == 1 ? VER_FLG_BASE : 0;
+void VersionDefinitionSection::writeOne(uint8_t *buf, uint32_t index,
+ StringRef name, size_t nameOff) {
+ uint16_t flags = index == 1 ? VER_FLG_BASE : 0;
// Write a verdef.
- write16(Buf, 1); // vd_version
- write16(Buf + 2, Flags); // vd_flags
- write16(Buf + 4, Index); // vd_ndx
- write16(Buf + 6, 1); // vd_cnt
- write32(Buf + 8, hashSysV(Name)); // vd_hash
- write32(Buf + 12, 20); // vd_aux
- write32(Buf + 16, 28); // vd_next
+ write16(buf, 1); // vd_version
+ write16(buf + 2, flags); // vd_flags
+ write16(buf + 4, index); // vd_ndx
+ write16(buf + 6, 1); // vd_cnt
+ write32(buf + 8, hashSysV(name)); // vd_hash
+ write32(buf + 12, 20); // vd_aux
+ write32(buf + 16, 28); // vd_next
// Write a veraux.
- write32(Buf + 20, NameOff); // vda_name
- write32(Buf + 24, 0); // vda_next
+ write32(buf + 20, nameOff); // vda_name
+ write32(buf + 24, 0); // vda_next
}
-void VersionDefinitionSection::writeTo(uint8_t *Buf) {
- writeOne(Buf, 1, getFileDefName(), FileDefNameOff);
+void VersionDefinitionSection::writeTo(uint8_t *buf) {
+ writeOne(buf, 1, getFileDefName(), fileDefNameOff);
- for (VersionDefinition &V : Config->VersionDefinitions) {
- Buf += EntrySize;
- writeOne(Buf, V.Id, V.Name, V.NameOff);
+ auto nameOffIt = verDefNameOffs.begin();
+ for (VersionDefinition &v : config->versionDefinitions) {
+ buf += EntrySize;
+ writeOne(buf, v.id, v.name, *nameOffIt++);
}
// Need to terminate the last version definition.
- write32(Buf + 16, 0); // vd_next
+ write32(buf + 16, 0); // vd_next
}
size_t VersionDefinitionSection::getSize() const {
@@ -2743,160 +2798,161 @@ size_t VersionDefinitionSection::getSize() const {
}
// .gnu.version is a table where each entry is 2 byte long.
-template <class ELFT>
-VersionTableSection<ELFT>::VersionTableSection()
+VersionTableSection::VersionTableSection()
: SyntheticSection(SHF_ALLOC, SHT_GNU_versym, sizeof(uint16_t),
".gnu.version") {
- this->Entsize = 2;
+ this->entsize = 2;
}
-template <class ELFT> void VersionTableSection<ELFT>::finalizeContents() {
+void VersionTableSection::finalizeContents() {
// At the moment of june 2016 GNU docs does not mention that sh_link field
// should be set, but Sun docs do. Also readelf relies on this field.
- getParent()->Link = In.DynSymTab->getParent()->SectionIndex;
+ getParent()->link = getPartition().dynSymTab->getParent()->sectionIndex;
}
-template <class ELFT> size_t VersionTableSection<ELFT>::getSize() const {
- return (In.DynSymTab->getSymbols().size() + 1) * 2;
+size_t VersionTableSection::getSize() const {
+ return (getPartition().dynSymTab->getSymbols().size() + 1) * 2;
}
-template <class ELFT> void VersionTableSection<ELFT>::writeTo(uint8_t *Buf) {
- Buf += 2;
- for (const SymbolTableEntry &S : In.DynSymTab->getSymbols()) {
- write16(Buf, S.Sym->VersionId);
- Buf += 2;
+void VersionTableSection::writeTo(uint8_t *buf) {
+ buf += 2;
+ for (const SymbolTableEntry &s : getPartition().dynSymTab->getSymbols()) {
+ write16(buf, s.sym->versionId);
+ buf += 2;
}
}
-template <class ELFT> bool VersionTableSection<ELFT>::empty() const {
- return !In.VerDef && InX<ELFT>::VerNeed->empty();
+bool VersionTableSection::isNeeded() const {
+ return getPartition().verDef || getPartition().verNeed->isNeeded();
+}
+
+void elf::addVerneed(Symbol *ss) {
+ auto &file = cast<SharedFile>(*ss->file);
+ if (ss->verdefIndex == VER_NDX_GLOBAL) {
+ ss->versionId = VER_NDX_GLOBAL;
+ return;
+ }
+
+ if (file.vernauxs.empty())
+ file.vernauxs.resize(file.verdefs.size());
+
+ // Select a version identifier for the vernaux data structure, if we haven't
+ // already allocated one. The verdef identifiers cover the range
+ // [1..getVerDefNum()]; this causes the vernaux identifiers to start from
+ // getVerDefNum()+1.
+ if (file.vernauxs[ss->verdefIndex] == 0)
+ file.vernauxs[ss->verdefIndex] = ++SharedFile::vernauxNum + getVerDefNum();
+
+ ss->versionId = file.vernauxs[ss->verdefIndex];
}
template <class ELFT>
VersionNeedSection<ELFT>::VersionNeedSection()
: SyntheticSection(SHF_ALLOC, SHT_GNU_verneed, sizeof(uint32_t),
- ".gnu.version_r") {
- // Identifiers in verneed section start at 2 because 0 and 1 are reserved
- // for VER_NDX_LOCAL and VER_NDX_GLOBAL.
- // First identifiers are reserved by verdef section if it exist.
- NextIndex = getVerDefNum() + 1;
-}
+ ".gnu.version_r") {}
-template <class ELFT> void VersionNeedSection<ELFT>::addSymbol(Symbol *SS) {
- auto &File = cast<SharedFile<ELFT>>(*SS->File);
- if (SS->VerdefIndex == VER_NDX_GLOBAL) {
- SS->VersionId = VER_NDX_GLOBAL;
- return;
+template <class ELFT> void VersionNeedSection<ELFT>::finalizeContents() {
+ for (SharedFile *f : sharedFiles) {
+ if (f->vernauxs.empty())
+ continue;
+ verneeds.emplace_back();
+ Verneed &vn = verneeds.back();
+ vn.nameStrTab = getPartition().dynStrTab->addString(f->soName);
+ for (unsigned i = 0; i != f->vernauxs.size(); ++i) {
+ if (f->vernauxs[i] == 0)
+ continue;
+ auto *verdef =
+ reinterpret_cast<const typename ELFT::Verdef *>(f->verdefs[i]);
+ vn.vernauxs.push_back(
+ {verdef->vd_hash, f->vernauxs[i],
+ getPartition().dynStrTab->addString(f->getStringTable().data() +
+ verdef->getAux()->vda_name)});
+ }
}
- // If we don't already know that we need an Elf_Verneed for this DSO, prepare
- // to create one by adding it to our needed list and creating a dynstr entry
- // for the soname.
- if (File.VerdefMap.empty())
- Needed.push_back({&File, In.DynStrTab->addString(File.SoName)});
- const typename ELFT::Verdef *Ver = File.Verdefs[SS->VerdefIndex];
- typename SharedFile<ELFT>::NeededVer &NV = File.VerdefMap[Ver];
-
- // If we don't already know that we need an Elf_Vernaux for this Elf_Verdef,
- // prepare to create one by allocating a version identifier and creating a
- // dynstr entry for the version name.
- if (NV.Index == 0) {
- NV.StrTab = In.DynStrTab->addString(File.getStringTable().data() +
- Ver->getAux()->vda_name);
- NV.Index = NextIndex++;
- }
- SS->VersionId = NV.Index;
+ if (OutputSection *sec = getPartition().dynStrTab->getParent())
+ getParent()->link = sec->sectionIndex;
+ getParent()->info = verneeds.size();
}
-template <class ELFT> void VersionNeedSection<ELFT>::writeTo(uint8_t *Buf) {
+template <class ELFT> void VersionNeedSection<ELFT>::writeTo(uint8_t *buf) {
// The Elf_Verneeds need to appear first, followed by the Elf_Vernauxs.
- auto *Verneed = reinterpret_cast<Elf_Verneed *>(Buf);
- auto *Vernaux = reinterpret_cast<Elf_Vernaux *>(Verneed + Needed.size());
+ auto *verneed = reinterpret_cast<Elf_Verneed *>(buf);
+ auto *vernaux = reinterpret_cast<Elf_Vernaux *>(verneed + verneeds.size());
- for (std::pair<SharedFile<ELFT> *, size_t> &P : Needed) {
+ for (auto &vn : verneeds) {
// Create an Elf_Verneed for this DSO.
- Verneed->vn_version = 1;
- Verneed->vn_cnt = P.first->VerdefMap.size();
- Verneed->vn_file = P.second;
- Verneed->vn_aux =
- reinterpret_cast<char *>(Vernaux) - reinterpret_cast<char *>(Verneed);
- Verneed->vn_next = sizeof(Elf_Verneed);
- ++Verneed;
-
- // Create the Elf_Vernauxs for this Elf_Verneed. The loop iterates over
- // VerdefMap, which will only contain references to needed version
- // definitions. Each Elf_Vernaux is based on the information contained in
- // the Elf_Verdef in the source DSO. This loop iterates over a std::map of
- // pointers, but is deterministic because the pointers refer to Elf_Verdef
- // data structures within a single input file.
- for (auto &NV : P.first->VerdefMap) {
- Vernaux->vna_hash = NV.first->vd_hash;
- Vernaux->vna_flags = 0;
- Vernaux->vna_other = NV.second.Index;
- Vernaux->vna_name = NV.second.StrTab;
- Vernaux->vna_next = sizeof(Elf_Vernaux);
- ++Vernaux;
+ verneed->vn_version = 1;
+ verneed->vn_cnt = vn.vernauxs.size();
+ verneed->vn_file = vn.nameStrTab;
+ verneed->vn_aux =
+ reinterpret_cast<char *>(vernaux) - reinterpret_cast<char *>(verneed);
+ verneed->vn_next = sizeof(Elf_Verneed);
+ ++verneed;
+
+ // Create the Elf_Vernauxs for this Elf_Verneed.
+ for (auto &vna : vn.vernauxs) {
+ vernaux->vna_hash = vna.hash;
+ vernaux->vna_flags = 0;
+ vernaux->vna_other = vna.verneedIndex;
+ vernaux->vna_name = vna.nameStrTab;
+ vernaux->vna_next = sizeof(Elf_Vernaux);
+ ++vernaux;
}
- Vernaux[-1].vna_next = 0;
+ vernaux[-1].vna_next = 0;
}
- Verneed[-1].vn_next = 0;
-}
-
-template <class ELFT> void VersionNeedSection<ELFT>::finalizeContents() {
- if (OutputSection *Sec = In.DynStrTab->getParent())
- getParent()->Link = Sec->SectionIndex;
- getParent()->Info = Needed.size();
+ verneed[-1].vn_next = 0;
}
template <class ELFT> size_t VersionNeedSection<ELFT>::getSize() const {
- unsigned Size = Needed.size() * sizeof(Elf_Verneed);
- for (const std::pair<SharedFile<ELFT> *, size_t> &P : Needed)
- Size += P.first->VerdefMap.size() * sizeof(Elf_Vernaux);
- return Size;
+ return verneeds.size() * sizeof(Elf_Verneed) +
+ SharedFile::vernauxNum * sizeof(Elf_Vernaux);
}
-template <class ELFT> bool VersionNeedSection<ELFT>::empty() const {
- return getNeedNum() == 0;
+template <class ELFT> bool VersionNeedSection<ELFT>::isNeeded() const {
+ return SharedFile::vernauxNum != 0;
}
-void MergeSyntheticSection::addSection(MergeInputSection *MS) {
- MS->Parent = this;
- Sections.push_back(MS);
+void MergeSyntheticSection::addSection(MergeInputSection *ms) {
+ ms->parent = this;
+ sections.push_back(ms);
+ assert(alignment == ms->alignment || !(ms->flags & SHF_STRINGS));
+ alignment = std::max(alignment, ms->alignment);
}
-MergeTailSection::MergeTailSection(StringRef Name, uint32_t Type,
- uint64_t Flags, uint32_t Alignment)
- : MergeSyntheticSection(Name, Type, Flags, Alignment),
- Builder(StringTableBuilder::RAW, Alignment) {}
+MergeTailSection::MergeTailSection(StringRef name, uint32_t type,
+ uint64_t flags, uint32_t alignment)
+ : MergeSyntheticSection(name, type, flags, alignment),
+ builder(StringTableBuilder::RAW, alignment) {}
-size_t MergeTailSection::getSize() const { return Builder.getSize(); }
+size_t MergeTailSection::getSize() const { return builder.getSize(); }
-void MergeTailSection::writeTo(uint8_t *Buf) { Builder.write(Buf); }
+void MergeTailSection::writeTo(uint8_t *buf) { builder.write(buf); }
void MergeTailSection::finalizeContents() {
// Add all string pieces to the string table builder to create section
// contents.
- for (MergeInputSection *Sec : Sections)
- for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
- if (Sec->Pieces[I].Live)
- Builder.add(Sec->getData(I));
+ for (MergeInputSection *sec : sections)
+ for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
+ if (sec->pieces[i].live)
+ builder.add(sec->getData(i));
// Fix the string table content. After this, the contents will never change.
- Builder.finalize();
+ builder.finalize();
// finalize() fixed tail-optimized strings, so we can now get
// offsets of strings. Get an offset for each string and save it
- // to a corresponding StringPiece for easy access.
- for (MergeInputSection *Sec : Sections)
- for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
- if (Sec->Pieces[I].Live)
- Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I));
+ // to a corresponding SectionPiece for easy access.
+ for (MergeInputSection *sec : sections)
+ for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
+ if (sec->pieces[i].live)
+ sec->pieces[i].outputOff = builder.getOffset(sec->getData(i));
}
-void MergeNoTailSection::writeTo(uint8_t *Buf) {
- for (size_t I = 0; I < NumShards; ++I)
- Shards[I].write(Buf + ShardOffsets[I]);
+void MergeNoTailSection::writeTo(uint8_t *buf) {
+ for (size_t i = 0; i < numShards; ++i)
+ shards[i].write(buf + shardOffsets[i]);
}
// This function is very hot (i.e. it can take several seconds to finish)
@@ -2909,66 +2965,68 @@ void MergeNoTailSection::writeTo(uint8_t *Buf) {
// We do it in parallel.
void MergeNoTailSection::finalizeContents() {
// Initializes string table builders.
- for (size_t I = 0; I < NumShards; ++I)
- Shards.emplace_back(StringTableBuilder::RAW, Alignment);
+ for (size_t i = 0; i < numShards; ++i)
+ shards.emplace_back(StringTableBuilder::RAW, alignment);
// Concurrency level. Must be a power of 2 to avoid expensive modulo
// operations in the following tight loop.
- size_t Concurrency = 1;
- if (ThreadsEnabled)
- Concurrency =
- std::min<size_t>(PowerOf2Floor(hardware_concurrency()), NumShards);
+ size_t concurrency = 1;
+ if (threadsEnabled)
+ concurrency =
+ std::min<size_t>(PowerOf2Floor(hardware_concurrency()), numShards);
// Add section pieces to the builders.
- parallelForEachN(0, Concurrency, [&](size_t ThreadId) {
- for (MergeInputSection *Sec : Sections) {
- for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) {
- size_t ShardId = getShardId(Sec->Pieces[I].Hash);
- if ((ShardId & (Concurrency - 1)) == ThreadId && Sec->Pieces[I].Live)
- Sec->Pieces[I].OutputOff = Shards[ShardId].add(Sec->getData(I));
+ parallelForEachN(0, concurrency, [&](size_t threadId) {
+ for (MergeInputSection *sec : sections) {
+ for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) {
+ if (!sec->pieces[i].live)
+ continue;
+ size_t shardId = getShardId(sec->pieces[i].hash);
+ if ((shardId & (concurrency - 1)) == threadId)
+ sec->pieces[i].outputOff = shards[shardId].add(sec->getData(i));
}
}
});
// Compute an in-section offset for each shard.
- size_t Off = 0;
- for (size_t I = 0; I < NumShards; ++I) {
- Shards[I].finalizeInOrder();
- if (Shards[I].getSize() > 0)
- Off = alignTo(Off, Alignment);
- ShardOffsets[I] = Off;
- Off += Shards[I].getSize();
+ size_t off = 0;
+ for (size_t i = 0; i < numShards; ++i) {
+ shards[i].finalizeInOrder();
+ if (shards[i].getSize() > 0)
+ off = alignTo(off, alignment);
+ shardOffsets[i] = off;
+ off += shards[i].getSize();
}
- Size = Off;
+ size = off;
// So far, section pieces have offsets from beginning of shards, but
// we want offsets from beginning of the whole section. Fix them.
- parallelForEach(Sections, [&](MergeInputSection *Sec) {
- for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
- if (Sec->Pieces[I].Live)
- Sec->Pieces[I].OutputOff +=
- ShardOffsets[getShardId(Sec->Pieces[I].Hash)];
+ parallelForEach(sections, [&](MergeInputSection *sec) {
+ for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
+ if (sec->pieces[i].live)
+ sec->pieces[i].outputOff +=
+ shardOffsets[getShardId(sec->pieces[i].hash)];
});
}
-static MergeSyntheticSection *createMergeSynthetic(StringRef Name,
- uint32_t Type,
- uint64_t Flags,
- uint32_t Alignment) {
- bool ShouldTailMerge = (Flags & SHF_STRINGS) && Config->Optimize >= 2;
- if (ShouldTailMerge)
- return make<MergeTailSection>(Name, Type, Flags, Alignment);
- return make<MergeNoTailSection>(Name, Type, Flags, Alignment);
+static MergeSyntheticSection *createMergeSynthetic(StringRef name,
+ uint32_t type,
+ uint64_t flags,
+ uint32_t alignment) {
+ bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2;
+ if (shouldTailMerge)
+ return make<MergeTailSection>(name, type, flags, alignment);
+ return make<MergeNoTailSection>(name, type, flags, alignment);
}
template <class ELFT> void elf::splitSections() {
// splitIntoPieces needs to be called on each MergeInputSection
// before calling finalizeContents().
- parallelForEach(InputSections, [](InputSectionBase *Sec) {
- if (auto *S = dyn_cast<MergeInputSection>(Sec))
- S->splitIntoPieces();
- else if (auto *Eh = dyn_cast<EhInputSection>(Sec))
- Eh->split<ELFT>();
+ parallelForEach(inputSections, [](InputSectionBase *sec) {
+ if (auto *s = dyn_cast<MergeInputSection>(sec))
+ s->splitIntoPieces();
+ else if (auto *eh = dyn_cast<EhInputSection>(sec))
+ eh->split<ELFT>();
});
}
@@ -2980,23 +3038,22 @@ template <class ELFT> void elf::splitSections() {
// that it replaces. It then finalizes each synthetic section in order
// to compute an output offset for each piece of each input section.
void elf::mergeSections() {
- std::vector<MergeSyntheticSection *> MergeSections;
- for (InputSectionBase *&S : InputSections) {
- MergeInputSection *MS = dyn_cast<MergeInputSection>(S);
- if (!MS)
+ std::vector<MergeSyntheticSection *> mergeSections;
+ for (InputSectionBase *&s : inputSections) {
+ MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
+ if (!ms)
continue;
// We do not want to handle sections that are not alive, so just remove
// them instead of trying to merge.
- if (!MS->Live) {
- S = nullptr;
+ if (!ms->isLive()) {
+ s = nullptr;
continue;
}
- StringRef OutsecName = getOutputSectionName(MS);
- uint32_t Alignment = std::max<uint32_t>(MS->Alignment, MS->Entsize);
+ StringRef outsecName = getOutputSectionName(ms);
- auto I = llvm::find_if(MergeSections, [=](MergeSyntheticSection *Sec) {
+ auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) {
// While we could create a single synthetic section for two different
// values of Entsize, it is better to take Entsize into consideration.
//
@@ -3005,98 +3062,285 @@ void elf::mergeSections() {
//
// Using Entsize in here also allows us to propagate it to the synthetic
// section.
- return Sec->Name == OutsecName && Sec->Flags == MS->Flags &&
- Sec->Entsize == MS->Entsize && Sec->Alignment == Alignment;
+ //
+ // SHF_STRINGS section with different alignments should not be merged.
+ return sec->name == outsecName && sec->flags == ms->flags &&
+ sec->entsize == ms->entsize &&
+ (sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
});
- if (I == MergeSections.end()) {
- MergeSyntheticSection *Syn =
- createMergeSynthetic(OutsecName, MS->Type, MS->Flags, Alignment);
- MergeSections.push_back(Syn);
- I = std::prev(MergeSections.end());
- S = Syn;
- Syn->Entsize = MS->Entsize;
+ if (i == mergeSections.end()) {
+ MergeSyntheticSection *syn =
+ createMergeSynthetic(outsecName, ms->type, ms->flags, ms->alignment);
+ mergeSections.push_back(syn);
+ i = std::prev(mergeSections.end());
+ s = syn;
+ syn->entsize = ms->entsize;
} else {
- S = nullptr;
+ s = nullptr;
}
- (*I)->addSection(MS);
+ (*i)->addSection(ms);
}
- for (auto *MS : MergeSections)
- MS->finalizeContents();
+ for (auto *ms : mergeSections)
+ ms->finalizeContents();
- std::vector<InputSectionBase *> &V = InputSections;
- V.erase(std::remove(V.begin(), V.end(), nullptr), V.end());
+ std::vector<InputSectionBase *> &v = inputSections;
+ v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
}
MipsRldMapSection::MipsRldMapSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, Config->Wordsize,
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
".rld_map") {}
-ARMExidxSentinelSection::ARMExidxSentinelSection()
+ARMExidxSyntheticSection::ARMExidxSyntheticSection()
: SyntheticSection(SHF_ALLOC | SHF_LINK_ORDER, SHT_ARM_EXIDX,
- Config->Wordsize, ".ARM.exidx") {}
-
-// Write a terminating sentinel entry to the end of the .ARM.exidx table.
-// This section will have been sorted last in the .ARM.exidx table.
-// This table entry will have the form:
-// | PREL31 upper bound of code that has exception tables | EXIDX_CANTUNWIND |
-// The sentinel must have the PREL31 value of an address higher than any
-// address described by any other table entry.
-void ARMExidxSentinelSection::writeTo(uint8_t *Buf) {
- assert(Highest);
- uint64_t S = Highest->getVA(Highest->getSize());
- uint64_t P = getVA();
- Target->relocateOne(Buf, R_ARM_PREL31, S - P);
- write32le(Buf + 4, 1);
-}
-
-// The sentinel has to be removed if there are no other .ARM.exidx entries.
-bool ARMExidxSentinelSection::empty() const {
- for (InputSection *IS : getInputSections(getParent()))
- if (!isa<ARMExidxSentinelSection>(IS))
+ config->wordsize, ".ARM.exidx") {}
+
+static InputSection *findExidxSection(InputSection *isec) {
+ for (InputSection *d : isec->dependentSections)
+ if (d->type == SHT_ARM_EXIDX)
+ return d;
+ return nullptr;
+}
+
+bool ARMExidxSyntheticSection::addSection(InputSection *isec) {
+ if (isec->type == SHT_ARM_EXIDX) {
+ exidxSections.push_back(isec);
+ return true;
+ }
+
+ if ((isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) &&
+ isec->getSize() > 0) {
+ executableSections.push_back(isec);
+ if (empty && findExidxSection(isec))
+ empty = false;
+ return false;
+ }
+
+ // FIXME: we do not output a relocation section when --emit-relocs is used
+ // as we do not have relocation sections for linker generated table entries
+ // and we would have to erase at a late stage relocations from merged entries.
+ // Given that exception tables are already position independent and a binary
+ // analyzer could derive the relocations we choose to erase the relocations.
+ if (config->emitRelocs && isec->type == SHT_REL)
+ if (InputSectionBase *ex = isec->getRelocatedSection())
+ if (isa<InputSection>(ex) && ex->type == SHT_ARM_EXIDX)
+ return true;
+
+ return false;
+}
+
+// References to .ARM.Extab Sections have bit 31 clear and are not the
+// special EXIDX_CANTUNWIND bit-pattern.
+static bool isExtabRef(uint32_t unwind) {
+ return (unwind & 0x80000000) == 0 && unwind != 0x1;
+}
+
+// Return true if the .ARM.exidx section Cur can be merged into the .ARM.exidx
+// section Prev, where Cur follows Prev in the table. This can be done if the
+// unwinding instructions in Cur are identical to Prev. Linker generated
+// EXIDX_CANTUNWIND entries are represented by nullptr as they do not have an
+// InputSection.
+static bool isDuplicateArmExidxSec(InputSection *prev, InputSection *cur) {
+
+ struct ExidxEntry {
+ ulittle32_t fn;
+ ulittle32_t unwind;
+ };
+ // Get the last table Entry from the previous .ARM.exidx section. If Prev is
+ // nullptr then it will be a synthesized EXIDX_CANTUNWIND entry.
+ ExidxEntry prevEntry = {ulittle32_t(0), ulittle32_t(1)};
+ if (prev)
+ prevEntry = prev->getDataAs<ExidxEntry>().back();
+ if (isExtabRef(prevEntry.unwind))
+ return false;
+
+ // We consider the unwind instructions of an .ARM.exidx table entry
+ // a duplicate if the previous unwind instructions if:
+ // - Both are the special EXIDX_CANTUNWIND.
+ // - Both are the same inline unwind instructions.
+ // We do not attempt to follow and check links into .ARM.extab tables as
+ // consecutive identical entries are rare and the effort to check that they
+ // are identical is high.
+
+ // If Cur is nullptr then this is synthesized EXIDX_CANTUNWIND entry.
+ if (cur == nullptr)
+ return prevEntry.unwind == 1;
+
+ for (const ExidxEntry entry : cur->getDataAs<ExidxEntry>())
+ if (isExtabRef(entry.unwind) || entry.unwind != prevEntry.unwind)
return false;
+
+ // All table entries in this .ARM.exidx Section can be merged into the
+ // previous Section.
return true;
}
-bool ARMExidxSentinelSection::classof(const SectionBase *D) {
- return D->kind() == InputSectionBase::Synthetic && D->Type == SHT_ARM_EXIDX;
+// The .ARM.exidx table must be sorted in ascending order of the address of the
+// functions the table describes. Optionally duplicate adjacent table entries
+// can be removed. At the end of the function the ExecutableSections must be
+// sorted in ascending order of address, Sentinel is set to the InputSection
+// with the highest address and any InputSections that have mergeable
+// .ARM.exidx table entries are removed from it.
+void ARMExidxSyntheticSection::finalizeContents() {
+ // Sort the executable sections that may or may not have associated
+ // .ARM.exidx sections by order of ascending address. This requires the
+ // relative positions of InputSections to be known.
+ auto compareByFilePosition = [](const InputSection *a,
+ const InputSection *b) {
+ OutputSection *aOut = a->getParent();
+ OutputSection *bOut = b->getParent();
+
+ if (aOut != bOut)
+ return aOut->sectionIndex < bOut->sectionIndex;
+ return a->outSecOff < b->outSecOff;
+ };
+ llvm::stable_sort(executableSections, compareByFilePosition);
+ sentinel = executableSections.back();
+ // Optionally merge adjacent duplicate entries.
+ if (config->mergeArmExidx) {
+ std::vector<InputSection *> selectedSections;
+ selectedSections.reserve(executableSections.size());
+ selectedSections.push_back(executableSections[0]);
+ size_t prev = 0;
+ for (size_t i = 1; i < executableSections.size(); ++i) {
+ InputSection *ex1 = findExidxSection(executableSections[prev]);
+ InputSection *ex2 = findExidxSection(executableSections[i]);
+ if (!isDuplicateArmExidxSec(ex1, ex2)) {
+ selectedSections.push_back(executableSections[i]);
+ prev = i;
+ }
+ }
+ executableSections = std::move(selectedSections);
+ }
+
+ size_t offset = 0;
+ size = 0;
+ for (InputSection *isec : executableSections) {
+ if (InputSection *d = findExidxSection(isec)) {
+ d->outSecOff = offset;
+ d->parent = getParent();
+ offset += d->getSize();
+ } else {
+ offset += 8;
+ }
+ }
+ // Size includes Sentinel.
+ size = offset + 8;
+}
+
+InputSection *ARMExidxSyntheticSection::getLinkOrderDep() const {
+ return executableSections.front();
}
-ThunkSection::ThunkSection(OutputSection *OS, uint64_t Off)
+// To write the .ARM.exidx table from the ExecutableSections we have three cases
+// 1.) The InputSection has a .ARM.exidx InputSection in its dependent sections.
+// We write the .ARM.exidx section contents and apply its relocations.
+// 2.) The InputSection does not have a dependent .ARM.exidx InputSection. We
+// must write the contents of an EXIDX_CANTUNWIND directly. We use the
+// start of the InputSection as the purpose of the linker generated
+// section is to terminate the address range of the previous entry.
+// 3.) A trailing EXIDX_CANTUNWIND sentinel section is required at the end of
+// the table to terminate the address range of the final entry.
+void ARMExidxSyntheticSection::writeTo(uint8_t *buf) {
+
+ const uint8_t cantUnwindData[8] = {0, 0, 0, 0, // PREL31 to target
+ 1, 0, 0, 0}; // EXIDX_CANTUNWIND
+
+ uint64_t offset = 0;
+ for (InputSection *isec : executableSections) {
+ assert(isec->getParent() != nullptr);
+ if (InputSection *d = findExidxSection(isec)) {
+ memcpy(buf + offset, d->data().data(), d->data().size());
+ d->relocateAlloc(buf, buf + d->getSize());
+ offset += d->getSize();
+ } else {
+ // A Linker generated CANTUNWIND section.
+ memcpy(buf + offset, cantUnwindData, sizeof(cantUnwindData));
+ uint64_t s = isec->getVA();
+ uint64_t p = getVA() + offset;
+ target->relocateOne(buf + offset, R_ARM_PREL31, s - p);
+ offset += 8;
+ }
+ }
+ // Write Sentinel.
+ memcpy(buf + offset, cantUnwindData, sizeof(cantUnwindData));
+ uint64_t s = sentinel->getVA(sentinel->getSize());
+ uint64_t p = getVA() + offset;
+ target->relocateOne(buf + offset, R_ARM_PREL31, s - p);
+ assert(size == offset + 8);
+}
+
+bool ARMExidxSyntheticSection::classof(const SectionBase *d) {
+ return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX;
+}
+
+ThunkSection::ThunkSection(OutputSection *os, uint64_t off)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS,
- Config->Wordsize, ".text.thunk") {
- this->Parent = OS;
- this->OutSecOff = Off;
+ config->wordsize, ".text.thunk") {
+ this->parent = os;
+ this->outSecOff = off;
}
-void ThunkSection::addThunk(Thunk *T) {
- Thunks.push_back(T);
- T->addSymbols(*this);
+void ThunkSection::addThunk(Thunk *t) {
+ thunks.push_back(t);
+ t->addSymbols(*this);
}
-void ThunkSection::writeTo(uint8_t *Buf) {
- for (Thunk *T : Thunks)
- T->writeTo(Buf + T->Offset);
+void ThunkSection::writeTo(uint8_t *buf) {
+ for (Thunk *t : thunks)
+ t->writeTo(buf + t->offset);
}
InputSection *ThunkSection::getTargetInputSection() const {
- if (Thunks.empty())
+ if (thunks.empty())
return nullptr;
- const Thunk *T = Thunks.front();
- return T->getTargetInputSection();
+ const Thunk *t = thunks.front();
+ return t->getTargetInputSection();
}
bool ThunkSection::assignOffsets() {
- uint64_t Off = 0;
- for (Thunk *T : Thunks) {
- Off = alignTo(Off, T->Alignment);
- T->setOffset(Off);
- uint32_t Size = T->size();
- T->getThunkTargetSym()->Size = Size;
- Off += Size;
- }
- bool Changed = Off != Size;
- Size = Off;
- return Changed;
+ uint64_t off = 0;
+ for (Thunk *t : thunks) {
+ off = alignTo(off, t->alignment);
+ t->setOffset(off);
+ uint32_t size = t->size();
+ t->getThunkTargetSym()->size = size;
+ off += size;
+ }
+ bool changed = off != size;
+ size = off;
+ return changed;
+}
+
+PPC32Got2Section::PPC32Got2Section()
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 4, ".got2") {}
+
+bool PPC32Got2Section::isNeeded() const {
+ // See the comment below. This is not needed if there is no other
+ // InputSection.
+ for (BaseCommand *base : getParent()->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ for (InputSection *isec : isd->sections)
+ if (isec != this)
+ return true;
+ return false;
+}
+
+void PPC32Got2Section::finalizeContents() {
+ // PPC32 may create multiple GOT sections for -fPIC/-fPIE, one per file in
+ // .got2 . This function computes outSecOff of each .got2 to be used in
+ // PPC32PltCallStub::writeTo(). The purpose of this empty synthetic section is
+ // to collect input sections named ".got2".
+ uint32_t offset = 0;
+ for (BaseCommand *base : getParent()->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
+ for (InputSection *isec : isd->sections) {
+ if (isec == this)
+ continue;
+ isec->file->ppc32Got2OutSecOff = offset;
+ offset += (uint32_t)isec->getSize();
+ }
+ }
}
// If linking position-dependent code then the table will store the addresses
@@ -3105,48 +3349,188 @@ bool ThunkSection::assignOffsets() {
// allocated and filled in by the dynamic linker.
PPC64LongBranchTargetSection::PPC64LongBranchTargetSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE,
- Config->Pic ? SHT_NOBITS : SHT_PROGBITS, 8,
+ config->isPic ? SHT_NOBITS : SHT_PROGBITS, 8,
".branch_lt") {}
-void PPC64LongBranchTargetSection::addEntry(Symbol &Sym) {
- assert(Sym.PPC64BranchltIndex == 0xffff);
- Sym.PPC64BranchltIndex = Entries.size();
- Entries.push_back(&Sym);
+void PPC64LongBranchTargetSection::addEntry(Symbol &sym) {
+ assert(sym.ppc64BranchltIndex == 0xffff);
+ sym.ppc64BranchltIndex = entries.size();
+ entries.push_back(&sym);
}
size_t PPC64LongBranchTargetSection::getSize() const {
- return Entries.size() * 8;
+ return entries.size() * 8;
}
-void PPC64LongBranchTargetSection::writeTo(uint8_t *Buf) {
- assert(Target->GotPltEntrySize == 8);
+void PPC64LongBranchTargetSection::writeTo(uint8_t *buf) {
// If linking non-pic we have the final addresses of the targets and they get
// written to the table directly. For pic the dynamic linker will allocate
// the section and fill it it.
- if (Config->Pic)
+ if (config->isPic)
return;
- for (const Symbol *Sym : Entries) {
- assert(Sym->getVA());
+ for (const Symbol *sym : entries) {
+ assert(sym->getVA());
// Need calls to branch to the local entry-point since a long-branch
// must be a local-call.
- write64(Buf,
- Sym->getVA() + getPPC64GlobalEntryToLocalEntryOffset(Sym->StOther));
- Buf += Target->GotPltEntrySize;
+ write64(buf,
+ sym->getVA() + getPPC64GlobalEntryToLocalEntryOffset(sym->stOther));
+ buf += 8;
}
}
-bool PPC64LongBranchTargetSection::empty() const {
+bool PPC64LongBranchTargetSection::isNeeded() const {
// `removeUnusedSyntheticSections()` is called before thunk allocation which
// is too early to determine if this section will be empty or not. We need
// Finalized to keep the section alive until after thunk creation. Finalized
// only gets set to true once `finalizeSections()` is called after thunk
// creation. Becuase of this, if we don't create any long-branch thunks we end
// up with an empty .branch_lt section in the binary.
- return Finalized && Entries.empty();
+ return !finalized || !entries.empty();
}
-InStruct elf::In;
+RISCVSdataSection::RISCVSdataSection()
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 1, ".sdata") {}
+
+bool RISCVSdataSection::isNeeded() const {
+ if (!ElfSym::riscvGlobalPointer)
+ return false;
+
+ // __global_pointer$ is defined relative to .sdata . If the section does not
+ // exist, create a dummy one.
+ for (BaseCommand *base : getParent()->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ for (InputSection *isec : isd->sections)
+ if (isec != this)
+ return false;
+ return true;
+}
+
+static uint8_t getAbiVersion() {
+ // MIPS non-PIC executable gets ABI version 1.
+ if (config->emachine == EM_MIPS) {
+ if (!config->isPic && !config->relocatable &&
+ (config->eflags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC)
+ return 1;
+ return 0;
+ }
+
+ if (config->emachine == EM_AMDGPU) {
+ uint8_t ver = objectFiles[0]->abiVersion;
+ for (InputFile *file : makeArrayRef(objectFiles).slice(1))
+ if (file->abiVersion != ver)
+ error("incompatible ABI version: " + toString(file));
+ return ver;
+ }
+
+ return 0;
+}
+
+template <typename ELFT> void elf::writeEhdr(uint8_t *buf, Partition &part) {
+ // For executable segments, the trap instructions are written before writing
+ // the header. Setting Elf header bytes to zero ensures that any unused bytes
+ // in header are zero-cleared, instead of having trap instructions.
+ memset(buf, 0, sizeof(typename ELFT::Ehdr));
+ memcpy(buf, "\177ELF", 4);
+
+ auto *eHdr = reinterpret_cast<typename ELFT::Ehdr *>(buf);
+ eHdr->e_ident[EI_CLASS] = config->is64 ? ELFCLASS64 : ELFCLASS32;
+ eHdr->e_ident[EI_DATA] = config->isLE ? ELFDATA2LSB : ELFDATA2MSB;
+ eHdr->e_ident[EI_VERSION] = EV_CURRENT;
+ eHdr->e_ident[EI_OSABI] = config->osabi;
+ eHdr->e_ident[EI_ABIVERSION] = getAbiVersion();
+ eHdr->e_machine = config->emachine;
+ eHdr->e_version = EV_CURRENT;
+ eHdr->e_flags = config->eflags;
+ eHdr->e_ehsize = sizeof(typename ELFT::Ehdr);
+ eHdr->e_phnum = part.phdrs.size();
+ eHdr->e_shentsize = sizeof(typename ELFT::Shdr);
+
+ if (!config->relocatable) {
+ eHdr->e_phoff = sizeof(typename ELFT::Ehdr);
+ eHdr->e_phentsize = sizeof(typename ELFT::Phdr);
+ }
+}
+
+template <typename ELFT> void elf::writePhdrs(uint8_t *buf, Partition &part) {
+ // Write the program header table.
+ auto *hBuf = reinterpret_cast<typename ELFT::Phdr *>(buf);
+ for (PhdrEntry *p : part.phdrs) {
+ hBuf->p_type = p->p_type;
+ hBuf->p_flags = p->p_flags;
+ hBuf->p_offset = p->p_offset;
+ hBuf->p_vaddr = p->p_vaddr;
+ hBuf->p_paddr = p->p_paddr;
+ hBuf->p_filesz = p->p_filesz;
+ hBuf->p_memsz = p->p_memsz;
+ hBuf->p_align = p->p_align;
+ ++hBuf;
+ }
+}
+
+template <typename ELFT>
+PartitionElfHeaderSection<ELFT>::PartitionElfHeaderSection()
+ : SyntheticSection(SHF_ALLOC, SHT_LLVM_PART_EHDR, 1, "") {}
+
+template <typename ELFT>
+size_t PartitionElfHeaderSection<ELFT>::getSize() const {
+ return sizeof(typename ELFT::Ehdr);
+}
+
+template <typename ELFT>
+void PartitionElfHeaderSection<ELFT>::writeTo(uint8_t *buf) {
+ writeEhdr<ELFT>(buf, getPartition());
+
+ // Loadable partitions are always ET_DYN.
+ auto *eHdr = reinterpret_cast<typename ELFT::Ehdr *>(buf);
+ eHdr->e_type = ET_DYN;
+}
+
+template <typename ELFT>
+PartitionProgramHeadersSection<ELFT>::PartitionProgramHeadersSection()
+ : SyntheticSection(SHF_ALLOC, SHT_LLVM_PART_PHDR, 1, ".phdrs") {}
+
+template <typename ELFT>
+size_t PartitionProgramHeadersSection<ELFT>::getSize() const {
+ return sizeof(typename ELFT::Phdr) * getPartition().phdrs.size();
+}
+
+template <typename ELFT>
+void PartitionProgramHeadersSection<ELFT>::writeTo(uint8_t *buf) {
+ writePhdrs<ELFT>(buf, getPartition());
+}
+
+PartitionIndexSection::PartitionIndexSection()
+ : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".rodata") {}
+
+size_t PartitionIndexSection::getSize() const {
+ return 12 * (partitions.size() - 1);
+}
+
+void PartitionIndexSection::finalizeContents() {
+ for (size_t i = 1; i != partitions.size(); ++i)
+ partitions[i].nameStrTab = mainPart->dynStrTab->addString(partitions[i].name);
+}
+
+void PartitionIndexSection::writeTo(uint8_t *buf) {
+ uint64_t va = getVA();
+ for (size_t i = 1; i != partitions.size(); ++i) {
+ write32(buf, mainPart->dynStrTab->getVA() + partitions[i].nameStrTab - va);
+ write32(buf + 4, partitions[i].elfHeader->getVA() - (va + 4));
+
+ SyntheticSection *next =
+ i == partitions.size() - 1 ? in.partEnd : partitions[i + 1].elfHeader;
+ write32(buf + 8, next->getVA() - partitions[i].elfHeader->getVA());
+
+ va += 12;
+ buf += 12;
+ }
+}
+
+InStruct elf::in;
+
+std::vector<Partition> elf::partitions;
+Partition *elf::mainPart;
template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
@@ -3168,11 +3552,6 @@ template void PltSection::addEntry<ELF32BE>(Symbol &Sym);
template void PltSection::addEntry<ELF64LE>(Symbol &Sym);
template void PltSection::addEntry<ELF64BE>(Symbol &Sym);
-template void MipsGotSection::build<ELF32LE>();
-template void MipsGotSection::build<ELF32BE>();
-template void MipsGotSection::build<ELF64LE>();
-template void MipsGotSection::build<ELF64BE>();
-
template class elf::MipsAbiFlagsSection<ELF32LE>;
template class elf::MipsAbiFlagsSection<ELF32BE>;
template class elf::MipsAbiFlagsSection<ELF64LE>;
@@ -3213,12 +3592,27 @@ template class elf::SymbolTableSection<ELF32BE>;
template class elf::SymbolTableSection<ELF64LE>;
template class elf::SymbolTableSection<ELF64BE>;
-template class elf::VersionTableSection<ELF32LE>;
-template class elf::VersionTableSection<ELF32BE>;
-template class elf::VersionTableSection<ELF64LE>;
-template class elf::VersionTableSection<ELF64BE>;
-
template class elf::VersionNeedSection<ELF32LE>;
template class elf::VersionNeedSection<ELF32BE>;
template class elf::VersionNeedSection<ELF64LE>;
template class elf::VersionNeedSection<ELF64BE>;
+
+template void elf::writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part);
+template void elf::writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part);
+template void elf::writeEhdr<ELF64LE>(uint8_t *Buf, Partition &Part);
+template void elf::writeEhdr<ELF64BE>(uint8_t *Buf, Partition &Part);
+
+template void elf::writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part);
+template void elf::writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
+template void elf::writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
+template void elf::writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
+
+template class elf::PartitionElfHeaderSection<ELF32LE>;
+template class elf::PartitionElfHeaderSection<ELF32BE>;
+template class elf::PartitionElfHeaderSection<ELF64LE>;
+template class elf::PartitionElfHeaderSection<ELF64BE>;
+
+template class elf::PartitionProgramHeadersSection<ELF32LE>;
+template class elf::PartitionProgramHeadersSection<ELF32BE>;
+template class elf::PartitionProgramHeadersSection<ELF64LE>;
+template class elf::PartitionProgramHeadersSection<ELF64BE>;
diff --git a/ELF/SyntheticSections.h b/ELF/SyntheticSections.h
index 6fc40d355d5e2..1c4dd06e02776 100644
--- a/ELF/SyntheticSections.h
+++ b/ELF/SyntheticSections.h
@@ -1,9 +1,8 @@
//===- SyntheticSection.h ---------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -18,8 +17,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLD_ELF_SYNTHETIC_SECTION_H
-#define LLD_ELF_SYNTHETIC_SECTION_H
+#ifndef LLD_ELF_SYNTHETIC_SECTIONS_H
+#define LLD_ELF_SYNTHETIC_SECTIONS_H
#include "DWARF.h"
#include "EhFrame.h"
@@ -32,107 +31,113 @@
namespace lld {
namespace elf {
class Defined;
-class SharedSymbol;
+struct PhdrEntry;
+class SymbolTableBaseSection;
+class VersionNeedBaseSection;
class SyntheticSection : public InputSection {
public:
- SyntheticSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
- StringRef Name)
- : InputSection(nullptr, Flags, Type, Alignment, {}, Name,
+ SyntheticSection(uint64_t flags, uint32_t type, uint32_t alignment,
+ StringRef name)
+ : InputSection(nullptr, flags, type, alignment, {}, name,
InputSectionBase::Synthetic) {
- this->Live = true;
+ markLive();
}
virtual ~SyntheticSection() = default;
- virtual void writeTo(uint8_t *Buf) = 0;
+ virtual void writeTo(uint8_t *buf) = 0;
virtual size_t getSize() const = 0;
virtual void finalizeContents() {}
// If the section has the SHF_ALLOC flag and the size may be changed if
// thunks are added, update the section size.
virtual bool updateAllocSize() { return false; }
- virtual bool empty() const { return false; }
+ virtual bool isNeeded() const { return true; }
- static bool classof(const SectionBase *D) {
- return D->kind() == InputSectionBase::Synthetic;
+ static bool classof(const SectionBase *d) {
+ return d->kind() == InputSectionBase::Synthetic;
}
};
struct CieRecord {
- EhSectionPiece *Cie = nullptr;
- std::vector<EhSectionPiece *> Fdes;
+ EhSectionPiece *cie = nullptr;
+ std::vector<EhSectionPiece *> fdes;
};
// Section for .eh_frame.
class EhFrameSection final : public SyntheticSection {
public:
EhFrameSection();
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
void finalizeContents() override;
- bool empty() const override { return Sections.empty(); }
- size_t getSize() const override { return Size; }
+ bool isNeeded() const override { return !sections.empty(); }
+ size_t getSize() const override { return size; }
+
+ static bool classof(const SectionBase *d) {
+ return SyntheticSection::classof(d) && d->name == ".eh_frame";
+ }
- template <class ELFT> void addSection(InputSectionBase *S);
+ template <class ELFT> void addSection(InputSectionBase *s);
- std::vector<EhInputSection *> Sections;
- size_t NumFdes = 0;
+ std::vector<EhInputSection *> sections;
+ size_t numFdes = 0;
struct FdeData {
- uint32_t PcRel;
- uint32_t FdeVARel;
+ uint32_t pcRel;
+ uint32_t fdeVARel;
};
std::vector<FdeData> getFdeData() const;
- ArrayRef<CieRecord *> getCieRecords() const { return CieRecords; }
+ ArrayRef<CieRecord *> getCieRecords() const { return cieRecords; }
private:
// This is used only when parsing EhInputSection. We keep it here to avoid
// allocating one for each EhInputSection.
- llvm::DenseMap<size_t, CieRecord *> OffsetToCie;
+ llvm::DenseMap<size_t, CieRecord *> offsetToCie;
- uint64_t Size = 0;
+ uint64_t size = 0;
template <class ELFT, class RelTy>
- void addSectionAux(EhInputSection *S, llvm::ArrayRef<RelTy> Rels);
+ void addSectionAux(EhInputSection *s, llvm::ArrayRef<RelTy> rels);
template <class ELFT, class RelTy>
- CieRecord *addCie(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
+ CieRecord *addCie(EhSectionPiece &piece, ArrayRef<RelTy> rels);
template <class ELFT, class RelTy>
- bool isFdeLive(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
+ bool isFdeLive(EhSectionPiece &piece, ArrayRef<RelTy> rels);
- uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc) const;
+ uint64_t getFdePc(uint8_t *buf, size_t off, uint8_t enc) const;
- std::vector<CieRecord *> CieRecords;
+ std::vector<CieRecord *> cieRecords;
// CIE records are uniquified by their contents and personality functions.
- llvm::DenseMap<std::pair<ArrayRef<uint8_t>, Symbol *>, CieRecord *> CieMap;
+ llvm::DenseMap<std::pair<ArrayRef<uint8_t>, Symbol *>, CieRecord *> cieMap;
};
class GotSection : public SyntheticSection {
public:
GotSection();
- size_t getSize() const override { return Size; }
+ size_t getSize() const override { return size; }
void finalizeContents() override;
- bool empty() const override;
- void writeTo(uint8_t *Buf) override;
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) override;
- void addEntry(Symbol &Sym);
- bool addDynTlsEntry(Symbol &Sym);
+ void addEntry(Symbol &sym);
+ bool addDynTlsEntry(Symbol &sym);
bool addTlsIndex();
- uint64_t getGlobalDynAddr(const Symbol &B) const;
- uint64_t getGlobalDynOffset(const Symbol &B) const;
+ uint64_t getGlobalDynAddr(const Symbol &b) const;
+ uint64_t getGlobalDynOffset(const Symbol &b) const;
- uint64_t getTlsIndexVA() { return this->getVA() + TlsIndexOff; }
- uint32_t getTlsIndexOff() const { return TlsIndexOff; }
+ uint64_t getTlsIndexVA() { return this->getVA() + tlsIndexOff; }
+ uint32_t getTlsIndexOff() const { return tlsIndexOff; }
// Flag to force GOT to be in output if we have relocations
// that relies on its address.
- bool HasGotOffRel = false;
+ bool hasGotOffRel = false;
protected:
- size_t NumEntries = 0;
- uint32_t TlsIndexOff = -1;
- uint64_t Size = 0;
+ size_t numEntries = 0;
+ uint32_t tlsIndexOff = -1;
+ uint64_t size = 0;
};
// .note.GNU-stack section.
@@ -140,27 +145,31 @@ class GnuStackSection : public SyntheticSection {
public:
GnuStackSection()
: SyntheticSection(0, llvm::ELF::SHT_PROGBITS, 1, ".note.GNU-stack") {}
- void writeTo(uint8_t *Buf) override {}
+ void writeTo(uint8_t *buf) override {}
size_t getSize() const override { return 0; }
};
+class GnuPropertySection : public SyntheticSection {
+public:
+ GnuPropertySection();
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override;
+};
+
// .note.gnu.build-id section.
class BuildIdSection : public SyntheticSection {
// First 16 bytes are a header.
- static const unsigned HeaderSize = 16;
+ static const unsigned headerSize = 16;
public:
+ const size_t hashSize;
BuildIdSection();
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return HeaderSize + HashSize; }
- void writeBuildId(llvm::ArrayRef<uint8_t> Buf);
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return headerSize + hashSize; }
+ void writeBuildId(llvm::ArrayRef<uint8_t> buf);
private:
- void computeHash(llvm::ArrayRef<uint8_t> Buf,
- std::function<void(uint8_t *, ArrayRef<uint8_t>)> Hash);
-
- size_t HashSize;
- uint8_t *HashBuf;
+ uint8_t *hashBuf;
};
// BssSection is used to reserve space for copy relocations and common symbols.
@@ -169,40 +178,40 @@ private:
// respectively.
class BssSection final : public SyntheticSection {
public:
- BssSection(StringRef Name, uint64_t Size, uint32_t Alignment);
+ BssSection(StringRef name, uint64_t size, uint32_t alignment);
void writeTo(uint8_t *) override {
llvm_unreachable("unexpected writeTo() call for SHT_NOBITS section");
}
- bool empty() const override { return getSize() == 0; }
- size_t getSize() const override { return Size; }
+ bool isNeeded() const override { return size != 0; }
+ size_t getSize() const override { return size; }
- static bool classof(const SectionBase *S) { return S->Bss; }
- uint64_t Size;
+ static bool classof(const SectionBase *s) { return s->bss; }
+ uint64_t size;
};
class MipsGotSection final : public SyntheticSection {
public:
MipsGotSection();
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
bool updateAllocSize() override;
void finalizeContents() override;
- bool empty() const override;
+ bool isNeeded() const override;
// Join separate GOTs built for each input file to generate
// primary and optional multiple secondary GOTs.
- template <class ELFT> void build();
+ void build();
- void addEntry(InputFile &File, Symbol &Sym, int64_t Addend, RelExpr Expr);
- void addDynTlsEntry(InputFile &File, Symbol &Sym);
- void addTlsIndex(InputFile &File);
+ void addEntry(InputFile &file, Symbol &sym, int64_t addend, RelExpr expr);
+ void addDynTlsEntry(InputFile &file, Symbol &sym);
+ void addTlsIndex(InputFile &file);
- uint64_t getPageEntryOffset(const InputFile *F, const Symbol &S,
- int64_t Addend) const;
- uint64_t getSymEntryOffset(const InputFile *F, const Symbol &S,
- int64_t Addend) const;
- uint64_t getGlobalDynOffset(const InputFile *F, const Symbol &S) const;
- uint64_t getTlsIndexOffset(const InputFile *F) const;
+ uint64_t getPageEntryOffset(const InputFile *f, const Symbol &s,
+ int64_t addend) const;
+ uint64_t getSymEntryOffset(const InputFile *f, const Symbol &s,
+ int64_t addend) const;
+ uint64_t getGlobalDynOffset(const InputFile *f, const Symbol &s) const;
+ uint64_t getTlsIndexOffset(const InputFile *f) const;
// Returns the symbol which corresponds to the first entry of the global part
// of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
@@ -215,7 +224,7 @@ public:
unsigned getLocalEntriesNum() const;
// Return _gp value for primary GOT (nullptr) or particular input file.
- uint64_t getGp(const InputFile *F = nullptr) const;
+ uint64_t getGp(const InputFile *f = nullptr) const;
private:
// MIPS GOT consists of three parts: local, global and tls. Each part
@@ -305,34 +314,35 @@ private:
// https://dmz-portal.mips.com/wiki/MIPS_Multi_GOT
// Number of "Header" entries.
- static const unsigned HeaderEntriesNum = 2;
+ static const unsigned headerEntriesNum = 2;
- uint64_t Size = 0;
+ uint64_t size = 0;
// Symbol and addend.
- typedef std::pair<Symbol *, int64_t> GotEntry;
+ using GotEntry = std::pair<Symbol *, int64_t>;
struct FileGot {
- InputFile *File = nullptr;
- size_t StartIndex = 0;
+ InputFile *file = nullptr;
+ size_t startIndex = 0;
struct PageBlock {
- size_t FirstIndex = 0;
- size_t Count = 0;
+ size_t firstIndex;
+ size_t count;
+ PageBlock() : firstIndex(0), count(0) {}
};
// Map output sections referenced by MIPS GOT relocations
// to the description (index/count) "page" entries allocated
// for this section.
- llvm::SmallMapVector<const OutputSection *, PageBlock, 16> PagesMap;
+ llvm::SmallMapVector<const OutputSection *, PageBlock, 16> pagesMap;
// Maps from Symbol+Addend pair or just Symbol to the GOT entry index.
- llvm::MapVector<GotEntry, size_t> Local16;
- llvm::MapVector<GotEntry, size_t> Local32;
- llvm::MapVector<Symbol *, size_t> Global;
- llvm::MapVector<Symbol *, size_t> Relocs;
- llvm::MapVector<Symbol *, size_t> Tls;
+ llvm::MapVector<GotEntry, size_t> local16;
+ llvm::MapVector<GotEntry, size_t> local32;
+ llvm::MapVector<Symbol *, size_t> global;
+ llvm::MapVector<Symbol *, size_t> relocs;
+ llvm::MapVector<Symbol *, size_t> tls;
// Set of symbols referenced by dynamic TLS relocations.
- llvm::MapVector<Symbol *, size_t> DynTlsSymbols;
+ llvm::MapVector<Symbol *, size_t> dynTlsSymbols;
// Total number of all entries.
size_t getEntriesNum() const;
@@ -345,27 +355,31 @@ private:
// Container of GOT created for each input file.
// After building a final series of GOTs this container
// holds primary and secondary GOT's.
- std::vector<FileGot> Gots;
+ std::vector<FileGot> gots;
// Return (and create if necessary) `FileGot`.
- FileGot &getGot(InputFile &F);
+ FileGot &getGot(InputFile &f);
// Try to merge two GOTs. In case of success the `Dst` contains
// result of merging and the function returns true. In case of
// ovwerflow the `Dst` is unchanged and the function returns false.
- bool tryMergeGots(FileGot & Dst, FileGot & Src, bool IsPrimary);
+ bool tryMergeGots(FileGot & dst, FileGot & src, bool isPrimary);
};
class GotPltSection final : public SyntheticSection {
public:
GotPltSection();
- void addEntry(Symbol &Sym);
+ void addEntry(Symbol &sym);
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
- bool empty() const override;
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override;
+
+ // Flag to force GotPlt to be in output if we have relocations
+ // that relies on its address.
+ bool hasGotPltOffRel = false;
private:
- std::vector<const Symbol *> Entries;
+ std::vector<const Symbol *> entries;
};
// The IgotPltSection is a Got associated with the PltSection for GNU Ifunc
@@ -375,167 +389,164 @@ private:
class IgotPltSection final : public SyntheticSection {
public:
IgotPltSection();
- void addEntry(Symbol &Sym);
+ void addEntry(Symbol &sym);
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
- bool empty() const override { return Entries.empty(); }
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override { return !entries.empty(); }
private:
- std::vector<const Symbol *> Entries;
+ std::vector<const Symbol *> entries;
};
class StringTableSection final : public SyntheticSection {
public:
- StringTableSection(StringRef Name, bool Dynamic);
- unsigned addString(StringRef S, bool HashIt = true);
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
- bool isDynamic() const { return Dynamic; }
+ StringTableSection(StringRef name, bool dynamic);
+ unsigned addString(StringRef s, bool hashIt = true);
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
+ bool isDynamic() const { return dynamic; }
private:
- const bool Dynamic;
+ const bool dynamic;
- uint64_t Size = 0;
+ uint64_t size = 0;
- llvm::DenseMap<StringRef, unsigned> StringMap;
- std::vector<StringRef> Strings;
+ llvm::DenseMap<StringRef, unsigned> stringMap;
+ std::vector<StringRef> strings;
};
class DynamicReloc {
public:
- DynamicReloc(RelType Type, const InputSectionBase *InputSec,
- uint64_t OffsetInSec, bool UseSymVA, Symbol *Sym, int64_t Addend)
- : Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
- UseSymVA(UseSymVA), Addend(Addend), OutputSec(nullptr) {}
+ DynamicReloc(RelType type, const InputSectionBase *inputSec,
+ uint64_t offsetInSec, bool useSymVA, Symbol *sym, int64_t addend)
+ : type(type), sym(sym), inputSec(inputSec), offsetInSec(offsetInSec),
+ useSymVA(useSymVA), addend(addend), outputSec(nullptr) {}
// This constructor records dynamic relocation settings used by MIPS
// multi-GOT implementation. It's to relocate addresses of 64kb pages
// lie inside the output section.
- DynamicReloc(RelType Type, const InputSectionBase *InputSec,
- uint64_t OffsetInSec, const OutputSection *OutputSec,
- int64_t Addend)
- : Type(Type), Sym(nullptr), InputSec(InputSec), OffsetInSec(OffsetInSec),
- UseSymVA(false), Addend(Addend), OutputSec(OutputSec) {}
+ DynamicReloc(RelType type, const InputSectionBase *inputSec,
+ uint64_t offsetInSec, const OutputSection *outputSec,
+ int64_t addend)
+ : type(type), sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec),
+ useSymVA(false), addend(addend), outputSec(outputSec) {}
uint64_t getOffset() const;
- uint32_t getSymIndex() const;
- const InputSectionBase *getInputSec() const { return InputSec; }
+ uint32_t getSymIndex(SymbolTableBaseSection *symTab) const;
// Computes the addend of the dynamic relocation. Note that this is not the
- // same as the Addend member variable as it also includes the symbol address
- // if UseSymVA is true.
+ // same as the addend member variable as it also includes the symbol address
+ // if useSymVA is true.
int64_t computeAddend() const;
- RelType Type;
+ RelType type;
-private:
- Symbol *Sym;
- const InputSectionBase *InputSec = nullptr;
- uint64_t OffsetInSec;
+ Symbol *sym;
+ const InputSectionBase *inputSec = nullptr;
+ uint64_t offsetInSec;
// If this member is true, the dynamic relocation will not be against the
// symbol but will instead be a relative relocation that simply adds the
// load address. This means we need to write the symbol virtual address
// plus the original addend as the final relocation addend.
- bool UseSymVA;
- int64_t Addend;
- const OutputSection *OutputSec;
+ bool useSymVA;
+ int64_t addend;
+ const OutputSection *outputSec;
};
template <class ELFT> class DynamicSection final : public SyntheticSection {
- typedef typename ELFT::Dyn Elf_Dyn;
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
- typedef typename ELFT::Relr Elf_Relr;
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
+ using Elf_Dyn = typename ELFT::Dyn;
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
+ using Elf_Relr = typename ELFT::Relr;
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Sym = typename ELFT::Sym;
// finalizeContents() fills this vector with the section contents.
- std::vector<std::pair<int32_t, std::function<uint64_t()>>> Entries;
+ std::vector<std::pair<int32_t, std::function<uint64_t()>>> entries;
public:
DynamicSection();
void finalizeContents() override;
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
private:
- void add(int32_t Tag, std::function<uint64_t()> Fn);
- void addInt(int32_t Tag, uint64_t Val);
- void addInSec(int32_t Tag, InputSection *Sec);
- void addInSecRelative(int32_t Tag, InputSection *Sec);
- void addOutSec(int32_t Tag, OutputSection *Sec);
- void addSize(int32_t Tag, OutputSection *Sec);
- void addSym(int32_t Tag, Symbol *Sym);
-
- uint64_t Size = 0;
+ void add(int32_t tag, std::function<uint64_t()> fn);
+ void addInt(int32_t tag, uint64_t val);
+ void addInSec(int32_t tag, InputSection *sec);
+ void addInSecRelative(int32_t tag, InputSection *sec);
+ void addOutSec(int32_t tag, OutputSection *sec);
+ void addSize(int32_t tag, OutputSection *sec);
+ void addSym(int32_t tag, Symbol *sym);
+
+ uint64_t size = 0;
};
class RelocationBaseSection : public SyntheticSection {
public:
- RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag,
- int32_t SizeDynamicTag);
- void addReloc(RelType DynType, InputSectionBase *IS, uint64_t OffsetInSec,
- Symbol *Sym);
+ RelocationBaseSection(StringRef name, uint32_t type, int32_t dynamicTag,
+ int32_t sizeDynamicTag);
+ void addReloc(RelType dynType, InputSectionBase *isec, uint64_t offsetInSec,
+ Symbol *sym);
// Add a dynamic relocation that might need an addend. This takes care of
// writing the addend to the output section if needed.
- void addReloc(RelType DynType, InputSectionBase *InputSec,
- uint64_t OffsetInSec, Symbol *Sym, int64_t Addend, RelExpr Expr,
- RelType Type);
- void addReloc(const DynamicReloc &Reloc);
- bool empty() const override { return Relocs.empty(); }
- size_t getSize() const override { return Relocs.size() * this->Entsize; }
- size_t getRelativeRelocCount() const { return NumRelativeRelocs; }
+ void addReloc(RelType dynType, InputSectionBase *inputSec,
+ uint64_t offsetInSec, Symbol *sym, int64_t addend, RelExpr expr,
+ RelType type);
+ void addReloc(const DynamicReloc &reloc);
+ bool isNeeded() const override { return !relocs.empty(); }
+ size_t getSize() const override { return relocs.size() * this->entsize; }
+ size_t getRelativeRelocCount() const { return numRelativeRelocs; }
void finalizeContents() override;
- int32_t DynamicTag, SizeDynamicTag;
+ int32_t dynamicTag, sizeDynamicTag;
+ std::vector<DynamicReloc> relocs;
protected:
- std::vector<DynamicReloc> Relocs;
- size_t NumRelativeRelocs = 0;
+ size_t numRelativeRelocs = 0;
};
template <class ELFT>
class RelocationSection final : public RelocationBaseSection {
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
public:
- RelocationSection(StringRef Name, bool Sort);
- unsigned getRelocOffset();
- void writeTo(uint8_t *Buf) override;
+ RelocationSection(StringRef name, bool sort);
+ void writeTo(uint8_t *buf) override;
private:
- bool Sort;
+ bool sort;
};
template <class ELFT>
class AndroidPackedRelocationSection final : public RelocationBaseSection {
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
public:
- AndroidPackedRelocationSection(StringRef Name);
+ AndroidPackedRelocationSection(StringRef name);
bool updateAllocSize() override;
- size_t getSize() const override { return RelocData.size(); }
- void writeTo(uint8_t *Buf) override {
- memcpy(Buf, RelocData.data(), RelocData.size());
+ size_t getSize() const override { return relocData.size(); }
+ void writeTo(uint8_t *buf) override {
+ memcpy(buf, relocData.data(), relocData.size());
}
private:
- SmallVector<char, 0> RelocData;
+ SmallVector<char, 0> relocData;
};
struct RelativeReloc {
- uint64_t getOffset() const { return InputSec->getVA(OffsetInSec); }
+ uint64_t getOffset() const { return inputSec->getVA(offsetInSec); }
- const InputSectionBase *InputSec;
- uint64_t OffsetInSec;
+ const InputSectionBase *inputSec;
+ uint64_t offsetInSec;
};
class RelrBaseSection : public SyntheticSection {
public:
RelrBaseSection();
- bool empty() const override { return Relocs.empty(); }
- std::vector<RelativeReloc> Relocs;
+ bool isNeeded() const override { return !relocs.empty(); }
+ std::vector<RelativeReloc> relocs;
};
// RelrSection is used to encode offsets for relative relocations.
@@ -543,65 +554,65 @@ public:
// https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
// For more details, see the comment in RelrSection::updateAllocSize().
template <class ELFT> class RelrSection final : public RelrBaseSection {
- typedef typename ELFT::Relr Elf_Relr;
+ using Elf_Relr = typename ELFT::Relr;
public:
RelrSection();
bool updateAllocSize() override;
- size_t getSize() const override { return RelrRelocs.size() * this->Entsize; }
- void writeTo(uint8_t *Buf) override {
- memcpy(Buf, RelrRelocs.data(), getSize());
+ size_t getSize() const override { return relrRelocs.size() * this->entsize; }
+ void writeTo(uint8_t *buf) override {
+ memcpy(buf, relrRelocs.data(), getSize());
}
private:
- std::vector<Elf_Relr> RelrRelocs;
+ std::vector<Elf_Relr> relrRelocs;
};
struct SymbolTableEntry {
- Symbol *Sym;
- size_t StrTabOffset;
+ Symbol *sym;
+ size_t strTabOffset;
};
class SymbolTableBaseSection : public SyntheticSection {
public:
- SymbolTableBaseSection(StringTableSection &StrTabSec);
+ SymbolTableBaseSection(StringTableSection &strTabSec);
void finalizeContents() override;
- size_t getSize() const override { return getNumSymbols() * Entsize; }
- void addSymbol(Symbol *Sym);
- unsigned getNumSymbols() const { return Symbols.size() + 1; }
- size_t getSymbolIndex(Symbol *Sym);
- ArrayRef<SymbolTableEntry> getSymbols() const { return Symbols; }
+ size_t getSize() const override { return getNumSymbols() * entsize; }
+ void addSymbol(Symbol *sym);
+ unsigned getNumSymbols() const { return symbols.size() + 1; }
+ size_t getSymbolIndex(Symbol *sym);
+ ArrayRef<SymbolTableEntry> getSymbols() const { return symbols; }
protected:
void sortSymTabSymbols();
// A vector of symbols and their string table offsets.
- std::vector<SymbolTableEntry> Symbols;
+ std::vector<SymbolTableEntry> symbols;
- StringTableSection &StrTabSec;
+ StringTableSection &strTabSec;
- llvm::once_flag OnceFlag;
- llvm::DenseMap<Symbol *, size_t> SymbolIndexMap;
- llvm::DenseMap<OutputSection *, size_t> SectionIndexMap;
+ llvm::once_flag onceFlag;
+ llvm::DenseMap<Symbol *, size_t> symbolIndexMap;
+ llvm::DenseMap<OutputSection *, size_t> sectionIndexMap;
};
template <class ELFT>
class SymbolTableSection final : public SymbolTableBaseSection {
- typedef typename ELFT::Sym Elf_Sym;
+ using Elf_Sym = typename ELFT::Sym;
public:
- SymbolTableSection(StringTableSection &StrTabSec);
- void writeTo(uint8_t *Buf) override;
+ SymbolTableSection(StringTableSection &strTabSec);
+ void writeTo(uint8_t *buf) override;
};
class SymtabShndxSection final : public SyntheticSection {
public:
SymtabShndxSection();
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- bool empty() const override;
+ bool isNeeded() const override;
void finalizeContents() override;
};
@@ -611,42 +622,42 @@ class GnuHashTableSection final : public SyntheticSection {
public:
GnuHashTableSection();
void finalizeContents() override;
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
// Adds symbols to the hash table.
// Sorts the input to satisfy GNU hash section requirements.
- void addSymbols(std::vector<SymbolTableEntry> &Symbols);
+ void addSymbols(std::vector<SymbolTableEntry> &symbols);
private:
// See the comment in writeBloomFilter.
enum { Shift2 = 26 };
- void writeBloomFilter(uint8_t *Buf);
- void writeHashTable(uint8_t *Buf);
+ void writeBloomFilter(uint8_t *buf);
+ void writeHashTable(uint8_t *buf);
struct Entry {
- Symbol *Sym;
- size_t StrTabOffset;
- uint32_t Hash;
- uint32_t BucketIdx;
+ Symbol *sym;
+ size_t strTabOffset;
+ uint32_t hash;
+ uint32_t bucketIdx;
};
- std::vector<Entry> Symbols;
- size_t MaskWords;
- size_t NBuckets = 0;
- size_t Size = 0;
+ std::vector<Entry> symbols;
+ size_t maskWords;
+ size_t nBuckets = 0;
+ size_t size = 0;
};
class HashTableSection final : public SyntheticSection {
public:
HashTableSection();
void finalizeContents() override;
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
private:
- size_t Size = 0;
+ size_t size = 0;
};
// The PltSection is used for both the Plt and Iplt. The former usually has a
@@ -655,67 +666,66 @@ private:
// Target->IRelativeRel.
class PltSection : public SyntheticSection {
public:
- PltSection(bool IsIplt);
- void writeTo(uint8_t *Buf) override;
+ PltSection(bool isIplt);
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- bool empty() const override { return Entries.empty(); }
+ bool isNeeded() const override { return !entries.empty(); }
void addSymbols();
- template <class ELFT> void addEntry(Symbol &Sym);
+ template <class ELFT> void addEntry(Symbol &sym);
- size_t HeaderSize;
+ size_t headerSize;
private:
- unsigned getPltRelocOff() const;
- std::vector<std::pair<const Symbol *, unsigned>> Entries;
- bool IsIplt;
+ std::vector<const Symbol *> entries;
+ bool isIplt;
};
class GdbIndexSection final : public SyntheticSection {
public:
struct AddressEntry {
- InputSection *Section;
- uint64_t LowAddress;
- uint64_t HighAddress;
- uint32_t CuIndex;
+ InputSection *section;
+ uint64_t lowAddress;
+ uint64_t highAddress;
+ uint32_t cuIndex;
};
struct CuEntry {
- uint64_t CuOffset;
- uint64_t CuLength;
+ uint64_t cuOffset;
+ uint64_t cuLength;
};
struct NameAttrEntry {
- llvm::CachedHashStringRef Name;
- uint32_t CuIndexAndAttrs;
+ llvm::CachedHashStringRef name;
+ uint32_t cuIndexAndAttrs;
};
struct GdbChunk {
- InputSection *Sec;
- std::vector<AddressEntry> AddressAreas;
- std::vector<CuEntry> CompilationUnits;
+ InputSection *sec;
+ std::vector<AddressEntry> addressAreas;
+ std::vector<CuEntry> compilationUnits;
};
struct GdbSymbol {
- llvm::CachedHashStringRef Name;
- std::vector<uint32_t> CuVector;
- uint32_t NameOff;
- uint32_t CuVectorOff;
+ llvm::CachedHashStringRef name;
+ std::vector<uint32_t> cuVector;
+ uint32_t nameOff;
+ uint32_t cuVectorOff;
};
GdbIndexSection();
template <typename ELFT> static GdbIndexSection *create();
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
- bool empty() const override;
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
+ bool isNeeded() const override;
private:
struct GdbIndexHeader {
- llvm::support::ulittle32_t Version;
- llvm::support::ulittle32_t CuListOff;
- llvm::support::ulittle32_t CuTypesOff;
- llvm::support::ulittle32_t AddressAreaOff;
- llvm::support::ulittle32_t SymtabOff;
- llvm::support::ulittle32_t ConstantPoolOff;
+ llvm::support::ulittle32_t version;
+ llvm::support::ulittle32_t cuListOff;
+ llvm::support::ulittle32_t cuTypesOff;
+ llvm::support::ulittle32_t addressAreaOff;
+ llvm::support::ulittle32_t symtabOff;
+ llvm::support::ulittle32_t constantPoolOff;
};
void initOutputSize();
@@ -723,12 +733,12 @@ private:
// Each chunk contains information gathered from debug sections of a
// single object file.
- std::vector<GdbChunk> Chunks;
+ std::vector<GdbChunk> chunks;
// A symbol table for this .gdb_index section.
- std::vector<GdbSymbol> Symbols;
+ std::vector<GdbSymbol> symbols;
- size_t Size;
+ size_t size;
};
// --eh-frame-hdr option tells linker to construct a header for all the
@@ -743,9 +753,10 @@ private:
class EhFrameHeader final : public SyntheticSection {
public:
EhFrameHeader();
- void writeTo(uint8_t *Buf) override;
+ void write();
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- bool empty() const override;
+ bool isNeeded() const override;
};
// For more information about .gnu.version and .gnu.version_r see:
@@ -761,13 +772,15 @@ public:
VersionDefinitionSection();
void finalizeContents() override;
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
private:
enum { EntrySize = 28 };
- void writeOne(uint8_t *Buf, uint32_t Index, StringRef Name, size_t NameOff);
+ void writeOne(uint8_t *buf, uint32_t index, StringRef name, size_t nameOff);
+ StringRef getFileDefName();
- unsigned FileDefNameOff;
+ unsigned fileDefNameOff;
+ std::vector<unsigned> verDefNameOffs;
};
// The .gnu.version section specifies the required version of each symbol in the
@@ -776,14 +789,13 @@ private:
// identifier defined in the either .gnu.version_r or .gnu.version_d section.
// The values 0 and 1 are reserved. All other values are used for versions in
// the own object or in any of the dependencies.
-template <class ELFT>
class VersionTableSection final : public SyntheticSection {
public:
VersionTableSection();
void finalizeContents() override;
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
- bool empty() const override;
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override;
};
// The .gnu.version_r section defines the version identifiers used by
@@ -791,25 +803,30 @@ public:
// Elf_Verneed specifies the version requirements for a single DSO, and contains
// a reference to a linked list of Elf_Vernaux data structures which define the
// mapping from version identifiers to version names.
-template <class ELFT> class VersionNeedSection final : public SyntheticSection {
- typedef typename ELFT::Verneed Elf_Verneed;
- typedef typename ELFT::Vernaux Elf_Vernaux;
+template <class ELFT>
+class VersionNeedSection final : public SyntheticSection {
+ using Elf_Verneed = typename ELFT::Verneed;
+ using Elf_Vernaux = typename ELFT::Vernaux;
+
+ struct Vernaux {
+ uint64_t hash;
+ uint32_t verneedIndex;
+ uint64_t nameStrTab;
+ };
- // A vector of shared files that need Elf_Verneed data structures and the
- // string table offsets of their sonames.
- std::vector<std::pair<SharedFile<ELFT> *, size_t>> Needed;
+ struct Verneed {
+ uint64_t nameStrTab;
+ std::vector<Vernaux> vernauxs;
+ };
- // The next available version identifier.
- unsigned NextIndex;
+ std::vector<Verneed> verneeds;
public:
VersionNeedSection();
- void addSymbol(Symbol *Sym);
void finalizeContents() override;
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- size_t getNeedNum() const { return Needed.size(); }
- bool empty() const override;
+ bool isNeeded() const override;
};
// MergeSyntheticSection is a class that allows us to put mergeable sections
@@ -818,36 +835,36 @@ public:
// attached to regular output sections.
class MergeSyntheticSection : public SyntheticSection {
public:
- void addSection(MergeInputSection *MS);
- std::vector<MergeInputSection *> Sections;
+ void addSection(MergeInputSection *ms);
+ std::vector<MergeInputSection *> sections;
protected:
- MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
- uint32_t Alignment)
- : SyntheticSection(Flags, Type, Alignment, Name) {}
+ MergeSyntheticSection(StringRef name, uint32_t type, uint64_t flags,
+ uint32_t alignment)
+ : SyntheticSection(flags, type, alignment, name) {}
};
class MergeTailSection final : public MergeSyntheticSection {
public:
- MergeTailSection(StringRef Name, uint32_t Type, uint64_t Flags,
- uint32_t Alignment);
+ MergeTailSection(StringRef name, uint32_t type, uint64_t flags,
+ uint32_t alignment);
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
void finalizeContents() override;
private:
- llvm::StringTableBuilder Builder;
+ llvm::StringTableBuilder builder;
};
class MergeNoTailSection final : public MergeSyntheticSection {
public:
- MergeNoTailSection(StringRef Name, uint32_t Type, uint64_t Flags,
- uint32_t Alignment)
- : MergeSyntheticSection(Name, Type, Flags, Alignment) {}
+ MergeNoTailSection(StringRef name, uint32_t type, uint64_t flags,
+ uint32_t alignment)
+ : MergeSyntheticSection(name, type, flags, alignment) {}
- size_t getSize() const override { return Size; }
- void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return size; }
+ void writeTo(uint8_t *buf) override;
void finalizeContents() override;
private:
@@ -856,67 +873,68 @@ private:
// because DenseMap also uses lower bits to determine a bucket ID.
// If we use lower bits, it significantly increases the probability of
// hash collisons.
- size_t getShardId(uint32_t Hash) {
- return Hash >> (32 - llvm::countTrailingZeros(NumShards));
+ size_t getShardId(uint32_t hash) {
+ assert((hash >> 31) == 0);
+ return hash >> (31 - llvm::countTrailingZeros(numShards));
}
// Section size
- size_t Size;
+ size_t size;
// String table contents
- constexpr static size_t NumShards = 32;
- std::vector<llvm::StringTableBuilder> Shards;
- size_t ShardOffsets[NumShards];
+ constexpr static size_t numShards = 32;
+ std::vector<llvm::StringTableBuilder> shards;
+ size_t shardOffsets[numShards];
};
// .MIPS.abiflags section.
template <class ELFT>
class MipsAbiFlagsSection final : public SyntheticSection {
- typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags;
+ using Elf_Mips_ABIFlags = llvm::object::Elf_Mips_ABIFlags<ELFT>;
public:
static MipsAbiFlagsSection *create();
- MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags);
+ MipsAbiFlagsSection(Elf_Mips_ABIFlags flags);
size_t getSize() const override { return sizeof(Elf_Mips_ABIFlags); }
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
private:
- Elf_Mips_ABIFlags Flags;
+ Elf_Mips_ABIFlags flags;
};
// .MIPS.options section.
template <class ELFT> class MipsOptionsSection final : public SyntheticSection {
- typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options;
- typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
+ using Elf_Mips_Options = llvm::object::Elf_Mips_Options<ELFT>;
+ using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
public:
static MipsOptionsSection *create();
- MipsOptionsSection(Elf_Mips_RegInfo Reginfo);
- void writeTo(uint8_t *Buf) override;
+ MipsOptionsSection(Elf_Mips_RegInfo reginfo);
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override {
return sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
}
private:
- Elf_Mips_RegInfo Reginfo;
+ Elf_Mips_RegInfo reginfo;
};
// MIPS .reginfo section.
template <class ELFT> class MipsReginfoSection final : public SyntheticSection {
- typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
+ using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
public:
static MipsReginfoSection *create();
- MipsReginfoSection(Elf_Mips_RegInfo Reginfo);
+ MipsReginfoSection(Elf_Mips_RegInfo reginfo);
size_t getSize() const override { return sizeof(Elf_Mips_RegInfo); }
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
private:
- Elf_Mips_RegInfo Reginfo;
+ Elf_Mips_RegInfo reginfo;
};
// This is a MIPS specific section to hold a space within the data segment
@@ -926,45 +944,115 @@ private:
class MipsRldMapSection : public SyntheticSection {
public:
MipsRldMapSection();
- size_t getSize() const override { return Config->Wordsize; }
- void writeTo(uint8_t *Buf) override {}
+ size_t getSize() const override { return config->wordsize; }
+ void writeTo(uint8_t *buf) override {}
};
-class ARMExidxSentinelSection : public SyntheticSection {
+// Representation of the combined .ARM.Exidx input sections. We process these
+// as a SyntheticSection like .eh_frame as we need to merge duplicate entries
+// and add terminating sentinel entries.
+//
+// The .ARM.exidx input sections after SHF_LINK_ORDER processing is done form
+// a table that the unwinder can derive (Addresses are encoded as offsets from
+// table):
+// | Address of function | Unwind instructions for function |
+// where the unwind instructions are either a small number of unwind or the
+// special EXIDX_CANTUNWIND entry representing no unwinding information.
+// When an exception is thrown from an address A, the unwinder searches the
+// table for the closest table entry with Address of function <= A. This means
+// that for two consecutive table entries:
+// | A1 | U1 |
+// | A2 | U2 |
+// The range of addresses described by U1 is [A1, A2)
+//
+// There are two cases where we need a linker generated table entry to fixup
+// the address ranges in the table
+// Case 1:
+// - A sentinel entry added with an address higher than all
+// executable sections. This was needed to work around libunwind bug pr31091.
+// - After address assignment we need to find the highest addressed executable
+// section and use the limit of that section so that the unwinder never
+// matches it.
+// Case 2:
+// - InputSections without a .ARM.exidx section (usually from Assembly)
+// need a table entry so that they terminate the range of the previously
+// function. This is pr40277.
+//
+// Instead of storing pointers to the .ARM.exidx InputSections from
+// InputObjects, we store pointers to the executable sections that need
+// .ARM.exidx sections. We can then use the dependentSections of these to
+// either find the .ARM.exidx section or know that we need to generate one.
+class ARMExidxSyntheticSection : public SyntheticSection {
public:
- ARMExidxSentinelSection();
- size_t getSize() const override { return 8; }
- void writeTo(uint8_t *Buf) override;
- bool empty() const override;
+ ARMExidxSyntheticSection();
+
+ // Add an input section to the ARMExidxSyntheticSection. Returns whether the
+ // section needs to be removed from the main input section list.
+ bool addSection(InputSection *isec);
- static bool classof(const SectionBase *D);
+ size_t getSize() const override { return size; }
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override { return !empty; }
+ // Sort and remove duplicate entries.
+ void finalizeContents() override;
+ InputSection *getLinkOrderDep() const;
+
+ static bool classof(const SectionBase *d);
+
+ // Links to the ARMExidxSections so we can transfer the relocations once the
+ // layout is known.
+ std::vector<InputSection *> exidxSections;
- // The last section referenced by a regular .ARM.exidx section.
- // It is found and filled in Writer<ELFT>::resolveShfLinkOrder().
- // The sentinel points at the end of that section.
- InputSection *Highest = nullptr;
+private:
+ size_t size;
+
+ // Empty if ExecutableSections contains no dependent .ARM.exidx sections.
+ bool empty = true;
+
+ // Instead of storing pointers to the .ARM.exidx InputSections from
+ // InputObjects, we store pointers to the executable sections that need
+ // .ARM.exidx sections. We can then use the dependentSections of these to
+ // either find the .ARM.exidx section or know that we need to generate one.
+ std::vector<InputSection *> executableSections;
+
+ // The executable InputSection with the highest address to use for the
+ // sentinel. We store separately from ExecutableSections as merging of
+ // duplicate entries may mean this InputSection is removed from
+ // ExecutableSections.
+ InputSection *sentinel = nullptr;
};
// A container for one or more linker generated thunks. Instances of these
// thunks including ARM interworking and Mips LA25 PI to non-PI thunks.
class ThunkSection : public SyntheticSection {
public:
- // ThunkSection in OS, with desired OutSecOff of Off
- ThunkSection(OutputSection *OS, uint64_t Off);
+ // ThunkSection in OS, with desired outSecOff of Off
+ ThunkSection(OutputSection *os, uint64_t off);
// Add a newly created Thunk to this container:
// Thunk is given offset from start of this InputSection
// Thunk defines a symbol in this InputSection that can be used as target
// of a relocation
- void addThunk(Thunk *T);
- size_t getSize() const override { return Size; }
- void writeTo(uint8_t *Buf) override;
+ void addThunk(Thunk *t);
+ size_t getSize() const override { return size; }
+ void writeTo(uint8_t *buf) override;
InputSection *getTargetInputSection() const;
bool assignOffsets();
private:
- std::vector<Thunk *> Thunks;
- size_t Size = 0;
+ std::vector<Thunk *> thunks;
+ size_t size = 0;
+};
+
+// Used to compute outSecOff of .got2 in each object file. This is needed to
+// synthesize PLT entries for PPC32 Secure PLT ABI.
+class PPC32Got2Section final : public SyntheticSection {
+public:
+ PPC32Got2Section();
+ size_t getSize() const override { return 0; }
+ bool isNeeded() const override;
+ void finalizeContents() override;
+ void writeTo(uint8_t *buf) override {}
};
// This section is used to store the addresses of functions that are called
@@ -975,15 +1063,48 @@ private:
class PPC64LongBranchTargetSection final : public SyntheticSection {
public:
PPC64LongBranchTargetSection();
- void addEntry(Symbol &Sym);
+ void addEntry(Symbol &sym);
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
- bool empty() const override;
- void finalizeContents() override { Finalized = true; }
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override;
+ void finalizeContents() override { finalized = true; }
private:
- std::vector<const Symbol *> Entries;
- bool Finalized = false;
+ std::vector<const Symbol *> entries;
+ bool finalized = false;
+};
+
+template <typename ELFT>
+class PartitionElfHeaderSection : public SyntheticSection {
+public:
+ PartitionElfHeaderSection();
+ size_t getSize() const override;
+ void writeTo(uint8_t *buf) override;
+};
+
+template <typename ELFT>
+class PartitionProgramHeadersSection : public SyntheticSection {
+public:
+ PartitionProgramHeadersSection();
+ size_t getSize() const override;
+ void writeTo(uint8_t *buf) override;
+};
+
+class PartitionIndexSection : public SyntheticSection {
+public:
+ PartitionIndexSection();
+ size_t getSize() const override;
+ void finalizeContents() override;
+ void writeTo(uint8_t *buf) override;
+};
+
+// Create a dummy .sdata for __global_pointer$ if .sdata does not exist.
+class RISCVSdataSection final : public SyntheticSection {
+public:
+ RISCVSdataSection();
+ size_t getSize() const override { return 0; }
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) override {}
};
InputSection *createInterpSection();
@@ -991,52 +1112,76 @@ MergeInputSection *createCommentSection();
template <class ELFT> void splitSections();
void mergeSections();
-Defined *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
- uint64_t Size, InputSectionBase &Section);
-
-// Linker generated sections which can be used as inputs.
-struct InStruct {
- InputSection *ARMAttributes;
- BssSection *Bss;
- BssSection *BssRelRo;
- BuildIdSection *BuildId;
- EhFrameHeader *EhFrameHdr;
- EhFrameSection *EhFrame;
- SyntheticSection *Dynamic;
- StringTableSection *DynStrTab;
- SymbolTableBaseSection *DynSymTab;
- GnuHashTableSection *GnuHashTab;
- HashTableSection *HashTab;
- InputSection *Interp;
- GdbIndexSection *GdbIndex;
- GotSection *Got;
- GotPltSection *GotPlt;
- IgotPltSection *IgotPlt;
- PPC64LongBranchTargetSection *PPC64LongBranchTarget;
- MipsGotSection *MipsGot;
- MipsRldMapSection *MipsRldMap;
- PltSection *Plt;
- PltSection *Iplt;
- RelocationBaseSection *RelaDyn;
- RelrBaseSection *RelrDyn;
- RelocationBaseSection *RelaPlt;
- RelocationBaseSection *RelaIplt;
- StringTableSection *ShStrTab;
- StringTableSection *StrTab;
- SymbolTableBaseSection *SymTab;
- SymtabShndxSection *SymTabShndx;
- VersionDefinitionSection *VerDef;
+template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part);
+template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part);
+
+Defined *addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
+ uint64_t size, InputSectionBase &section);
+
+void addVerneed(Symbol *ss);
+
+// Linker generated per-partition sections.
+struct Partition {
+ StringRef name;
+ uint64_t nameStrTab;
+
+ SyntheticSection *elfHeader;
+ SyntheticSection *programHeaders;
+ std::vector<PhdrEntry *> phdrs;
+
+ ARMExidxSyntheticSection *armExidx;
+ BuildIdSection *buildId;
+ SyntheticSection *dynamic;
+ StringTableSection *dynStrTab;
+ SymbolTableBaseSection *dynSymTab;
+ EhFrameHeader *ehFrameHdr;
+ EhFrameSection *ehFrame;
+ GnuHashTableSection *gnuHashTab;
+ HashTableSection *hashTab;
+ RelocationBaseSection *relaDyn;
+ RelrBaseSection *relrDyn;
+ VersionDefinitionSection *verDef;
+ SyntheticSection *verNeed;
+ VersionTableSection *verSym;
+
+ unsigned getNumber() const { return this - &partitions[0] + 1; }
};
-extern InStruct In;
+extern Partition *mainPart;
-template <class ELFT> struct InX {
- static VersionTableSection<ELFT> *VerSym;
- static VersionNeedSection<ELFT> *VerNeed;
+inline Partition &SectionBase::getPartition() const {
+ assert(isLive());
+ return partitions[partition - 1];
+}
+
+// Linker generated sections which can be used as inputs and are not specific to
+// a partition.
+struct InStruct {
+ InputSection *armAttributes;
+ BssSection *bss;
+ BssSection *bssRelRo;
+ GotSection *got;
+ GotPltSection *gotPlt;
+ IgotPltSection *igotPlt;
+ PPC64LongBranchTargetSection *ppc64LongBranchTarget;
+ MipsGotSection *mipsGot;
+ MipsRldMapSection *mipsRldMap;
+ SyntheticSection *partEnd;
+ SyntheticSection *partIndex;
+ PltSection *plt;
+ PltSection *iplt;
+ PPC32Got2Section *ppc32Got2;
+ RISCVSdataSection *riscvSdata;
+ RelocationBaseSection *relaPlt;
+ RelocationBaseSection *relaIplt;
+ StringTableSection *shStrTab;
+ StringTableSection *strTab;
+ SymbolTableBaseSection *symTab;
+ SymtabShndxSection *symTabShndx;
};
-template <class ELFT> VersionTableSection<ELFT> *InX<ELFT>::VerSym;
-template <class ELFT> VersionNeedSection<ELFT> *InX<ELFT>::VerNeed;
+extern InStruct in;
+
} // namespace elf
} // namespace lld
diff --git a/ELF/Target.cpp b/ELF/Target.cpp
index 01073a62cfd6a..d07478a5178c3 100644
--- a/ELF/Target.cpp
+++ b/ELF/Target.cpp
@@ -1,9 +1,8 @@
//===- Target.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -38,17 +37,17 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-TargetInfo *elf::Target;
+const TargetInfo *elf::target;
-std::string lld::toString(RelType Type) {
- StringRef S = getELFRelocationTypeName(elf::Config->EMachine, Type);
- if (S == "Unknown")
- return ("Unknown (" + Twine(Type) + ")").str();
- return S;
+std::string lld::toString(RelType type) {
+ StringRef s = getELFRelocationTypeName(elf::config->emachine, type);
+ if (s == "Unknown")
+ return ("Unknown (" + Twine(type) + ")").str();
+ return s;
}
TargetInfo *elf::getTarget() {
- switch (Config->EMachine) {
+ switch (config->emachine) {
case EM_386:
case EM_IAMCU:
return getX86TargetInfo();
@@ -63,7 +62,7 @@ TargetInfo *elf::getTarget() {
case EM_HEXAGON:
return getHexagonTargetInfo();
case EM_MIPS:
- switch (Config->EKind) {
+ switch (config->ekind) {
case ELF32LEKind:
return getMipsTargetInfo<ELF32LE>();
case ELF32BEKind:
@@ -86,36 +85,34 @@ TargetInfo *elf::getTarget() {
case EM_SPARCV9:
return getSPARCV9TargetInfo();
case EM_X86_64:
- if (Config->EKind == ELF32LEKind)
- return getX32TargetInfo();
return getX86_64TargetInfo();
}
llvm_unreachable("unknown target machine");
}
-template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *Loc) {
- for (InputSectionBase *D : InputSections) {
- auto *IS = cast<InputSection>(D);
- if (!IS->getParent())
+template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
+ for (InputSectionBase *d : inputSections) {
+ auto *isec = cast<InputSection>(d);
+ if (!isec->getParent())
continue;
- uint8_t *ISLoc = IS->getParent()->Loc + IS->OutSecOff;
- if (ISLoc <= Loc && Loc < ISLoc + IS->getSize())
- return {IS, IS->template getLocation<ELFT>(Loc - ISLoc) + ": "};
+ uint8_t *isecLoc = Out::bufferStart + isec->getParent()->offset + isec->outSecOff;
+ if (isecLoc <= loc && loc < isecLoc + isec->getSize())
+ return {isec, isec->template getLocation<ELFT>(loc - isecLoc) + ": "};
}
return {};
}
-ErrorPlace elf::getErrorPlace(const uint8_t *Loc) {
- switch (Config->EKind) {
+ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
+ switch (config->ekind) {
case ELF32LEKind:
- return getErrPlace<ELF32LE>(Loc);
+ return getErrPlace<ELF32LE>(loc);
case ELF32BEKind:
- return getErrPlace<ELF32BE>(Loc);
+ return getErrPlace<ELF32BE>(loc);
case ELF64LEKind:
- return getErrPlace<ELF64LE>(Loc);
+ return getErrPlace<ELF64LE>(loc);
case ELF64BEKind:
- return getErrPlace<ELF64BE>(Loc);
+ return getErrPlace<ELF64BE>(loc);
default:
llvm_unreachable("unknown ELF type");
}
@@ -123,62 +120,62 @@ ErrorPlace elf::getErrorPlace(const uint8_t *Loc) {
TargetInfo::~TargetInfo() {}
-int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
+int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const {
return 0;
}
-bool TargetInfo::usesOnlyLowPageBits(RelType Type) const { return false; }
+bool TargetInfo::usesOnlyLowPageBits(RelType type) const { return false; }
-bool TargetInfo::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
+bool TargetInfo::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
return false;
}
-bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const {
+bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const {
llvm_unreachable("Target doesn't support split stacks.");
}
-bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
+bool TargetInfo::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
return true;
}
-void TargetInfo::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
- writeGotPlt(Buf, S);
+void TargetInfo::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
+ writeGotPlt(buf, s);
}
-RelExpr TargetInfo::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const {
- return Expr;
+RelExpr TargetInfo::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ return expr;
}
-void TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const {
+void TargetInfo::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
+void TargetInfo::relaxTlsGdToLe(uint8_t *loc, RelType type,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToIe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
+void TargetInfo::relaxTlsGdToIe(uint8_t *loc, RelType type,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsIeToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
+void TargetInfo::relaxTlsIeToLe(uint8_t *loc, RelType type,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsLdToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
+void TargetInfo::relaxTlsLdToLe(uint8_t *loc, RelType type,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-uint64_t TargetInfo::getImageBase() {
+uint64_t TargetInfo::getImageBase() const {
// Use -image-base if set. Fall back to the target default if not.
- if (Config->ImageBase)
- return *Config->ImageBase;
- return Config->Pic ? 0 : DefaultImageBase;
+ if (config->imageBase)
+ return *config->imageBase;
+ return config->isPic ? 0 : defaultImageBase;
}
diff --git a/ELF/Target.h b/ELF/Target.h
index 685ad05ecd664..effa6001f6d96 100644
--- a/ELF/Target.h
+++ b/ELF/Target.h
@@ -1,9 +1,8 @@
//===- Target.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -17,7 +16,7 @@
#include <array>
namespace lld {
-std::string toString(elf::RelType Type);
+std::string toString(elf::RelType type);
namespace elf {
class Defined;
@@ -27,36 +26,39 @@ class Symbol;
class TargetInfo {
public:
virtual uint32_t calcEFlags() const { return 0; }
- virtual RelType getDynRel(RelType Type) const { return Type; }
- virtual void writeGotPltHeader(uint8_t *Buf) const {}
- virtual void writeGotHeader(uint8_t *Buf) const {}
- virtual void writeGotPlt(uint8_t *Buf, const Symbol &S) const {};
- virtual void writeIgotPlt(uint8_t *Buf, const Symbol &S) const;
- virtual int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const;
+ virtual RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const = 0;
+ virtual RelType getDynRel(RelType type) const { return 0; }
+ virtual void writeGotPltHeader(uint8_t *buf) const {}
+ virtual void writeGotHeader(uint8_t *buf) const {}
+ virtual void writeGotPlt(uint8_t *buf, const Symbol &s) const {};
+ virtual void writeIgotPlt(uint8_t *buf, const Symbol &s) const;
+ virtual int64_t getImplicitAddend(const uint8_t *buf, RelType type) const;
+ virtual int getTlsGdRelaxSkip(RelType type) const { return 1; }
// If lazy binding is supported, the first entry of the PLT has code
// to call the dynamic linker to resolve PLT entries the first time
// they are called. This function writes that code.
- virtual void writePltHeader(uint8_t *Buf) const {}
+ virtual void writePltHeader(uint8_t *buf) const {}
- virtual void writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {}
- virtual void addPltHeaderSymbols(InputSection &IS) const {}
- virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {}
+ virtual void writePlt(uint8_t *buf, uint64_t gotEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {}
+ virtual void addPltHeaderSymbols(InputSection &isec) const {}
+ virtual void addPltSymbols(InputSection &isec, uint64_t off) const {}
// Returns true if a relocation only uses the low bits of a value such that
// all those bits are in the same page. For example, if the relocation
// only uses the low 12 bits in a system with 4k pages. If this is true, the
// bits will always have the same value at runtime and we don't have to emit
// a dynamic relocation.
- virtual bool usesOnlyLowPageBits(RelType Type) const;
+ virtual bool usesOnlyLowPageBits(RelType type) const;
// Decide whether a Thunk is needed for the relocation from File
// targeting S.
- virtual bool needsThunk(RelExpr Expr, RelType RelocType,
- const InputFile *File, uint64_t BranchAddr,
- const Symbol &S) const;
+ virtual bool needsThunk(RelExpr expr, RelType relocType,
+ const InputFile *file, uint64_t branchAddr,
+ const Symbol &s) const;
// On systems with range extensions we place collections of Thunks at
// regular spacings that enable the majority of branches reach the Thunks.
@@ -68,77 +70,71 @@ public:
// to do the right thing. See https://gcc.gnu.org/wiki/SplitStacks.
// The symbols st_other flags are needed on PowerPC64 for determining the
// offset to the split-stack prologue.
- virtual bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const;
+ virtual bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const;
- // Return true if we can reach Dst from Src with Relocation RelocType
- virtual bool inBranchRange(RelType Type, uint64_t Src,
- uint64_t Dst) const;
- virtual RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const = 0;
+ // Return true if we can reach dst from src with RelType type.
+ virtual bool inBranchRange(RelType type, uint64_t src,
+ uint64_t dst) const;
- virtual void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const = 0;
+ virtual void relocateOne(uint8_t *loc, RelType type, uint64_t val) const = 0;
virtual ~TargetInfo();
- unsigned TlsGdRelaxSkip = 1;
- unsigned PageSize = 4096;
- unsigned DefaultMaxPageSize = 4096;
+ unsigned defaultCommonPageSize = 4096;
+ unsigned defaultMaxPageSize = 4096;
- uint64_t getImageBase();
+ uint64_t getImageBase() const;
- // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got or .got.plt section.
- uint64_t GotBaseSymOff = 0;
// True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got.
- bool GotBaseSymInGotPlt = true;
-
- RelType CopyRel;
- RelType GotRel;
- RelType NoneRel;
- RelType PltRel;
- RelType RelativeRel;
- RelType IRelativeRel;
- RelType TlsDescRel;
- RelType TlsGotRel;
- RelType TlsModuleIndexRel;
- RelType TlsOffsetRel;
- unsigned GotEntrySize = 0;
- unsigned GotPltEntrySize = 0;
- unsigned PltEntrySize;
- unsigned PltHeaderSize;
+ bool gotBaseSymInGotPlt = true;
+
+ RelType copyRel;
+ RelType gotRel;
+ RelType noneRel;
+ RelType pltRel;
+ RelType relativeRel;
+ RelType iRelativeRel;
+ RelType symbolicRel;
+ RelType tlsDescRel;
+ RelType tlsGotRel;
+ RelType tlsModuleIndexRel;
+ RelType tlsOffsetRel;
+ unsigned pltEntrySize;
+ unsigned pltHeaderSize;
// At least on x86_64 positions 1 and 2 are used by the first plt entry
// to support lazy loading.
- unsigned GotPltHeaderEntriesNum = 3;
+ unsigned gotPltHeaderEntriesNum = 3;
// On PPC ELF V2 abi, the first entry in the .got is the .TOC.
- unsigned GotHeaderEntriesNum = 0;
+ unsigned gotHeaderEntriesNum = 0;
- bool NeedsThunks = false;
+ bool needsThunks = false;
// A 4-byte field corresponding to one or more trap instructions, used to pad
// executable OutputSections.
- std::array<uint8_t, 4> TrapInstr;
+ std::array<uint8_t, 4> trapInstr;
// If a target needs to rewrite calls to __morestack to instead call
// __morestack_non_split when a split-stack enabled caller calls a
// non-split-stack callee this will return true. Otherwise returns false.
- bool NeedsMoreStackNonSplit = true;
+ bool needsMoreStackNonSplit = true;
- virtual RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const;
- virtual void relaxGot(uint8_t *Loc, uint64_t Val) const;
- virtual void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const;
- virtual void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
- virtual void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
- virtual void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
+ virtual RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const;
+ virtual void relaxGot(uint8_t *loc, RelType type, uint64_t val) const;
+ virtual void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const;
+ virtual void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const;
+ virtual void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const;
+ virtual void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const;
protected:
// On FreeBSD x86_64 the first page cannot be mmaped.
// On Linux that is controled by vm.mmap_min_addr. At least on some x86_64
// installs that is 65536, so the first 15 pages cannot be used.
// Given that, the smallest value that can be used in here is 0x10000.
- uint64_t DefaultImageBase = 0x10000;
+ uint64_t defaultImageBase = 0x10000;
};
TargetInfo *getAArch64TargetInfo();
@@ -151,108 +147,113 @@ TargetInfo *getPPC64TargetInfo();
TargetInfo *getPPCTargetInfo();
TargetInfo *getRISCVTargetInfo();
TargetInfo *getSPARCV9TargetInfo();
-TargetInfo *getX32TargetInfo();
TargetInfo *getX86TargetInfo();
TargetInfo *getX86_64TargetInfo();
template <class ELFT> TargetInfo *getMipsTargetInfo();
struct ErrorPlace {
- InputSectionBase *IS;
- std::string Loc;
+ InputSectionBase *isec;
+ std::string loc;
};
// Returns input section and corresponding source string for the given location.
-ErrorPlace getErrorPlace(const uint8_t *Loc);
+ErrorPlace getErrorPlace(const uint8_t *loc);
-static inline std::string getErrorLocation(const uint8_t *Loc) {
- return getErrorPlace(Loc).Loc;
+static inline std::string getErrorLocation(const uint8_t *loc) {
+ return getErrorPlace(loc).loc;
}
-// In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first is
-// a global entry point (GEP) which typically is used to intiailzie the TOC
+void writePPC32GlinkSection(uint8_t *buf, size_t numEntries);
+
+bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
+ uint8_t *bufLoc);
+unsigned getPPCDFormOp(unsigned secondaryOp);
+
+// In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first
+// is a global entry point (GEP) which typically is used to initialize the TOC
// pointer in general purpose register 2. The second is a local entry
// point (LEP) which bypasses the TOC pointer initialization code. The
// offset between GEP and LEP is encoded in a function's st_other flags.
// This function will return the offset (in bytes) from the global entry-point
// to the local entry-point.
-unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther);
+unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther);
+
+// Returns true if a relocation is a small code model relocation that accesses
+// the .toc section.
+bool isPPC64SmallCodeModelTocReloc(RelType type);
uint64_t getPPC64TocBase();
-uint64_t getAArch64Page(uint64_t Expr);
+uint64_t getAArch64Page(uint64_t expr);
-extern TargetInfo *Target;
+extern const TargetInfo *target;
TargetInfo *getTarget();
-template <class ELFT> bool isMipsPIC(const Defined *Sym);
+template <class ELFT> bool isMipsPIC(const Defined *sym);
-static inline void reportRangeError(uint8_t *Loc, RelType Type, const Twine &V,
- int64_t Min, uint64_t Max) {
- ErrorPlace ErrPlace = getErrorPlace(Loc);
- StringRef Hint;
- if (ErrPlace.IS && ErrPlace.IS->Name.startswith(".debug"))
- Hint = "; consider recompiling with -fdebug-types-section to reduce size "
+static inline void reportRangeError(uint8_t *loc, RelType type, const Twine &v,
+ int64_t min, uint64_t max) {
+ ErrorPlace errPlace = getErrorPlace(loc);
+ StringRef hint;
+ if (errPlace.isec && errPlace.isec->name.startswith(".debug"))
+ hint = "; consider recompiling with -fdebug-types-section to reduce size "
"of debug sections";
- errorOrWarn(ErrPlace.Loc + "relocation " + lld::toString(Type) +
- " out of range: " + V.str() + " is not in [" + Twine(Min).str() +
- ", " + Twine(Max).str() + "]" + Hint);
-}
-
-inline unsigned getPltEntryOffset(unsigned Idx) {
- return Target->PltHeaderSize + Target->PltEntrySize * Idx;
+ errorOrWarn(errPlace.loc + "relocation " + lld::toString(type) +
+ " out of range: " + v.str() + " is not in [" + Twine(min).str() +
+ ", " + Twine(max).str() + "]" + hint);
}
// Make sure that V can be represented as an N bit signed integer.
-inline void checkInt(uint8_t *Loc, int64_t V, int N, RelType Type) {
- if (V != llvm::SignExtend64(V, N))
- reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N));
+inline void checkInt(uint8_t *loc, int64_t v, int n, RelType type) {
+ if (v != llvm::SignExtend64(v, n))
+ reportRangeError(loc, type, Twine(v), llvm::minIntN(n), llvm::maxIntN(n));
}
// Make sure that V can be represented as an N bit unsigned integer.
-inline void checkUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) {
- if ((V >> N) != 0)
- reportRangeError(Loc, Type, Twine(V), 0, llvm::maxUIntN(N));
+inline void checkUInt(uint8_t *loc, uint64_t v, int n, RelType type) {
+ if ((v >> n) != 0)
+ reportRangeError(loc, type, Twine(v), 0, llvm::maxUIntN(n));
}
// Make sure that V can be represented as an N bit signed or unsigned integer.
-inline void checkIntUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) {
+inline void checkIntUInt(uint8_t *loc, uint64_t v, int n, RelType type) {
// For the error message we should cast V to a signed integer so that error
// messages show a small negative value rather than an extremely large one
- if (V != (uint64_t)llvm::SignExtend64(V, N) && (V >> N) != 0)
- reportRangeError(Loc, Type, Twine((int64_t)V), llvm::minIntN(N),
- llvm::maxIntN(N));
+ if (v != (uint64_t)llvm::SignExtend64(v, n) && (v >> n) != 0)
+ reportRangeError(loc, type, Twine((int64_t)v), llvm::minIntN(n),
+ llvm::maxUIntN(n));
}
-inline void checkAlignment(uint8_t *Loc, uint64_t V, int N, RelType Type) {
- if ((V & (N - 1)) != 0)
- error(getErrorLocation(Loc) + "improper alignment for relocation " +
- lld::toString(Type) + ": 0x" + llvm::utohexstr(V) +
- " is not aligned to " + Twine(N) + " bytes");
+inline void checkAlignment(uint8_t *loc, uint64_t v, int n, RelType type) {
+ if ((v & (n - 1)) != 0)
+ error(getErrorLocation(loc) + "improper alignment for relocation " +
+ lld::toString(type) + ": 0x" + llvm::utohexstr(v) +
+ " is not aligned to " + Twine(n) + " bytes");
}
// Endianness-aware read/write.
-inline uint16_t read16(const void *P) {
- return llvm::support::endian::read16(P, Config->Endianness);
+inline uint16_t read16(const void *p) {
+ return llvm::support::endian::read16(p, config->endianness);
}
-inline uint32_t read32(const void *P) {
- return llvm::support::endian::read32(P, Config->Endianness);
+inline uint32_t read32(const void *p) {
+ return llvm::support::endian::read32(p, config->endianness);
}
-inline uint64_t read64(const void *P) {
- return llvm::support::endian::read64(P, Config->Endianness);
+inline uint64_t read64(const void *p) {
+ return llvm::support::endian::read64(p, config->endianness);
}
-inline void write16(void *P, uint16_t V) {
- llvm::support::endian::write16(P, V, Config->Endianness);
+inline void write16(void *p, uint16_t v) {
+ llvm::support::endian::write16(p, v, config->endianness);
}
-inline void write32(void *P, uint32_t V) {
- llvm::support::endian::write32(P, V, Config->Endianness);
+inline void write32(void *p, uint32_t v) {
+ llvm::support::endian::write32(p, v, config->endianness);
}
-inline void write64(void *P, uint64_t V) {
- llvm::support::endian::write64(P, V, Config->Endianness);
+inline void write64(void *p, uint64_t v) {
+ llvm::support::endian::write64(p, v, config->endianness);
}
} // namespace elf
} // namespace lld
diff --git a/ELF/Thunks.cpp b/ELF/Thunks.cpp
index 95b57dc0db426..73208f932031d 100644
--- a/ELF/Thunks.cpp
+++ b/ELF/Thunks.cpp
@@ -1,9 +1,8 @@
//===- Thunks.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===---------------------------------------------------------------------===//
//
@@ -50,18 +49,18 @@ namespace {
// AArch64 long range Thunks
class AArch64ABSLongThunk final : public Thunk {
public:
- AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
+ AArch64ABSLongThunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 16; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class AArch64ADRPThunk final : public Thunk {
public:
- AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
+ AArch64ADRPThunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 12; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
// Base class for ARM thunks.
@@ -74,18 +73,19 @@ public:
// if the target is in range, otherwise it creates a long thunk.
class ARMThunk : public Thunk {
public:
- ARMThunk(Symbol &Dest) : Thunk(Dest) {}
+ ARMThunk(Symbol &dest) : Thunk(dest) {}
- bool mayUseShortThunk();
- uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
- void writeTo(uint8_t *Buf) override;
- bool isCompatibleWith(RelType Type) const override;
+ bool getMayUseShortThunk();
+ uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
+ void writeTo(uint8_t *buf) override;
+ bool isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const override;
// Returns the size of a long thunk.
virtual uint32_t sizeLong() = 0;
// Writes a long thunk to Buf.
- virtual void writeLong(uint8_t *Buf) = 0;
+ virtual void writeLong(uint8_t *buf) = 0;
private:
// This field tracks whether all previously considered layouts would allow
@@ -94,7 +94,7 @@ private:
// distance to the target. We do this because transitioning from long to short
// can create layout oscillations in certain corner cases which would prevent
// the layout from converging.
- bool MayUseShortThunk = true;
+ bool mayUseShortThunk = true;
};
// Base class for Thumb-2 thunks.
@@ -103,60 +103,61 @@ private:
// which has a range of 16MB.
class ThumbThunk : public Thunk {
public:
- ThumbThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
+ ThumbThunk(Symbol &dest) : Thunk(dest) { alignment = 2; }
- bool mayUseShortThunk();
- uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
- void writeTo(uint8_t *Buf) override;
- bool isCompatibleWith(RelType Type) const override;
+ bool getMayUseShortThunk();
+ uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
+ void writeTo(uint8_t *buf) override;
+ bool isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const override;
// Returns the size of a long thunk.
virtual uint32_t sizeLong() = 0;
// Writes a long thunk to Buf.
- virtual void writeLong(uint8_t *Buf) = 0;
+ virtual void writeLong(uint8_t *buf) = 0;
private:
// See comment in ARMThunk above.
- bool MayUseShortThunk = true;
+ bool mayUseShortThunk = true;
};
// Specific ARM Thunk implementations. The naming convention is:
// Source State, TargetState, Target Requirement, ABS or PI, Range
class ARMV7ABSLongThunk final : public ARMThunk {
public:
- ARMV7ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
+ ARMV7ABSLongThunk(Symbol &dest) : ARMThunk(dest) {}
uint32_t sizeLong() override { return 12; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class ARMV7PILongThunk final : public ARMThunk {
public:
- ARMV7PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
+ ARMV7PILongThunk(Symbol &dest) : ARMThunk(dest) {}
uint32_t sizeLong() override { return 16; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class ThumbV7ABSLongThunk final : public ThumbThunk {
public:
- ThumbV7ABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
+ ThumbV7ABSLongThunk(Symbol &dest) : ThumbThunk(dest) {}
uint32_t sizeLong() override { return 10; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class ThumbV7PILongThunk final : public ThumbThunk {
public:
- ThumbV7PILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
+ ThumbV7PILongThunk(Symbol &dest) : ThumbThunk(dest) {}
uint32_t sizeLong() override { return 12; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
// Implementations of Thunks for older Arm architectures that do not support
@@ -166,76 +167,96 @@ public:
// can result in a thunk
class ARMV5ABSLongThunk final : public ARMThunk {
public:
- ARMV5ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
+ ARMV5ABSLongThunk(Symbol &dest) : ARMThunk(dest) {}
uint32_t sizeLong() override { return 8; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(uint32_t RelocType) const override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+ bool isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const override;
};
class ARMV5PILongThunk final : public ARMThunk {
public:
- ARMV5PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
+ ARMV5PILongThunk(Symbol &dest) : ARMThunk(dest) {}
uint32_t sizeLong() override { return 16; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(uint32_t RelocType) const override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+ bool isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const override;
};
// Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted
class ThumbV6MABSLongThunk final : public ThumbThunk {
public:
- ThumbV6MABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
+ ThumbV6MABSLongThunk(Symbol &dest) : ThumbThunk(dest) {}
uint32_t sizeLong() override { return 12; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class ThumbV6MPILongThunk final : public ThumbThunk {
public:
- ThumbV6MPILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
+ ThumbV6MPILongThunk(Symbol &dest) : ThumbThunk(dest) {}
uint32_t sizeLong() override { return 16; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
// MIPS LA25 thunk
class MipsThunk final : public Thunk {
public:
- MipsThunk(Symbol &Dest) : Thunk(Dest) {}
+ MipsThunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 16; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
InputSection *getTargetInputSection() const override;
};
// microMIPS R2-R5 LA25 thunk
class MicroMipsThunk final : public Thunk {
public:
- MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
+ MicroMipsThunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 14; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
InputSection *getTargetInputSection() const override;
};
// microMIPS R6 LA25 thunk
class MicroMipsR6Thunk final : public Thunk {
public:
- MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
+ MicroMipsR6Thunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 12; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
InputSection *getTargetInputSection() const override;
};
+class PPC32PltCallStub final : public Thunk {
+public:
+ PPC32PltCallStub(const InputSection &isec, const Relocation &rel, Symbol &dest)
+ : Thunk(dest), addend(rel.type == R_PPC_PLTREL24 ? rel.addend : 0),
+ file(isec.file) {}
+ uint32_t size() override { return 16; }
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+ bool isCompatibleWith(const InputSection &isec, const Relocation &rel) const override;
+
+private:
+ // For R_PPC_PLTREL24, this records the addend, which will be used to decide
+ // the offsets in the call stub.
+ uint32_t addend;
+
+ // Records the call site of the call stub.
+ const InputFile *file;
+};
// PPC64 Plt call stubs.
// Any call site that needs to call through a plt entry needs a call stub in
@@ -247,10 +268,10 @@ public:
// 3) Transfering control to the target function through an indirect branch.
class PPC64PltCallStub final : public Thunk {
public:
- PPC64PltCallStub(Symbol &Dest) : Thunk(Dest) {}
+ PPC64PltCallStub(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 20; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
@@ -264,74 +285,75 @@ public:
class PPC64LongBranchThunk : public Thunk {
public:
uint32_t size() override { return 16; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
protected:
- PPC64LongBranchThunk(Symbol &Dest) : Thunk(Dest) {}
+ PPC64LongBranchThunk(Symbol &dest) : Thunk(dest) {}
};
class PPC64PILongBranchThunk final : public PPC64LongBranchThunk {
public:
- PPC64PILongBranchThunk(Symbol &Dest) : PPC64LongBranchThunk(Dest) {
- assert(!Dest.IsPreemptible);
- if (Dest.isInPPC64Branchlt())
+ PPC64PILongBranchThunk(Symbol &dest) : PPC64LongBranchThunk(dest) {
+ assert(!dest.isPreemptible);
+ if (dest.isInPPC64Branchlt())
return;
- In.PPC64LongBranchTarget->addEntry(Dest);
- In.RelaDyn->addReloc({Target->RelativeRel, In.PPC64LongBranchTarget,
- Dest.getPPC64LongBranchOffset(), true, &Dest,
- getPPC64GlobalEntryToLocalEntryOffset(Dest.StOther)});
+ in.ppc64LongBranchTarget->addEntry(dest);
+ mainPart->relaDyn->addReloc(
+ {target->relativeRel, in.ppc64LongBranchTarget,
+ dest.getPPC64LongBranchOffset(), true, &dest,
+ getPPC64GlobalEntryToLocalEntryOffset(dest.stOther)});
}
};
class PPC64PDLongBranchThunk final : public PPC64LongBranchThunk {
public:
- PPC64PDLongBranchThunk(Symbol &Dest) : PPC64LongBranchThunk(Dest) {
- if (!Dest.isInPPC64Branchlt())
- In.PPC64LongBranchTarget->addEntry(Dest);
+ PPC64PDLongBranchThunk(Symbol &dest) : PPC64LongBranchThunk(dest) {
+ if (!dest.isInPPC64Branchlt())
+ in.ppc64LongBranchTarget->addEntry(dest);
}
};
} // end anonymous namespace
-Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
- InputSectionBase &Section) {
- Defined *D = addSyntheticLocal(Name, Type, Value, /*Size=*/0, Section);
- Syms.push_back(D);
- return D;
+Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value,
+ InputSectionBase &section) {
+ Defined *d = addSyntheticLocal(name, type, value, /*size=*/0, section);
+ syms.push_back(d);
+ return d;
}
-void Thunk::setOffset(uint64_t NewOffset) {
- for (Defined *D : Syms)
- D->Value = D->Value - Offset + NewOffset;
- Offset = NewOffset;
+void Thunk::setOffset(uint64_t newOffset) {
+ for (Defined *d : syms)
+ d->value = d->value - offset + newOffset;
+ offset = newOffset;
}
// AArch64 long range Thunks
-static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
- uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
- return V;
+static uint64_t getAArch64ThunkDestVA(const Symbol &s) {
+ uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA();
+ return v;
}
-void AArch64ABSLongThunk::writeTo(uint8_t *Buf) {
- const uint8_t Data[] = {
+void AArch64ABSLongThunk::writeTo(uint8_t *buf) {
+ const uint8_t data[] = {
0x50, 0x00, 0x00, 0x58, // ldr x16, L0
0x00, 0x02, 0x1f, 0xd6, // br x16
0x00, 0x00, 0x00, 0x00, // L0: .xword S
0x00, 0x00, 0x00, 0x00,
};
- uint64_t S = getAArch64ThunkDestVA(Destination);
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S);
+ uint64_t s = getAArch64ThunkDestVA(destination);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf + 8, R_AARCH64_ABS64, s);
}
-void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()),
- STT_FUNC, 0, IS);
- addSymbol("$x", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 8, IS);
+void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__AArch64AbsLongThunk_" + destination.getName()),
+ STT_FUNC, 0, isec);
+ addSymbol("$x", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 8, isec);
}
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
@@ -339,259 +361,263 @@ void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
// clang and gcc do not support the large code model for position independent
// code so it is safe to use this for position independent thunks without
// worrying about the destination being more than 4Gb away.
-void AArch64ADRPThunk::writeTo(uint8_t *Buf) {
- const uint8_t Data[] = {
+void AArch64ADRPThunk::writeTo(uint8_t *buf) {
+ const uint8_t data[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
0x00, 0x02, 0x1f, 0xd6, // br x16
};
- uint64_t S = getAArch64ThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA();
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(S) - getAArch64Page(P));
- Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S);
+ uint64_t s = getAArch64ThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA();
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(s) - getAArch64Page(p));
+ target->relocateOne(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s);
}
-void AArch64ADRPThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
- 0, IS);
- addSymbol("$x", STT_NOTYPE, 0, IS);
+void AArch64ADRPThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__AArch64ADRPThunk_" + destination.getName()), STT_FUNC,
+ 0, isec);
+ addSymbol("$x", STT_NOTYPE, 0, isec);
}
// ARM Target Thunks
-static uint64_t getARMThunkDestVA(const Symbol &S) {
- uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
- return SignExtend64<32>(V);
+static uint64_t getARMThunkDestVA(const Symbol &s) {
+ uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA();
+ return SignExtend64<32>(v);
}
// This function returns true if the target is not Thumb and is within 2^26, and
-// it has not previously returned false (see comment for MayUseShortThunk).
-bool ARMThunk::mayUseShortThunk() {
- if (!MayUseShortThunk)
+// it has not previously returned false (see comment for mayUseShortThunk).
+bool ARMThunk::getMayUseShortThunk() {
+ if (!mayUseShortThunk)
return false;
- uint64_t S = getARMThunkDestVA(Destination);
- if (S & 1) {
- MayUseShortThunk = false;
+ uint64_t s = getARMThunkDestVA(destination);
+ if (s & 1) {
+ mayUseShortThunk = false;
return false;
}
- uint64_t P = getThunkTargetSym()->getVA();
- int64_t Offset = S - P - 8;
- MayUseShortThunk = llvm::isInt<26>(Offset);
- return MayUseShortThunk;
+ uint64_t p = getThunkTargetSym()->getVA();
+ int64_t offset = s - p - 8;
+ mayUseShortThunk = llvm::isInt<26>(offset);
+ return mayUseShortThunk;
}
-void ARMThunk::writeTo(uint8_t *Buf) {
- if (!mayUseShortThunk()) {
- writeLong(Buf);
+void ARMThunk::writeTo(uint8_t *buf) {
+ if (!getMayUseShortThunk()) {
+ writeLong(buf);
return;
}
- uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA();
- int64_t Offset = S - P - 8;
- const uint8_t Data[] = {
+ uint64_t s = getARMThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA();
+ int64_t offset = s - p - 8;
+ const uint8_t data[] = {
0x00, 0x00, 0x00, 0xea, // b S
};
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_JUMP24, Offset);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_ARM_JUMP24, offset);
}
-bool ARMThunk::isCompatibleWith(RelType Type) const {
+bool ARMThunk::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
// Thumb branch relocations can't use BLX
- return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
+ return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
}
// This function returns true if the target is Thumb and is within 2^25, and
-// it has not previously returned false (see comment for MayUseShortThunk).
-bool ThumbThunk::mayUseShortThunk() {
- if (!MayUseShortThunk)
+// it has not previously returned false (see comment for mayUseShortThunk).
+bool ThumbThunk::getMayUseShortThunk() {
+ if (!mayUseShortThunk)
return false;
- uint64_t S = getARMThunkDestVA(Destination);
- if ((S & 1) == 0) {
- MayUseShortThunk = false;
+ uint64_t s = getARMThunkDestVA(destination);
+ if ((s & 1) == 0) {
+ mayUseShortThunk = false;
return false;
}
- uint64_t P = getThunkTargetSym()->getVA() & ~1;
- int64_t Offset = S - P - 4;
- MayUseShortThunk = llvm::isInt<25>(Offset);
- return MayUseShortThunk;
+ uint64_t p = getThunkTargetSym()->getVA() & ~1;
+ int64_t offset = s - p - 4;
+ mayUseShortThunk = llvm::isInt<25>(offset);
+ return mayUseShortThunk;
}
-void ThumbThunk::writeTo(uint8_t *Buf) {
- if (!mayUseShortThunk()) {
- writeLong(Buf);
+void ThumbThunk::writeTo(uint8_t *buf) {
+ if (!getMayUseShortThunk()) {
+ writeLong(buf);
return;
}
- uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA();
- int64_t Offset = S - P - 4;
- const uint8_t Data[] = {
+ uint64_t s = getARMThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA();
+ int64_t offset = s - p - 4;
+ const uint8_t data[] = {
0x00, 0xf0, 0x00, 0xb0, // b.w S
};
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_ARM_THM_JUMP24, offset);
}
-bool ThumbThunk::isCompatibleWith(RelType Type) const {
+bool ThumbThunk::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
// ARM branch relocations can't use BLX
- return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
+ return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32;
}
-void ARMV7ABSLongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ARMV7ABSLongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
0x1c, 0xff, 0x2f, 0xe1, // bx ip
};
- uint64_t S = getARMThunkDestVA(Destination);
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
- Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
+ uint64_t s = getARMThunkDestVA(destination);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_ARM_MOVW_ABS_NC, s);
+ target->relocateOne(buf + 4, R_ARM_MOVT_ABS, s);
}
-void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()),
- STT_FUNC, 0, IS);
- addSymbol("$a", STT_NOTYPE, 0, IS);
+void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ARMv7ABSLongThunk_" + destination.getName()),
+ STT_FUNC, 0, isec);
+ addSymbol("$a", STT_NOTYPE, 0, isec);
}
-void ThumbV7ABSLongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
0x60, 0x47, // bx ip
};
- uint64_t S = getARMThunkDestVA(Destination);
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S);
- Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S);
+ uint64_t s = getARMThunkDestVA(destination);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_ARM_THM_MOVW_ABS_NC, s);
+ target->relocateOne(buf + 4, R_ARM_THM_MOVT_ABS, s);
}
-void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()),
- STT_FUNC, 1, IS);
- addSymbol("$t", STT_NOTYPE, 0, IS);
+void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__Thumbv7ABSLongThunk_" + destination.getName()),
+ STT_FUNC, 1, isec);
+ addSymbol("$t", STT_NOTYPE, 0, isec);
}
-void ARMV7PILongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ARMV7PILongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8)
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8)
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
0x1c, 0xff, 0x2f, 0xe1, // bx ip
};
- uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA();
- int64_t Offset = S - P - 16;
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
- Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset);
+ uint64_t s = getARMThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA();
+ int64_t offset = s - p - 16;
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_ARM_MOVW_PREL_NC, offset);
+ target->relocateOne(buf + 4, R_ARM_MOVT_PREL, offset);
}
-void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
- 0, IS);
- addSymbol("$a", STT_NOTYPE, 0, IS);
+void ARMV7PILongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ARMV7PILongThunk_" + destination.getName()), STT_FUNC,
+ 0, isec);
+ addSymbol("$a", STT_NOTYPE, 0, isec);
}
-void ThumbV7PILongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ThumbV7PILongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
0xfc, 0x44, // L1: add ip, pc
0x60, 0x47, // bx ip
};
- uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
- int64_t Offset = S - P - 12;
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
- Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset);
+ uint64_t s = getARMThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
+ int64_t offset = s - p - 12;
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_ARM_THM_MOVW_PREL_NC, offset);
+ target->relocateOne(buf + 4, R_ARM_THM_MOVT_PREL, offset);
}
-void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()),
- STT_FUNC, 1, IS);
- addSymbol("$t", STT_NOTYPE, 0, IS);
+void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ThumbV7PILongThunk_" + destination.getName()),
+ STT_FUNC, 1, isec);
+ addSymbol("$t", STT_NOTYPE, 0, isec);
}
-void ARMV5ABSLongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ARMV5ABSLongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1
0x00, 0x00, 0x00, 0x00, // L1: .word S
};
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf + 4, R_ARM_ABS32, getARMThunkDestVA(Destination));
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
}
-void ARMV5ABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ARMv5ABSLongThunk_" + Destination.getName()),
- STT_FUNC, 0, IS);
- addSymbol("$a", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 4, IS);
+void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ARMv5ABSLongThunk_" + destination.getName()),
+ STT_FUNC, 0, isec);
+ addSymbol("$a", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 4, isec);
}
-bool ARMV5ABSLongThunk::isCompatibleWith(uint32_t RelocType) const {
+bool ARMV5ABSLongThunk::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
// Thumb branch relocations can't use BLX
- return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
+ return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
}
-void ARMV5PILongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ARMV5PILongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x04, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc,#4] ; L2
0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
0x1c, 0xff, 0x2f, 0xe1, // bx ip
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
};
- uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf + 12, R_ARM_REL32, S - P - 12);
+ uint64_t s = getARMThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12);
}
-void ARMV5PILongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ARMV5PILongThunk_" + Destination.getName()), STT_FUNC,
- 0, IS);
- addSymbol("$a", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 12, IS);
+void ARMV5PILongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ARMV5PILongThunk_" + destination.getName()), STT_FUNC,
+ 0, isec);
+ addSymbol("$a", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 12, isec);
}
-bool ARMV5PILongThunk::isCompatibleWith(uint32_t RelocType) const {
+bool ARMV5PILongThunk::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
// Thumb branch relocations can't use BLX
- return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
+ return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
}
-void ThumbV6MABSLongThunk::writeLong(uint8_t *Buf) {
+void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) {
// Most Thumb instructions cannot access the high registers r8 - r15. As the
// only register we can corrupt is r12 we must instead spill a low register
// to the stack to use as a scratch register. We push r1 even though we
// don't need to get some space to use for the return address.
- const uint8_t Data[] = {
+ const uint8_t data[] = {
0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers
0x01, 0x48, // ldr r0, [pc, #4] ; L1
0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S
0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest
0x00, 0x00, 0x00, 0x00 // L1: .word S
};
- uint64_t S = getARMThunkDestVA(Destination);
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf + 8, R_ARM_ABS32, S);
+ uint64_t s = getARMThunkDestVA(destination);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf + 8, R_ARM_ABS32, s);
}
-void ThumbV6MABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__Thumbv6MABSLongThunk_" + Destination.getName()),
- STT_FUNC, 1, IS);
- addSymbol("$t", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 8, IS);
+void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__Thumbv6MABSLongThunk_" + destination.getName()),
+ STT_FUNC, 1, isec);
+ addSymbol("$t", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 8, isec);
}
-void ThumbV6MPILongThunk::writeLong(uint8_t *Buf) {
+void ThumbV6MPILongThunk::writeLong(uint8_t *buf) {
// Most Thumb instructions cannot access the high registers r8 - r15. As the
// only register we can corrupt is ip (r12) we must instead spill a low
// register to the stack to use as a scratch register.
- const uint8_t Data[] = {
+ const uint8_t data[] = {
0x01, 0xb4, // P: push {r0} ; Obtain scratch register
0x02, 0x48, // ldr r0, [pc, #8] ; L2
0x84, 0x46, // mov ip, r0 ; high to low register
@@ -600,131 +626,185 @@ void ThumbV6MPILongThunk::writeLong(uint8_t *Buf) {
0xc0, 0x46, // nop ; pad to 4-byte boundary
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4)
};
- uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf + 12, R_ARM_REL32, S - P - 12);
+ uint64_t s = getARMThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12);
}
-void ThumbV6MPILongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__Thumbv6MPILongThunk_" + Destination.getName()),
- STT_FUNC, 1, IS);
- addSymbol("$t", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 12, IS);
+void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__Thumbv6MPILongThunk_" + destination.getName()),
+ STT_FUNC, 1, isec);
+ addSymbol("$t", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 12, isec);
}
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
-void MipsThunk::writeTo(uint8_t *Buf) {
- uint64_t S = Destination.getVA();
- write32(Buf, 0x3c190000); // lui $25, %hi(func)
- write32(Buf + 4, 0x08000000 | (S >> 2)); // j func
- write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
- write32(Buf + 12, 0x00000000); // nop
- Target->relocateOne(Buf, R_MIPS_HI16, S);
- Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
+void MipsThunk::writeTo(uint8_t *buf) {
+ uint64_t s = destination.getVA();
+ write32(buf, 0x3c190000); // lui $25, %hi(func)
+ write32(buf + 4, 0x08000000 | (s >> 2)); // j func
+ write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
+ write32(buf + 12, 0x00000000); // nop
+ target->relocateOne(buf, R_MIPS_HI16, s);
+ target->relocateOne(buf + 8, R_MIPS_LO16, s);
}
-void MipsThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0,
- IS);
+void MipsThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0,
+ isec);
}
InputSection *MipsThunk::getTargetInputSection() const {
- auto &DR = cast<Defined>(Destination);
- return dyn_cast<InputSection>(DR.Section);
+ auto &dr = cast<Defined>(destination);
+ return dyn_cast<InputSection>(dr.section);
}
// Write microMIPS R2-R5 LA25 thunk code
// to call PIC function from the non-PIC one.
-void MicroMipsThunk::writeTo(uint8_t *Buf) {
- uint64_t S = Destination.getVA() | 1;
- write16(Buf, 0x41b9); // lui $25, %hi(func)
- write16(Buf + 4, 0xd400); // j func
- write16(Buf + 8, 0x3339); // addiu $25, $25, %lo(func)
- write16(Buf + 12, 0x0c00); // nop
- Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
- Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S);
- Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S);
+void MicroMipsThunk::writeTo(uint8_t *buf) {
+ uint64_t s = destination.getVA();
+ write16(buf, 0x41b9); // lui $25, %hi(func)
+ write16(buf + 4, 0xd400); // j func
+ write16(buf + 8, 0x3339); // addiu $25, $25, %lo(func)
+ write16(buf + 12, 0x0c00); // nop
+ target->relocateOne(buf, R_MICROMIPS_HI16, s);
+ target->relocateOne(buf + 4, R_MICROMIPS_26_S1, s);
+ target->relocateOne(buf + 8, R_MICROMIPS_LO16, s);
}
-void MicroMipsThunk::addSymbols(ThunkSection &IS) {
- Defined *D = addSymbol(
- Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
- D->StOther |= STO_MIPS_MICROMIPS;
+void MicroMipsThunk::addSymbols(ThunkSection &isec) {
+ Defined *d = addSymbol(
+ saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec);
+ d->stOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsThunk::getTargetInputSection() const {
- auto &DR = cast<Defined>(Destination);
- return dyn_cast<InputSection>(DR.Section);
+ auto &dr = cast<Defined>(destination);
+ return dyn_cast<InputSection>(dr.section);
}
// Write microMIPS R6 LA25 thunk code
// to call PIC function from the non-PIC one.
-void MicroMipsR6Thunk::writeTo(uint8_t *Buf) {
- uint64_t S = Destination.getVA() | 1;
- uint64_t P = getThunkTargetSym()->getVA();
- write16(Buf, 0x1320); // lui $25, %hi(func)
- write16(Buf + 4, 0x3339); // addiu $25, $25, %lo(func)
- write16(Buf + 8, 0x9400); // bc func
- Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
- Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S);
- Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12);
+void MicroMipsR6Thunk::writeTo(uint8_t *buf) {
+ uint64_t s = destination.getVA();
+ uint64_t p = getThunkTargetSym()->getVA();
+ write16(buf, 0x1320); // lui $25, %hi(func)
+ write16(buf + 4, 0x3339); // addiu $25, $25, %lo(func)
+ write16(buf + 8, 0x9400); // bc func
+ target->relocateOne(buf, R_MICROMIPS_HI16, s);
+ target->relocateOne(buf + 4, R_MICROMIPS_LO16, s);
+ target->relocateOne(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12);
}
-void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
- Defined *D = addSymbol(
- Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
- D->StOther |= STO_MIPS_MICROMIPS;
+void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) {
+ Defined *d = addSymbol(
+ saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec);
+ d->stOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
- auto &DR = cast<Defined>(Destination);
- return dyn_cast<InputSection>(DR.Section);
+ auto &dr = cast<Defined>(destination);
+ return dyn_cast<InputSection>(dr.section);
}
-static void writePPCLoadAndBranch(uint8_t *Buf, int64_t Offset) {
- uint16_t OffHa = (Offset + 0x8000) >> 16;
- uint16_t OffLo = Offset & 0xffff;
+void PPC32PltCallStub::writeTo(uint8_t *buf) {
+ if (!config->isPic) {
+ uint64_t va = destination.getGotPltVA();
+ write32(buf + 0, 0x3d600000 | (va + 0x8000) >> 16); // lis r11,ha
+ write32(buf + 4, 0x816b0000 | (uint16_t)va); // lwz r11,l(r11)
+ write32(buf + 8, 0x7d6903a6); // mtctr r11
+ write32(buf + 12, 0x4e800420); // bctr
+ return;
+ }
+ uint32_t offset;
+ if (addend >= 0x8000) {
+ // The stub loads an address relative to r30 (.got2+Addend). Addend is
+ // almost always 0x8000. The address of .got2 is different in another object
+ // file, so a stub cannot be shared.
+ offset = destination.getGotPltVA() - (in.ppc32Got2->getParent()->getVA() +
+ file->ppc32Got2OutSecOff + addend);
+ } else {
+ // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is
+ // currently the address of .got).
+ offset = destination.getGotPltVA() - in.got->getVA();
+ }
+ uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset;
+ if (ha == 0) {
+ write32(buf + 0, 0x817e0000 | l); // lwz r11,l(r30)
+ write32(buf + 4, 0x7d6903a6); // mtctr r11
+ write32(buf + 8, 0x4e800420); // bctr
+ write32(buf + 12, 0x60000000); // nop
+ } else {
+ write32(buf + 0, 0x3d7e0000 | ha); // addis r11,r30,ha
+ write32(buf + 4, 0x816b0000 | l); // lwz r11,l(r11)
+ write32(buf + 8, 0x7d6903a6); // mtctr r11
+ write32(buf + 12, 0x4e800420); // bctr
+ }
+}
+
+void PPC32PltCallStub::addSymbols(ThunkSection &isec) {
+ std::string buf;
+ raw_string_ostream os(buf);
+ os << format_hex_no_prefix(addend, 8);
+ if (!config->isPic)
+ os << ".plt_call32.";
+ else if (addend >= 0x8000)
+ os << ".got2.plt_pic32.";
+ else
+ os << ".plt_pic32.";
+ os << destination.getName();
+ addSymbol(saver.save(os.str()), STT_FUNC, 0, isec);
+}
+
+bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
+ return !config->isPic || (isec.file == file && rel.addend == addend);
+}
- write32(Buf + 0, 0x3d820000 | OffHa); // addis r12, r2, OffHa
- write32(Buf + 4, 0xe98c0000 | OffLo); // ld r12, OffLo(r12)
- write32(Buf + 8, 0x7d8903a6); // mtctr r12
- write32(Buf + 12, 0x4e800420); // bctr
+static void writePPCLoadAndBranch(uint8_t *buf, int64_t offset) {
+ uint16_t offHa = (offset + 0x8000) >> 16;
+ uint16_t offLo = offset & 0xffff;
+
+ write32(buf + 0, 0x3d820000 | offHa); // addis r12, r2, OffHa
+ write32(buf + 4, 0xe98c0000 | offLo); // ld r12, OffLo(r12)
+ write32(buf + 8, 0x7d8903a6); // mtctr r12
+ write32(buf + 12, 0x4e800420); // bctr
}
-void PPC64PltCallStub::writeTo(uint8_t *Buf) {
- int64_t Offset = Destination.getGotPltVA() - getPPC64TocBase();
+void PPC64PltCallStub::writeTo(uint8_t *buf) {
+ int64_t offset = destination.getGotPltVA() - getPPC64TocBase();
// Save the TOC pointer to the save-slot reserved in the call frame.
- write32(Buf + 0, 0xf8410018); // std r2,24(r1)
- writePPCLoadAndBranch(Buf + 4, Offset);
+ write32(buf + 0, 0xf8410018); // std r2,24(r1)
+ writePPCLoadAndBranch(buf + 4, offset);
}
-void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
- Defined *S = addSymbol(Saver.save("__plt_" + Destination.getName()), STT_FUNC,
- 0, IS);
- S->NeedsTocRestore = true;
+void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
+ Defined *s = addSymbol(saver.save("__plt_" + destination.getName()), STT_FUNC,
+ 0, isec);
+ s->needsTocRestore = true;
}
-void PPC64LongBranchThunk::writeTo(uint8_t *Buf) {
- int64_t Offset = Destination.getPPC64LongBranchTableVA() - getPPC64TocBase();
- writePPCLoadAndBranch(Buf, Offset);
+void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
+ int64_t offset = destination.getPPC64LongBranchTableVA() - getPPC64TocBase();
+ writePPCLoadAndBranch(buf, offset);
}
-void PPC64LongBranchThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__long_branch_" + Destination.getName()), STT_FUNC, 0,
- IS);
+void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__long_branch_" + destination.getName()), STT_FUNC, 0,
+ isec);
}
-Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
+Thunk::Thunk(Symbol &d) : destination(d), offset(0) {}
Thunk::~Thunk() = default;
-static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
- if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
+static Thunk *addThunkAArch64(RelType type, Symbol &s) {
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
fatal("unrecognized relocation type");
- if (Config->Pic)
- return make<AArch64ADRPThunk>(S);
- return make<AArch64ABSLongThunk>(S);
+ if (config->picThunk)
+ return make<AArch64ADRPThunk>(s);
+ return make<AArch64ABSLongThunk>(s);
}
// Creates a thunk for Thumb-ARM interworking.
@@ -732,18 +812,18 @@ static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
// - MOVT and MOVW instructions cannot be used
// - Only Thumb relocation that can generate a Thunk is a BL, this can always
// be transformed into a BLX
-static Thunk *addThunkPreArmv7(RelType Reloc, Symbol &S) {
- switch (Reloc) {
+static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s) {
+ switch (reloc) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
case R_ARM_CALL:
case R_ARM_THM_CALL:
- if (Config->Pic)
- return make<ARMV5PILongThunk>(S);
- return make<ARMV5ABSLongThunk>(S);
+ if (config->picThunk)
+ return make<ARMV5PILongThunk>(s);
+ return make<ARMV5ABSLongThunk>(s);
}
- fatal("relocation " + toString(Reloc) + " to " + toString(S) +
+ fatal("relocation " + toString(reloc) + " to " + toString(s) +
" not supported for Armv5 or Armv6 targets");
}
@@ -752,21 +832,21 @@ static Thunk *addThunkPreArmv7(RelType Reloc, Symbol &S) {
// - MOVT and MOVW instructions cannot be used.
// - Only a limited number of instructions can access registers r8 and above
// - No interworking support is needed (all Thumb).
-static Thunk *addThunkV6M(RelType Reloc, Symbol &S) {
- switch (Reloc) {
+static Thunk *addThunkV6M(RelType reloc, Symbol &s) {
+ switch (reloc) {
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
case R_ARM_THM_CALL:
- if (Config->Pic)
- return make<ThumbV6MPILongThunk>(S);
- return make<ThumbV6MABSLongThunk>(S);
+ if (config->isPic)
+ return make<ThumbV6MPILongThunk>(s);
+ return make<ThumbV6MABSLongThunk>(s);
}
- fatal("relocation " + toString(Reloc) + " to " + toString(S) +
+ fatal("relocation " + toString(reloc) + " to " + toString(s) +
" not supported for Armv6-M targets");
}
// Creates a thunk for Thumb-ARM interworking or branch range extension.
-static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
+static Thunk *addThunkArm(RelType reloc, Symbol &s) {
// Decide which Thunk is needed based on:
// Available instruction set
// - An Arm Thunk can only be used if Arm state is available.
@@ -783,61 +863,72 @@ static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
// can use in Thunks. The flags below are set by reading the BuildAttributes
// of the input objects. InputFiles.cpp contains the mapping from ARM
// architecture to flag.
- if (!Config->ARMHasMovtMovw) {
- if (!Config->ARMJ1J2BranchEncoding)
- return addThunkPreArmv7(Reloc, S);
- return addThunkV6M(Reloc, S);
+ if (!config->armHasMovtMovw) {
+ if (!config->armJ1J2BranchEncoding)
+ return addThunkPreArmv7(reloc, s);
+ return addThunkV6M(reloc, s);
}
- switch (Reloc) {
+ switch (reloc) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
case R_ARM_CALL:
- if (Config->Pic)
- return make<ARMV7PILongThunk>(S);
- return make<ARMV7ABSLongThunk>(S);
+ if (config->picThunk)
+ return make<ARMV7PILongThunk>(s);
+ return make<ARMV7ABSLongThunk>(s);
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
case R_ARM_THM_CALL:
- if (Config->Pic)
- return make<ThumbV7PILongThunk>(S);
- return make<ThumbV7ABSLongThunk>(S);
+ if (config->picThunk)
+ return make<ThumbV7PILongThunk>(s);
+ return make<ThumbV7ABSLongThunk>(s);
}
fatal("unrecognized relocation type");
}
-static Thunk *addThunkMips(RelType Type, Symbol &S) {
- if ((S.StOther & STO_MIPS_MICROMIPS) && isMipsR6())
- return make<MicroMipsR6Thunk>(S);
- if (S.StOther & STO_MIPS_MICROMIPS)
- return make<MicroMipsThunk>(S);
- return make<MipsThunk>(S);
+static Thunk *addThunkMips(RelType type, Symbol &s) {
+ if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6())
+ return make<MicroMipsR6Thunk>(s);
+ if (s.stOther & STO_MIPS_MICROMIPS)
+ return make<MicroMipsThunk>(s);
+ return make<MipsThunk>(s);
+}
+
+static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel, Symbol &s) {
+ assert((rel.type == R_PPC_REL24 || rel.type == R_PPC_PLTREL24) &&
+ "unexpected relocation type for thunk");
+ return make<PPC32PltCallStub>(isec, rel, s);
}
-static Thunk *addThunkPPC64(RelType Type, Symbol &S) {
- assert(Type == R_PPC64_REL24 && "unexpected relocation type for thunk");
- if (S.isInPlt())
- return make<PPC64PltCallStub>(S);
+static Thunk *addThunkPPC64(RelType type, Symbol &s) {
+ assert(type == R_PPC64_REL24 && "unexpected relocation type for thunk");
+ if (s.isInPlt())
+ return make<PPC64PltCallStub>(s);
- if (Config->Pic)
- return make<PPC64PILongBranchThunk>(S);
+ if (config->picThunk)
+ return make<PPC64PILongBranchThunk>(s);
- return make<PPC64PDLongBranchThunk>(S);
+ return make<PPC64PDLongBranchThunk>(s);
}
-Thunk *addThunk(RelType Type, Symbol &S) {
- if (Config->EMachine == EM_AARCH64)
- return addThunkAArch64(Type, S);
+Thunk *addThunk(const InputSection &isec, Relocation &rel) {
+ Symbol &s = *rel.sym;
+
+ if (config->emachine == EM_AARCH64)
+ return addThunkAArch64(rel.type, s);
+
+ if (config->emachine == EM_ARM)
+ return addThunkArm(rel.type, s);
- if (Config->EMachine == EM_ARM)
- return addThunkArm(Type, S);
+ if (config->emachine == EM_MIPS)
+ return addThunkMips(rel.type, s);
- if (Config->EMachine == EM_MIPS)
- return addThunkMips(Type, S);
+ if (config->emachine == EM_PPC)
+ return addThunkPPC32(isec, rel, s);
- if (Config->EMachine == EM_PPC64)
- return addThunkPPC64(Type, S);
+ if (config->emachine == EM_PPC64)
+ return addThunkPPC64(rel.type, s);
llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
}
diff --git a/ELF/Thunks.h b/ELF/Thunks.h
index ed82b4d946ac2..2d27ee5f6c38e 100644
--- a/ELF/Thunks.h
+++ b/ELF/Thunks.h
@@ -1,9 +1,8 @@
//===- Thunks.h --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -28,42 +27,45 @@ class ThunkSection;
// Thunks are assigned to synthetic ThunkSections
class Thunk {
public:
- Thunk(Symbol &Destination);
+ Thunk(Symbol &destination);
virtual ~Thunk();
virtual uint32_t size() = 0;
- virtual void writeTo(uint8_t *Buf) = 0;
+ virtual void writeTo(uint8_t *buf) = 0;
// All Thunks must define at least one symbol, known as the thunk target
// symbol, so that we can redirect relocations to it. The thunk may define
// additional symbols, but these are never targets for relocations.
- virtual void addSymbols(ThunkSection &IS) = 0;
+ virtual void addSymbols(ThunkSection &isec) = 0;
- void setOffset(uint64_t Offset);
- Defined *addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
- InputSectionBase &Section);
+ void setOffset(uint64_t offset);
+ Defined *addSymbol(StringRef name, uint8_t type, uint64_t value,
+ InputSectionBase &section);
// Some Thunks must be placed immediately before their Target as they elide
// a branch and fall through to the first Symbol in the Target.
virtual InputSection *getTargetInputSection() const { return nullptr; }
- // To reuse a Thunk the caller as identified by the Type must be
- // compatible with it.
- virtual bool isCompatibleWith(RelType Type) const { return true; }
+ // To reuse a Thunk the InputSection and the relocation must be compatible
+ // with it.
+ virtual bool isCompatibleWith(const InputSection &,
+ const Relocation &) const {
+ return true;
+ }
- Defined *getThunkTargetSym() const { return Syms[0]; }
+ Defined *getThunkTargetSym() const { return syms[0]; }
// The alignment requirement for this Thunk, defaults to the size of the
// typical code section alignment.
- Symbol &Destination;
- llvm::SmallVector<Defined *, 3> Syms;
- uint64_t Offset = 0;
- uint32_t Alignment = 4;
+ Symbol &destination;
+ llvm::SmallVector<Defined *, 3> syms;
+ uint64_t offset = 0;
+ uint32_t alignment = 4;
};
// For a Relocation to symbol S create a Thunk to be added to a synthetic
-// ThunkSection. At present there are implementations for ARM and Mips Thunks.
-Thunk *addThunk(RelType Type, Symbol &S);
+// ThunkSection.
+Thunk *addThunk(const InputSection &isec, Relocation &rel);
} // namespace elf
} // namespace lld
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 17f4c7961d30e..b8c8891648a46 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -1,9 +1,8 @@
//===- Writer.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -11,7 +10,6 @@
#include "AArch64ErrataFix.h"
#include "CallGraphSort.h"
#include "Config.h"
-#include "Filesystem.h"
#include "LinkerScript.h"
#include "MapFile.h"
#include "OutputSections.h"
@@ -20,11 +18,15 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Support/SHA1.h"
+#include "llvm/Support/xxhash.h"
#include <climits>
using namespace llvm;
@@ -40,31 +42,32 @@ namespace {
// The writer writes a SymbolTable result to a file.
template <class ELFT> class Writer {
public:
- Writer() : Buffer(errorHandler().OutputBuffer) {}
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Ehdr Elf_Ehdr;
- typedef typename ELFT::Phdr Elf_Phdr;
+ Writer() : buffer(errorHandler().outputBuffer) {}
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Ehdr = typename ELFT::Ehdr;
+ using Elf_Phdr = typename ELFT::Phdr;
void run();
private:
void copyLocalSymbols();
void addSectionSymbols();
- void forEachRelSec(llvm::function_ref<void(InputSectionBase &)> Fn);
+ void forEachRelSec(llvm::function_ref<void(InputSectionBase &)> fn);
void sortSections();
void resolveShfLinkOrder();
- void maybeAddThunks();
+ void finalizeAddressDependentContent();
void sortInputSections();
void finalizeSections();
void checkExecuteOnly();
void setReservedSymbolSections();
- std::vector<PhdrEntry *> createPhdrs();
- void removeEmptyPTLoad();
- void addPtArmExid(std::vector<PhdrEntry *> &Phdrs);
+ std::vector<PhdrEntry *> createPhdrs(Partition &part);
+ void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrEntry);
+ void addPhdrForSection(Partition &part, unsigned shType, unsigned pType,
+ unsigned pFlags);
void assignFileOffsets();
void assignFileOffsetsBinary();
- void setPhdrs();
+ void setPhdrs(Partition &part);
void checkSections();
void fixSectionAlignments();
void openFile();
@@ -74,36 +77,34 @@ private:
void writeSectionsBinary();
void writeBuildId();
- std::unique_ptr<FileOutputBuffer> &Buffer;
+ std::unique_ptr<FileOutputBuffer> &buffer;
void addRelIpltSymbols();
void addStartEndSymbols();
- void addStartStopSymbols(OutputSection *Sec);
-
- std::vector<PhdrEntry *> Phdrs;
+ void addStartStopSymbols(OutputSection *sec);
- uint64_t FileSize;
- uint64_t SectionHeaderOff;
+ uint64_t fileSize;
+ uint64_t sectionHeaderOff;
};
} // anonymous namespace
-static bool isSectionPrefix(StringRef Prefix, StringRef Name) {
- return Name.startswith(Prefix) || Name == Prefix.drop_back();
+static bool isSectionPrefix(StringRef prefix, StringRef name) {
+ return name.startswith(prefix) || name == prefix.drop_back();
}
-StringRef elf::getOutputSectionName(const InputSectionBase *S) {
- if (Config->Relocatable)
- return S->Name;
+StringRef elf::getOutputSectionName(const InputSectionBase *s) {
+ if (config->relocatable)
+ return s->name;
// This is for --emit-relocs. If .text.foo is emitted as .text.bar, we want
// to emit .rela.text.foo as .rela.text.bar for consistency (this is not
// technically required, but not doing it is odd). This code guarantees that.
- if (auto *IS = dyn_cast<InputSection>(S)) {
- if (InputSectionBase *Rel = IS->getRelocatedSection()) {
- OutputSection *Out = Rel->getOutputSection();
- if (S->Type == SHT_RELA)
- return Saver.save(".rela" + Out->Name);
- return Saver.save(".rel" + Out->Name);
+ if (auto *isec = dyn_cast<InputSection>(s)) {
+ if (InputSectionBase *rel = isec->getRelocatedSection()) {
+ OutputSection *out = rel->getOutputSection();
+ if (s->type == SHT_RELA)
+ return saver.save(".rela" + out->name);
+ return saver.save(".rel" + out->name);
}
}
@@ -113,97 +114,133 @@ StringRef elf::getOutputSectionName(const InputSectionBase *S) {
// When enabled, this allows identifying the hot code region (.text.hot) in
// the final binary which can be selectively mapped to huge pages or mlocked,
// for instance.
- if (Config->ZKeepTextSectionPrefix)
- for (StringRef V :
+ if (config->zKeepTextSectionPrefix)
+ for (StringRef v :
{".text.hot.", ".text.unlikely.", ".text.startup.", ".text.exit."})
- if (isSectionPrefix(V, S->Name))
- return V.drop_back();
+ if (isSectionPrefix(v, s->name))
+ return v.drop_back();
- for (StringRef V :
+ for (StringRef v :
{".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.",
".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.",
".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab."})
- if (isSectionPrefix(V, S->Name))
- return V.drop_back();
+ if (isSectionPrefix(v, s->name))
+ return v.drop_back();
// CommonSection is identified as "COMMON" in linker scripts.
// By default, it should go to .bss section.
- if (S->Name == "COMMON")
+ if (s->name == "COMMON")
return ".bss";
- return S->Name;
+ return s->name;
}
static bool needsInterpSection() {
- return !SharedFiles.empty() && !Config->DynamicLinker.empty() &&
- Script->needsInterpSection();
+ return !sharedFiles.empty() && !config->dynamicLinker.empty() &&
+ script->needsInterpSection();
}
template <class ELFT> void elf::writeResult() { Writer<ELFT>().run(); }
-template <class ELFT> void Writer<ELFT>::removeEmptyPTLoad() {
- llvm::erase_if(Phdrs, [&](const PhdrEntry *P) {
- if (P->p_type != PT_LOAD)
+template <class ELFT>
+void Writer<ELFT>::removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
+ llvm::erase_if(phdrs, [&](const PhdrEntry *p) {
+ if (p->p_type != PT_LOAD)
return false;
- if (!P->FirstSec)
+ if (!p->firstSec)
return true;
- uint64_t Size = P->LastSec->Addr + P->LastSec->Size - P->FirstSec->Addr;
- return Size == 0;
+ uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr;
+ return size == 0;
});
}
-template <class ELFT> static void combineEhFrameSections() {
- for (InputSectionBase *&S : InputSections) {
- EhInputSection *ES = dyn_cast<EhInputSection>(S);
- if (!ES || !ES->Live)
+template <class ELFT> static void copySectionsIntoPartitions() {
+ std::vector<InputSectionBase *> newSections;
+ for (unsigned part = 2; part != partitions.size() + 1; ++part) {
+ for (InputSectionBase *s : inputSections) {
+ if (!(s->flags & SHF_ALLOC) || !s->isLive())
+ continue;
+ InputSectionBase *copy;
+ if (s->type == SHT_NOTE)
+ copy = make<InputSection>(cast<InputSection>(*s));
+ else if (auto *es = dyn_cast<EhInputSection>(s))
+ copy = make<EhInputSection>(*es);
+ else
+ continue;
+ copy->partition = part;
+ newSections.push_back(copy);
+ }
+ }
+
+ inputSections.insert(inputSections.end(), newSections.begin(),
+ newSections.end());
+}
+
+template <class ELFT> static void combineEhSections() {
+ for (InputSectionBase *&s : inputSections) {
+ // Ignore dead sections and the partition end marker (.part.end),
+ // whose partition number is out of bounds.
+ if (!s->isLive() || s->partition == 255)
continue;
- In.EhFrame->addSection<ELFT>(ES);
- S = nullptr;
+ Partition &part = s->getPartition();
+ if (auto *es = dyn_cast<EhInputSection>(s)) {
+ part.ehFrame->addSection<ELFT>(es);
+ s = nullptr;
+ } else if (s->kind() == SectionBase::Regular && part.armExidx &&
+ part.armExidx->addSection(cast<InputSection>(s))) {
+ s = nullptr;
+ }
}
- std::vector<InputSectionBase *> &V = InputSections;
- V.erase(std::remove(V.begin(), V.end(), nullptr), V.end());
+ std::vector<InputSectionBase *> &v = inputSections;
+ v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
}
-static Defined *addOptionalRegular(StringRef Name, SectionBase *Sec,
- uint64_t Val, uint8_t StOther = STV_HIDDEN,
- uint8_t Binding = STB_GLOBAL) {
- Symbol *S = Symtab->find(Name);
- if (!S || S->isDefined())
+static Defined *addOptionalRegular(StringRef name, SectionBase *sec,
+ uint64_t val, uint8_t stOther = STV_HIDDEN,
+ uint8_t binding = STB_GLOBAL) {
+ Symbol *s = symtab->find(name);
+ if (!s || s->isDefined())
return nullptr;
- return Symtab->addDefined(Name, StOther, STT_NOTYPE, Val,
- /*Size=*/0, Binding, Sec,
- /*File=*/nullptr);
+
+ s->resolve(Defined{/*file=*/nullptr, name, binding, stOther, STT_NOTYPE, val,
+ /*size=*/0, sec});
+ return cast<Defined>(s);
}
-static Defined *addAbsolute(StringRef Name) {
- return Symtab->addDefined(Name, STV_HIDDEN, STT_NOTYPE, 0, 0, STB_GLOBAL,
- nullptr, nullptr);
+static Defined *addAbsolute(StringRef name) {
+ Symbol *sym = symtab->addSymbol(Defined{nullptr, name, STB_GLOBAL, STV_HIDDEN,
+ STT_NOTYPE, 0, 0, nullptr});
+ return cast<Defined>(sym);
}
// The linker is expected to define some symbols depending on
// the linking result. This function defines such symbols.
void elf::addReservedSymbols() {
- if (Config->EMachine == EM_MIPS) {
+ if (config->emachine == EM_MIPS) {
// Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
// so that it points to an absolute address which by default is relative
// to GOT. Default offset is 0x7ff0.
// See "Global Data Symbols" in Chapter 6 in the following document:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- ElfSym::MipsGp = addAbsolute("_gp");
+ ElfSym::mipsGp = addAbsolute("_gp");
// On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
// start of function and 'gp' pointer into GOT.
- if (Symtab->find("_gp_disp"))
- ElfSym::MipsGpDisp = addAbsolute("_gp_disp");
+ if (symtab->find("_gp_disp"))
+ ElfSym::mipsGpDisp = addAbsolute("_gp_disp");
// The __gnu_local_gp is a magic symbol equal to the current value of 'gp'
// pointer. This symbol is used in the code generated by .cpload pseudo-op
// in case of using -mno-shared option.
// https://sourceware.org/ml/binutils/2004-12/msg00094.html
- if (Symtab->find("__gnu_local_gp"))
- ElfSym::MipsLocalGp = addAbsolute("__gnu_local_gp");
+ if (symtab->find("__gnu_local_gp"))
+ ElfSym::mipsLocalGp = addAbsolute("__gnu_local_gp");
+ } else if (config->emachine == EM_PPC) {
+ // glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't
+ // support Small Data Area, define it arbitrarily as 0.
+ addOptionalRegular("_SDA_BASE_", nullptr, 0, STV_HIDDEN);
}
// The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which
@@ -214,56 +251,62 @@ void elf::addReservedSymbols() {
// the .got section.
// We do not allow _GLOBAL_OFFSET_TABLE_ to be defined by input objects as the
// correctness of some relocations depends on its value.
- StringRef GotTableSymName =
- (Config->EMachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_";
- if (Symbol *S = Symtab->find(GotTableSymName)) {
- if (S->isDefined())
- error(toString(S->File) + " cannot redefine linker defined symbol '" +
- GotTableSymName + "'");
- else
- ElfSym::GlobalOffsetTable = Symtab->addDefined(
- GotTableSymName, STV_HIDDEN, STT_NOTYPE, Target->GotBaseSymOff,
- /*Size=*/0, STB_GLOBAL, Out::ElfHeader,
- /*File=*/nullptr);
+ StringRef gotSymName =
+ (config->emachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_";
+
+ if (Symbol *s = symtab->find(gotSymName)) {
+ if (s->isDefined()) {
+ error(toString(s->file) + " cannot redefine linker defined symbol '" +
+ gotSymName + "'");
+ return;
+ }
+
+ uint64_t gotOff = 0;
+ if (config->emachine == EM_PPC64)
+ gotOff = 0x8000;
+
+ s->resolve(Defined{/*file=*/nullptr, gotSymName, STB_GLOBAL, STV_HIDDEN,
+ STT_NOTYPE, gotOff, /*size=*/0, Out::elfHeader});
+ ElfSym::globalOffsetTable = cast<Defined>(s);
}
// __ehdr_start is the location of ELF file headers. Note that we define
// this symbol unconditionally even when using a linker script, which
// differs from the behavior implemented by GNU linker which only define
// this symbol if ELF headers are in the memory mapped segment.
- addOptionalRegular("__ehdr_start", Out::ElfHeader, 0, STV_HIDDEN);
+ addOptionalRegular("__ehdr_start", Out::elfHeader, 0, STV_HIDDEN);
// __executable_start is not documented, but the expectation of at
// least the Android libc is that it points to the ELF header.
- addOptionalRegular("__executable_start", Out::ElfHeader, 0, STV_HIDDEN);
+ addOptionalRegular("__executable_start", Out::elfHeader, 0, STV_HIDDEN);
// __dso_handle symbol is passed to cxa_finalize as a marker to identify
// each DSO. The address of the symbol doesn't matter as long as they are
// different in different DSOs, so we chose the start address of the DSO.
- addOptionalRegular("__dso_handle", Out::ElfHeader, 0, STV_HIDDEN);
+ addOptionalRegular("__dso_handle", Out::elfHeader, 0, STV_HIDDEN);
// If linker script do layout we do not need to create any standart symbols.
- if (Script->HasSectionsCommand)
+ if (script->hasSectionsCommand)
return;
- auto Add = [](StringRef S, int64_t Pos) {
- return addOptionalRegular(S, Out::ElfHeader, Pos, STV_DEFAULT);
+ auto add = [](StringRef s, int64_t pos) {
+ return addOptionalRegular(s, Out::elfHeader, pos, STV_DEFAULT);
};
- ElfSym::Bss = Add("__bss_start", 0);
- ElfSym::End1 = Add("end", -1);
- ElfSym::End2 = Add("_end", -1);
- ElfSym::Etext1 = Add("etext", -1);
- ElfSym::Etext2 = Add("_etext", -1);
- ElfSym::Edata1 = Add("edata", -1);
- ElfSym::Edata2 = Add("_edata", -1);
+ ElfSym::bss = add("__bss_start", 0);
+ ElfSym::end1 = add("end", -1);
+ ElfSym::end2 = add("_end", -1);
+ ElfSym::etext1 = add("etext", -1);
+ ElfSym::etext2 = add("_etext", -1);
+ ElfSym::edata1 = add("edata", -1);
+ ElfSym::edata2 = add("_edata", -1);
}
-static OutputSection *findSection(StringRef Name) {
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- if (Sec->Name == Name)
- return Sec;
+static OutputSection *findSection(StringRef name, unsigned partition = 1) {
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ if (sec->name == name && sec->partition == partition)
+ return sec;
return nullptr;
}
@@ -271,202 +314,263 @@ static OutputSection *findSection(StringRef Name) {
template <class ELFT> static void createSyntheticSections() {
// Initialize all pointers with NULL. This is needed because
// you can call lld::elf::main more than once as a library.
- memset(&Out::First, 0, sizeof(Out));
+ memset(&Out::first, 0, sizeof(Out));
- auto Add = [](InputSectionBase *Sec) { InputSections.push_back(Sec); };
+ auto add = [](InputSectionBase *sec) { inputSections.push_back(sec); };
- In.DynStrTab = make<StringTableSection>(".dynstr", true);
- In.Dynamic = make<DynamicSection<ELFT>>();
- if (Config->AndroidPackDynRelocs) {
- In.RelaDyn = make<AndroidPackedRelocationSection<ELFT>>(
- Config->IsRela ? ".rela.dyn" : ".rel.dyn");
- } else {
- In.RelaDyn = make<RelocationSection<ELFT>>(
- Config->IsRela ? ".rela.dyn" : ".rel.dyn", Config->ZCombreloc);
- }
- In.ShStrTab = make<StringTableSection>(".shstrtab", false);
+ in.shStrTab = make<StringTableSection>(".shstrtab", false);
- Out::ProgramHeaders = make<OutputSection>("", 0, SHF_ALLOC);
- Out::ProgramHeaders->Alignment = Config->Wordsize;
+ Out::programHeaders = make<OutputSection>("", 0, SHF_ALLOC);
+ Out::programHeaders->alignment = config->wordsize;
- if (needsInterpSection()) {
- In.Interp = createInterpSection();
- Add(In.Interp);
+ if (config->strip != StripPolicy::All) {
+ in.strTab = make<StringTableSection>(".strtab", false);
+ in.symTab = make<SymbolTableSection<ELFT>>(*in.strTab);
+ in.symTabShndx = make<SymtabShndxSection>();
}
- if (Config->Strip != StripPolicy::All) {
- In.StrTab = make<StringTableSection>(".strtab", false);
- In.SymTab = make<SymbolTableSection<ELFT>>(*In.StrTab);
- In.SymTabShndx = make<SymtabShndxSection>();
- }
-
- if (Config->BuildId != BuildIdKind::None) {
- In.BuildId = make<BuildIdSection>();
- Add(In.BuildId);
- }
-
- In.Bss = make<BssSection>(".bss", 0, 1);
- Add(In.Bss);
+ in.bss = make<BssSection>(".bss", 0, 1);
+ add(in.bss);
// If there is a SECTIONS command and a .data.rel.ro section name use name
// .data.rel.ro.bss so that we match in the .data.rel.ro output section.
// This makes sure our relro is contiguous.
- bool HasDataRelRo = Script->HasSectionsCommand && findSection(".data.rel.ro");
- In.BssRelRo =
- make<BssSection>(HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
- Add(In.BssRelRo);
+ bool hasDataRelRo =
+ script->hasSectionsCommand && findSection(".data.rel.ro", 0);
+ in.bssRelRo =
+ make<BssSection>(hasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
+ add(in.bssRelRo);
// Add MIPS-specific sections.
- if (Config->EMachine == EM_MIPS) {
- if (!Config->Shared && Config->HasDynSymTab) {
- In.MipsRldMap = make<MipsRldMapSection>();
- Add(In.MipsRldMap);
+ if (config->emachine == EM_MIPS) {
+ if (!config->shared && config->hasDynSymTab) {
+ in.mipsRldMap = make<MipsRldMapSection>();
+ add(in.mipsRldMap);
}
- if (auto *Sec = MipsAbiFlagsSection<ELFT>::create())
- Add(Sec);
- if (auto *Sec = MipsOptionsSection<ELFT>::create())
- Add(Sec);
- if (auto *Sec = MipsReginfoSection<ELFT>::create())
- Add(Sec);
+ if (auto *sec = MipsAbiFlagsSection<ELFT>::create())
+ add(sec);
+ if (auto *sec = MipsOptionsSection<ELFT>::create())
+ add(sec);
+ if (auto *sec = MipsReginfoSection<ELFT>::create())
+ add(sec);
}
- if (Config->HasDynSymTab) {
- In.DynSymTab = make<SymbolTableSection<ELFT>>(*In.DynStrTab);
- Add(In.DynSymTab);
+ for (Partition &part : partitions) {
+ auto add = [&](InputSectionBase *sec) {
+ sec->partition = part.getNumber();
+ inputSections.push_back(sec);
+ };
+
+ if (!part.name.empty()) {
+ part.elfHeader = make<PartitionElfHeaderSection<ELFT>>();
+ part.elfHeader->name = part.name;
+ add(part.elfHeader);
- InX<ELFT>::VerSym = make<VersionTableSection<ELFT>>();
- Add(InX<ELFT>::VerSym);
+ part.programHeaders = make<PartitionProgramHeadersSection<ELFT>>();
+ add(part.programHeaders);
+ }
- if (!Config->VersionDefinitions.empty()) {
- In.VerDef = make<VersionDefinitionSection>();
- Add(In.VerDef);
+ if (config->buildId != BuildIdKind::None) {
+ part.buildId = make<BuildIdSection>();
+ add(part.buildId);
}
- InX<ELFT>::VerNeed = make<VersionNeedSection<ELFT>>();
- Add(InX<ELFT>::VerNeed);
+ part.dynStrTab = make<StringTableSection>(".dynstr", true);
+ part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
+ part.dynamic = make<DynamicSection<ELFT>>();
+ if (config->androidPackDynRelocs) {
+ part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>(
+ config->isRela ? ".rela.dyn" : ".rel.dyn");
+ } else {
+ part.relaDyn = make<RelocationSection<ELFT>>(
+ config->isRela ? ".rela.dyn" : ".rel.dyn", config->zCombreloc);
+ }
+
+ if (needsInterpSection())
+ add(createInterpSection());
+
+ if (config->hasDynSymTab) {
+ part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
+ add(part.dynSymTab);
+
+ part.verSym = make<VersionTableSection>();
+ add(part.verSym);
+
+ if (!config->versionDefinitions.empty()) {
+ part.verDef = make<VersionDefinitionSection>();
+ add(part.verDef);
+ }
+
+ part.verNeed = make<VersionNeedSection<ELFT>>();
+ add(part.verNeed);
+
+ if (config->gnuHash) {
+ part.gnuHashTab = make<GnuHashTableSection>();
+ add(part.gnuHashTab);
+ }
+
+ if (config->sysvHash) {
+ part.hashTab = make<HashTableSection>();
+ add(part.hashTab);
+ }
- if (Config->GnuHash) {
- In.GnuHashTab = make<GnuHashTableSection>();
- Add(In.GnuHashTab);
+ add(part.dynamic);
+ add(part.dynStrTab);
+ add(part.relaDyn);
}
- if (Config->SysvHash) {
- In.HashTab = make<HashTableSection>();
- Add(In.HashTab);
+ if (config->relrPackDynRelocs) {
+ part.relrDyn = make<RelrSection<ELFT>>();
+ add(part.relrDyn);
}
- Add(In.Dynamic);
- Add(In.DynStrTab);
- Add(In.RelaDyn);
+ if (!config->relocatable) {
+ if (config->ehFrameHdr) {
+ part.ehFrameHdr = make<EhFrameHeader>();
+ add(part.ehFrameHdr);
+ }
+ part.ehFrame = make<EhFrameSection>();
+ add(part.ehFrame);
+ }
+
+ if (config->emachine == EM_ARM && !config->relocatable) {
+ // The ARMExidxsyntheticsection replaces all the individual .ARM.exidx
+ // InputSections.
+ part.armExidx = make<ARMExidxSyntheticSection>();
+ add(part.armExidx);
+ }
}
- if (Config->RelrPackDynRelocs) {
- In.RelrDyn = make<RelrSection<ELFT>>();
- Add(In.RelrDyn);
+ if (partitions.size() != 1) {
+ // Create the partition end marker. This needs to be in partition number 255
+ // so that it is sorted after all other partitions. It also has other
+ // special handling (see createPhdrs() and combineEhSections()).
+ in.partEnd = make<BssSection>(".part.end", config->maxPageSize, 1);
+ in.partEnd->partition = 255;
+ add(in.partEnd);
+
+ in.partIndex = make<PartitionIndexSection>();
+ addOptionalRegular("__part_index_begin", in.partIndex, 0);
+ addOptionalRegular("__part_index_end", in.partIndex,
+ in.partIndex->getSize());
+ add(in.partIndex);
}
// Add .got. MIPS' .got is so different from the other archs,
// it has its own class.
- if (Config->EMachine == EM_MIPS) {
- In.MipsGot = make<MipsGotSection>();
- Add(In.MipsGot);
+ if (config->emachine == EM_MIPS) {
+ in.mipsGot = make<MipsGotSection>();
+ add(in.mipsGot);
} else {
- In.Got = make<GotSection>();
- Add(In.Got);
+ in.got = make<GotSection>();
+ add(in.got);
+ }
+
+ if (config->emachine == EM_PPC) {
+ in.ppc32Got2 = make<PPC32Got2Section>();
+ add(in.ppc32Got2);
}
- if (Config->EMachine == EM_PPC64) {
- In.PPC64LongBranchTarget = make<PPC64LongBranchTargetSection>();
- Add(In.PPC64LongBranchTarget);
+ if (config->emachine == EM_PPC64) {
+ in.ppc64LongBranchTarget = make<PPC64LongBranchTargetSection>();
+ add(in.ppc64LongBranchTarget);
}
- In.GotPlt = make<GotPltSection>();
- Add(In.GotPlt);
- In.IgotPlt = make<IgotPltSection>();
- Add(In.IgotPlt);
+ if (config->emachine == EM_RISCV) {
+ in.riscvSdata = make<RISCVSdataSection>();
+ add(in.riscvSdata);
+ }
+
+ in.gotPlt = make<GotPltSection>();
+ add(in.gotPlt);
+ in.igotPlt = make<IgotPltSection>();
+ add(in.igotPlt);
- if (Config->GdbIndex) {
- In.GdbIndex = GdbIndexSection::create<ELFT>();
- Add(In.GdbIndex);
+ // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat
+ // it as a relocation and ensure the referenced section is created.
+ if (ElfSym::globalOffsetTable && config->emachine != EM_MIPS) {
+ if (target->gotBaseSymInGotPlt)
+ in.gotPlt->hasGotPltOffRel = true;
+ else
+ in.got->hasGotOffRel = true;
}
+ if (config->gdbIndex)
+ add(GdbIndexSection::create<ELFT>());
+
// We always need to add rel[a].plt to output if it has entries.
// Even for static linking it can contain R_[*]_IRELATIVE relocations.
- In.RelaPlt = make<RelocationSection<ELFT>>(
- Config->IsRela ? ".rela.plt" : ".rel.plt", false /*Sort*/);
- Add(In.RelaPlt);
+ in.relaPlt = make<RelocationSection<ELFT>>(
+ config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false);
+ add(in.relaPlt);
- // The RelaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure
+ // The relaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure
// that the IRelative relocations are processed last by the dynamic loader.
// We cannot place the iplt section in .rel.dyn when Android relocation
// packing is enabled because that would cause a section type mismatch.
// However, because the Android dynamic loader reads .rel.plt after .rel.dyn,
// we can get the desired behaviour by placing the iplt section in .rel.plt.
- In.RelaIplt = make<RelocationSection<ELFT>>(
- (Config->EMachine == EM_ARM && !Config->AndroidPackDynRelocs)
+ in.relaIplt = make<RelocationSection<ELFT>>(
+ (config->emachine == EM_ARM && !config->androidPackDynRelocs)
? ".rel.dyn"
- : In.RelaPlt->Name,
- false /*Sort*/);
- Add(In.RelaIplt);
+ : in.relaPlt->name,
+ /*sort=*/false);
+ add(in.relaIplt);
+
+ in.plt = make<PltSection>(false);
+ add(in.plt);
+ in.iplt = make<PltSection>(true);
+ add(in.iplt);
- In.Plt = make<PltSection>(false);
- Add(In.Plt);
- In.Iplt = make<PltSection>(true);
- Add(In.Iplt);
+ if (config->andFeatures)
+ add(make<GnuPropertySection>());
// .note.GNU-stack is always added when we are creating a re-linkable
// object file. Other linkers are using the presence of this marker
// section to control the executable-ness of the stack area, but that
// is irrelevant these days. Stack area should always be non-executable
// by default. So we emit this section unconditionally.
- if (Config->Relocatable)
- Add(make<GnuStackSection>());
-
- if (!Config->Relocatable) {
- if (Config->EhFrameHdr) {
- In.EhFrameHdr = make<EhFrameHeader>();
- Add(In.EhFrameHdr);
- }
- In.EhFrame = make<EhFrameSection>();
- Add(In.EhFrame);
- }
-
- if (In.SymTab)
- Add(In.SymTab);
- if (In.SymTabShndx)
- Add(In.SymTabShndx);
- Add(In.ShStrTab);
- if (In.StrTab)
- Add(In.StrTab);
+ if (config->relocatable)
+ add(make<GnuStackSection>());
- if (Config->EMachine == EM_ARM && !Config->Relocatable)
- // Add a sentinel to terminate .ARM.exidx. It helps an unwinder
- // to find the exact address range of the last entry.
- Add(make<ARMExidxSentinelSection>());
+ if (in.symTab)
+ add(in.symTab);
+ if (in.symTabShndx)
+ add(in.symTabShndx);
+ add(in.shStrTab);
+ if (in.strTab)
+ add(in.strTab);
}
// The main function of the writer.
template <class ELFT> void Writer<ELFT>::run() {
+ // Make copies of any input sections that need to be copied into each
+ // partition.
+ copySectionsIntoPartitions<ELFT>();
+
// Create linker-synthesized sections such as .got or .plt.
// Such sections are of type input section.
createSyntheticSections<ELFT>();
- if (!Config->Relocatable)
- combineEhFrameSections<ELFT>();
+ // Some input sections that are used for exception handling need to be moved
+ // into synthetic sections. Do that now so that they aren't assigned to
+ // output sections in the usual way.
+ if (!config->relocatable)
+ combineEhSections<ELFT>();
// We want to process linker script commands. When SECTIONS command
// is given we let it create sections.
- Script->processSectionCommands();
+ script->processSectionCommands();
// Linker scripts controls how input sections are assigned to output sections.
// Input sections that were not handled by scripts are called "orphans", and
// they are assigned to output sections by the default rule. Process that.
- Script->addOrphanSections();
+ script->addOrphanSections();
- if (Config->Discard != DiscardPolicy::All)
+ if (config->discard != DiscardPolicy::All)
copyLocalSymbols();
- if (Config->CopyRelocs)
+ if (config->copyRelocs)
addSectionSymbols();
// Now that we have a complete set of output sections. This function
@@ -478,33 +582,35 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
- Script->assignAddresses();
+ script->assignAddresses();
// If -compressed-debug-sections is specified, we need to compress
// .debug_* sections. Do it right now because it changes the size of
// output sections.
- for (OutputSection *Sec : OutputSections)
- Sec->maybeCompress<ELFT>();
+ for (OutputSection *sec : outputSections)
+ sec->maybeCompress<ELFT>();
- Script->allocateHeaders(Phdrs);
+ script->allocateHeaders(mainPart->phdrs);
// Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
// 0 sized region. This has to be done late since only after assignAddresses
// we know the size of the sections.
- removeEmptyPTLoad();
+ for (Partition &part : partitions)
+ removeEmptyPTLoad(part.phdrs);
- if (!Config->OFormatBinary)
+ if (!config->oFormatBinary)
assignFileOffsets();
else
assignFileOffsetsBinary();
- setPhdrs();
+ for (Partition &part : partitions)
+ setPhdrs(part);
- if (Config->Relocatable)
- for (OutputSection *Sec : OutputSections)
- Sec->Addr = 0;
+ if (config->relocatable)
+ for (OutputSection *sec : outputSections)
+ sec->addr = 0;
- if (Config->CheckSections)
+ if (config->checkSections)
checkSections();
// It does not make sense try to open the file if we have error already.
@@ -515,7 +621,7 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
- if (!Config->OFormatBinary) {
+ if (!config->oFormatBinary) {
writeTrapInstr();
writeHeader();
writeSections();
@@ -535,16 +641,20 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
- if (auto E = Buffer->commit())
- error("failed to write to the output file: " + toString(std::move(E)));
+ if (auto e = buffer->commit())
+ error("failed to write to the output file: " + toString(std::move(e)));
}
-static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName,
- const Symbol &B) {
- if (B.isSection())
+static bool shouldKeepInSymtab(const Defined &sym) {
+ if (sym.isSection())
return false;
- if (Config->Discard == DiscardPolicy::None)
+ if (config->discard == DiscardPolicy::None)
+ return true;
+
+ // If -emit-reloc is given, all symbols including local ones need to be
+ // copied because they may be referenced by relocations.
+ if (config->emitRelocs)
return true;
// In ELF assembly .L symbols are normally discarded by the assembler.
@@ -552,61 +662,62 @@ static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName,
// * --discard-locals is used.
// * The symbol is in a SHF_MERGE section, which is normally the reason for
// the assembler keeping the .L symbol.
- if (!SymName.startswith(".L") && !SymName.empty())
+ StringRef name = sym.getName();
+ bool isLocal = name.startswith(".L") || name.empty();
+ if (!isLocal)
return true;
- if (Config->Discard == DiscardPolicy::Locals)
+ if (config->discard == DiscardPolicy::Locals)
return false;
- return !Sec || !(Sec->Flags & SHF_MERGE);
+ SectionBase *sec = sym.section;
+ return !sec || !(sec->flags & SHF_MERGE);
}
-static bool includeInSymtab(const Symbol &B) {
- if (!B.isLocal() && !B.IsUsedInRegularObj)
+static bool includeInSymtab(const Symbol &b) {
+ if (!b.isLocal() && !b.isUsedInRegularObj)
return false;
- if (auto *D = dyn_cast<Defined>(&B)) {
+ if (auto *d = dyn_cast<Defined>(&b)) {
// Always include absolute symbols.
- SectionBase *Sec = D->Section;
- if (!Sec)
+ SectionBase *sec = d->section;
+ if (!sec)
return true;
- Sec = Sec->Repl;
+ sec = sec->repl;
// Exclude symbols pointing to garbage-collected sections.
- if (isa<InputSectionBase>(Sec) && !Sec->Live)
+ if (isa<InputSectionBase>(sec) && !sec->isLive())
return false;
- if (auto *S = dyn_cast<MergeInputSection>(Sec))
- if (!S->getSectionPiece(D->Value)->Live)
+ if (auto *s = dyn_cast<MergeInputSection>(sec))
+ if (!s->getSectionPiece(d->value)->live)
return false;
return true;
}
- return B.Used;
+ return b.used;
}
// Local symbols are not in the linker's symbol table. This function scans
// each object file's symbol table to copy local symbols to the output.
template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
- if (!In.SymTab)
+ if (!in.symTab)
return;
- for (InputFile *File : ObjectFiles) {
- ObjFile<ELFT> *F = cast<ObjFile<ELFT>>(File);
- for (Symbol *B : F->getLocalSymbols()) {
- if (!B->isLocal())
- fatal(toString(F) +
+ for (InputFile *file : objectFiles) {
+ ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
+ for (Symbol *b : f->getLocalSymbols()) {
+ if (!b->isLocal())
+ fatal(toString(f) +
": broken object: getLocalSymbols returns a non-local symbol");
- auto *DR = dyn_cast<Defined>(B);
+ auto *dr = dyn_cast<Defined>(b);
// No reason to keep local undefined symbol in symtab.
- if (!DR)
+ if (!dr)
continue;
- if (!includeInSymtab(*B))
+ if (!includeInSymtab(*b))
continue;
-
- SectionBase *Sec = DR->Section;
- if (!shouldKeepInSymtab(Sec, B->getName(), *B))
+ if (!shouldKeepInSymtab(*dr))
continue;
- In.SymTab->addSymbol(B);
+ in.symTab->addSymbol(b);
}
}
}
@@ -616,33 +727,33 @@ template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
// referring to a section (that happens if the section is a synthetic one), we
// don't create a section symbol for that section.
template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
- for (BaseCommand *Base : Script->SectionCommands) {
- auto *Sec = dyn_cast<OutputSection>(Base);
- if (!Sec)
+ for (BaseCommand *base : script->sectionCommands) {
+ auto *sec = dyn_cast<OutputSection>(base);
+ if (!sec)
continue;
- auto I = llvm::find_if(Sec->SectionCommands, [](BaseCommand *Base) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
- return !ISD->Sections.empty();
+ auto i = llvm::find_if(sec->sectionCommands, [](BaseCommand *base) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ return !isd->sections.empty();
return false;
});
- if (I == Sec->SectionCommands.end())
+ if (i == sec->sectionCommands.end())
continue;
- InputSection *IS = cast<InputSectionDescription>(*I)->Sections[0];
+ InputSection *isec = cast<InputSectionDescription>(*i)->sections[0];
// Relocations are not using REL[A] section symbols.
- if (IS->Type == SHT_REL || IS->Type == SHT_RELA)
+ if (isec->type == SHT_REL || isec->type == SHT_RELA)
continue;
// Unlike other synthetic sections, mergeable output sections contain data
// copied from input sections, and there may be a relocation pointing to its
// contents if -r or -emit-reloc are given.
- if (isa<SyntheticSection>(IS) && !(IS->Flags & SHF_MERGE))
+ if (isa<SyntheticSection>(isec) && !(isec->flags & SHF_MERGE))
continue;
- auto *Sym =
- make<Defined>(IS->File, "", STB_LOCAL, /*StOther=*/0, STT_SECTION,
- /*Value=*/0, /*Size=*/0, IS);
- In.SymTab->addSymbol(Sym);
+ auto *sym =
+ make<Defined>(isec->file, "", STB_LOCAL, /*stOther=*/0, STT_SECTION,
+ /*value=*/0, /*size=*/0, isec);
+ in.symTab->addSymbol(sym);
}
}
@@ -652,25 +763,25 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
//
// This function returns true if a section needs to be put into a
// PT_GNU_RELRO segment.
-static bool isRelroSection(const OutputSection *Sec) {
- if (!Config->ZRelro)
+static bool isRelroSection(const OutputSection *sec) {
+ if (!config->zRelro)
return false;
- uint64_t Flags = Sec->Flags;
+ uint64_t flags = sec->flags;
// Non-allocatable or non-writable sections don't need RELRO because
// they are not writable or not even mapped to memory in the first place.
// RELRO is for sections that are essentially read-only but need to
// be writable only at process startup to allow dynamic linker to
// apply relocations.
- if (!(Flags & SHF_ALLOC) || !(Flags & SHF_WRITE))
+ if (!(flags & SHF_ALLOC) || !(flags & SHF_WRITE))
return false;
// Once initialized, TLS data segments are used as data templates
// for a thread-local storage. For each new thread, runtime
// allocates memory for a TLS and copy templates there. No thread
// are supposed to use templates directly. Thus, it can be in RELRO.
- if (Flags & SHF_TLS)
+ if (flags & SHF_TLS)
return true;
// .init_array, .preinit_array and .fini_array contain pointers to
@@ -679,15 +790,15 @@ static bool isRelroSection(const OutputSection *Sec) {
// to change at runtime. But if you are an attacker, you could do
// interesting things by manipulating pointers in .fini_array, for
// example. So they are put into RELRO.
- uint32_t Type = Sec->Type;
- if (Type == SHT_INIT_ARRAY || Type == SHT_FINI_ARRAY ||
- Type == SHT_PREINIT_ARRAY)
+ uint32_t type = sec->type;
+ if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY ||
+ type == SHT_PREINIT_ARRAY)
return true;
// .got contains pointers to external symbols. They are resolved by
// the dynamic linker when a module is loaded into memory, and after
// that they are not expected to change. So, it can be in RELRO.
- if (In.Got && Sec == In.Got->getParent())
+ if (in.got && sec == in.got->getParent())
return true;
// .toc is a GOT-ish section for PowerPC64. Their contents are accessed
@@ -695,30 +806,30 @@ static bool isRelroSection(const OutputSection *Sec) {
// for accessing .got as well, .got and .toc need to be close enough in the
// virtual address space. Usually, .toc comes just after .got. Since we place
// .got into RELRO, .toc needs to be placed into RELRO too.
- if (Sec->Name.equals(".toc"))
+ if (sec->name.equals(".toc"))
return true;
// .got.plt contains pointers to external function symbols. They are
// by default resolved lazily, so we usually cannot put it into RELRO.
// However, if "-z now" is given, the lazy symbol resolution is
// disabled, which enables us to put it into RELRO.
- if (Sec == In.GotPlt->getParent())
- return Config->ZNow;
+ if (sec == in.gotPlt->getParent())
+ return config->zNow;
// .dynamic section contains data for the dynamic linker, and
// there's no need to write to it at runtime, so it's better to put
// it into RELRO.
- if (Sec == In.Dynamic->getParent())
+ if (sec->name == ".dynamic")
return true;
// Sections with some special names are put into RELRO. This is a
// bit unfortunate because section names shouldn't be significant in
// ELF in spirit. But in reality many linker features depend on
// magic section names.
- StringRef S = Sec->Name;
- return S == ".data.rel.ro" || S == ".bss.rel.ro" || S == ".ctors" ||
- S == ".dtors" || S == ".jcr" || S == ".eh_frame" ||
- S == ".openbsd.randomdata";
+ StringRef s = sec->name;
+ return s == ".data.rel.ro" || s == ".bss.rel.ro" || s == ".ctors" ||
+ s == ".dtors" || s == ".jcr" || s == ".eh_frame" ||
+ s == ".openbsd.randomdata";
}
// We compute a rank for each section. The rank indicates where the
@@ -729,16 +840,18 @@ static bool isRelroSection(const OutputSection *Sec) {
// * It is easy to check if a give branch was taken.
// * It is easy two see how similar two ranks are (see getRankProximity).
enum RankFlags {
- RF_NOT_ADDR_SET = 1 << 18,
- RF_NOT_ALLOC = 1 << 17,
- RF_NOT_INTERP = 1 << 16,
- RF_NOT_NOTE = 1 << 15,
- RF_WRITE = 1 << 14,
- RF_EXEC_WRITE = 1 << 13,
- RF_EXEC = 1 << 12,
- RF_RODATA = 1 << 11,
- RF_NON_TLS_BSS = 1 << 10,
- RF_NON_TLS_BSS_RO = 1 << 9,
+ RF_NOT_ADDR_SET = 1 << 27,
+ RF_NOT_ALLOC = 1 << 26,
+ RF_PARTITION = 1 << 18, // Partition number (8 bits)
+ RF_NOT_PART_EHDR = 1 << 17,
+ RF_NOT_PART_PHDR = 1 << 16,
+ RF_NOT_INTERP = 1 << 15,
+ RF_NOT_NOTE = 1 << 14,
+ RF_WRITE = 1 << 13,
+ RF_EXEC_WRITE = 1 << 12,
+ RF_EXEC = 1 << 11,
+ RF_RODATA = 1 << 10,
+ RF_NOT_RELRO = 1 << 9,
RF_NOT_TLS = 1 << 8,
RF_BSS = 1 << 7,
RF_PPC_NOT_TOCBSS = 1 << 6,
@@ -750,33 +863,41 @@ enum RankFlags {
RF_MIPS_NOT_GOT = 1 << 0
};
-static unsigned getSectionRank(const OutputSection *Sec) {
- unsigned Rank = 0;
+static unsigned getSectionRank(const OutputSection *sec) {
+ unsigned rank = sec->partition * RF_PARTITION;
// We want to put section specified by -T option first, so we
// can start assigning VA starting from them later.
- if (Config->SectionStartMap.count(Sec->Name))
- return Rank;
- Rank |= RF_NOT_ADDR_SET;
+ if (config->sectionStartMap.count(sec->name))
+ return rank;
+ rank |= RF_NOT_ADDR_SET;
// Allocatable sections go first to reduce the total PT_LOAD size and
// so debug info doesn't change addresses in actual code.
- if (!(Sec->Flags & SHF_ALLOC))
- return Rank | RF_NOT_ALLOC;
+ if (!(sec->flags & SHF_ALLOC))
+ return rank | RF_NOT_ALLOC;
+
+ if (sec->type == SHT_LLVM_PART_EHDR)
+ return rank;
+ rank |= RF_NOT_PART_EHDR;
+
+ if (sec->type == SHT_LLVM_PART_PHDR)
+ return rank;
+ rank |= RF_NOT_PART_PHDR;
// Put .interp first because some loaders want to see that section
// on the first page of the executable file when loaded into memory.
- if (Sec->Name == ".interp")
- return Rank;
- Rank |= RF_NOT_INTERP;
+ if (sec->name == ".interp")
+ return rank;
+ rank |= RF_NOT_INTERP;
// Put .note sections (which make up one PT_NOTE) at the beginning so that
// they are likely to be included in a core file even if core file size is
// limited. In particular, we want a .note.gnu.build-id and a .note.tag to be
// included in a core to match core files with executables.
- if (Sec->Type == SHT_NOTE)
- return Rank;
- Rank |= RF_NOT_NOTE;
+ if (sec->type == SHT_NOTE)
+ return rank;
+ rank |= RF_NOT_NOTE;
// Sort sections based on their access permission in the following
// order: R, RX, RWX, RW. This order is based on the following
@@ -789,116 +910,105 @@ static unsigned getSectionRank(const OutputSection *Sec) {
// between .text and .data.
// * Writable sections come last, such that .bss lands at the very
// end of the last PT_LOAD.
- bool IsExec = Sec->Flags & SHF_EXECINSTR;
- bool IsWrite = Sec->Flags & SHF_WRITE;
+ bool isExec = sec->flags & SHF_EXECINSTR;
+ bool isWrite = sec->flags & SHF_WRITE;
- if (IsExec) {
- if (IsWrite)
- Rank |= RF_EXEC_WRITE;
+ if (isExec) {
+ if (isWrite)
+ rank |= RF_EXEC_WRITE;
else
- Rank |= RF_EXEC;
- } else if (IsWrite) {
- Rank |= RF_WRITE;
- } else if (Sec->Type == SHT_PROGBITS) {
+ rank |= RF_EXEC;
+ } else if (isWrite) {
+ rank |= RF_WRITE;
+ } else if (sec->type == SHT_PROGBITS) {
// Make non-executable and non-writable PROGBITS sections (e.g .rodata
// .eh_frame) closer to .text. They likely contain PC or GOT relative
// relocations and there could be relocation overflow if other huge sections
// (.dynstr .dynsym) were placed in between.
- Rank |= RF_RODATA;
+ rank |= RF_RODATA;
}
- // If we got here we know that both A and B are in the same PT_LOAD.
+ // Place RelRo sections first. After considering SHT_NOBITS below, the
+ // ordering is PT_LOAD(PT_GNU_RELRO(.data.rel.ro .bss.rel.ro) | .data .bss),
+ // where | marks where page alignment happens. An alternative ordering is
+ // PT_LOAD(.data | PT_GNU_RELRO( .data.rel.ro .bss.rel.ro) | .bss), but it may
+ // waste more bytes due to 2 alignment places.
+ if (!isRelroSection(sec))
+ rank |= RF_NOT_RELRO;
- bool IsTls = Sec->Flags & SHF_TLS;
- bool IsNoBits = Sec->Type == SHT_NOBITS;
-
- // The first requirement we have is to put (non-TLS) nobits sections last. The
- // reason is that the only thing the dynamic linker will see about them is a
- // p_memsz that is larger than p_filesz. Seeing that it zeros the end of the
- // PT_LOAD, so that has to correspond to the nobits sections.
- bool IsNonTlsNoBits = IsNoBits && !IsTls;
- if (IsNonTlsNoBits)
- Rank |= RF_NON_TLS_BSS;
-
- // We place nobits RelRo sections before plain r/w ones, and non-nobits RelRo
- // sections after r/w ones, so that the RelRo sections are contiguous.
- bool IsRelRo = isRelroSection(Sec);
- if (IsNonTlsNoBits && !IsRelRo)
- Rank |= RF_NON_TLS_BSS_RO;
- if (!IsNonTlsNoBits && IsRelRo)
- Rank |= RF_NON_TLS_BSS_RO;
+ // If we got here we know that both A and B are in the same PT_LOAD.
// The TLS initialization block needs to be a single contiguous block in a R/W
// PT_LOAD, so stick TLS sections directly before the other RelRo R/W
- // sections. The TLS NOBITS sections are placed here as they don't take up
- // virtual address space in the PT_LOAD.
- if (!IsTls)
- Rank |= RF_NOT_TLS;
+ // sections. Since p_filesz can be less than p_memsz, place NOBITS sections
+ // after PROGBITS.
+ if (!(sec->flags & SHF_TLS))
+ rank |= RF_NOT_TLS;
- // Within the TLS initialization block, the non-nobits sections need to appear
- // first.
- if (IsNoBits)
- Rank |= RF_BSS;
+ // Within TLS sections, or within other RelRo sections, or within non-RelRo
+ // sections, place non-NOBITS sections first.
+ if (sec->type == SHT_NOBITS)
+ rank |= RF_BSS;
// Some architectures have additional ordering restrictions for sections
// within the same PT_LOAD.
- if (Config->EMachine == EM_PPC64) {
+ if (config->emachine == EM_PPC64) {
// PPC64 has a number of special SHT_PROGBITS+SHF_ALLOC+SHF_WRITE sections
// that we would like to make sure appear is a specific order to maximize
// their coverage by a single signed 16-bit offset from the TOC base
// pointer. Conversely, the special .tocbss section should be first among
// all SHT_NOBITS sections. This will put it next to the loaded special
// PPC64 sections (and, thus, within reach of the TOC base pointer).
- StringRef Name = Sec->Name;
- if (Name != ".tocbss")
- Rank |= RF_PPC_NOT_TOCBSS;
+ StringRef name = sec->name;
+ if (name != ".tocbss")
+ rank |= RF_PPC_NOT_TOCBSS;
- if (Name == ".toc1")
- Rank |= RF_PPC_TOCL;
+ if (name == ".toc1")
+ rank |= RF_PPC_TOCL;
- if (Name == ".toc")
- Rank |= RF_PPC_TOC;
+ if (name == ".toc")
+ rank |= RF_PPC_TOC;
- if (Name == ".got")
- Rank |= RF_PPC_GOT;
+ if (name == ".got")
+ rank |= RF_PPC_GOT;
- if (Name == ".branch_lt")
- Rank |= RF_PPC_BRANCH_LT;
+ if (name == ".branch_lt")
+ rank |= RF_PPC_BRANCH_LT;
}
- if (Config->EMachine == EM_MIPS) {
+ if (config->emachine == EM_MIPS) {
// All sections with SHF_MIPS_GPREL flag should be grouped together
// because data in these sections is addressable with a gp relative address.
- if (Sec->Flags & SHF_MIPS_GPREL)
- Rank |= RF_MIPS_GPREL;
+ if (sec->flags & SHF_MIPS_GPREL)
+ rank |= RF_MIPS_GPREL;
- if (Sec->Name != ".got")
- Rank |= RF_MIPS_NOT_GOT;
+ if (sec->name != ".got")
+ rank |= RF_MIPS_NOT_GOT;
}
- return Rank;
+ return rank;
}
-static bool compareSections(const BaseCommand *ACmd, const BaseCommand *BCmd) {
- const OutputSection *A = cast<OutputSection>(ACmd);
- const OutputSection *B = cast<OutputSection>(BCmd);
+static bool compareSections(const BaseCommand *aCmd, const BaseCommand *bCmd) {
+ const OutputSection *a = cast<OutputSection>(aCmd);
+ const OutputSection *b = cast<OutputSection>(bCmd);
- if (A->SortRank != B->SortRank)
- return A->SortRank < B->SortRank;
+ if (a->sortRank != b->sortRank)
+ return a->sortRank < b->sortRank;
- if (!(A->SortRank & RF_NOT_ADDR_SET))
- return Config->SectionStartMap.lookup(A->Name) <
- Config->SectionStartMap.lookup(B->Name);
+ if (!(a->sortRank & RF_NOT_ADDR_SET))
+ return config->sectionStartMap.lookup(a->name) <
+ config->sectionStartMap.lookup(b->name);
return false;
}
-void PhdrEntry::add(OutputSection *Sec) {
- LastSec = Sec;
- if (!FirstSec)
- FirstSec = Sec;
- p_align = std::max(p_align, Sec->Alignment);
+void PhdrEntry::add(OutputSection *sec) {
+ lastSec = sec;
+ if (!firstSec)
+ firstSec = sec;
+ p_align = std::max(p_align, sec->alignment);
if (p_type == PT_LOAD)
- Sec->PtLoad = this;
+ sec->ptLoad = this;
}
// The beginning and the ending of .rel[a].plt section are marked
@@ -908,35 +1018,40 @@ void PhdrEntry::add(OutputSection *Sec) {
// need these symbols, since IRELATIVE relocs are resolved through GOT
// and PLT. For details, see http://www.airs.com/blog/archives/403.
template <class ELFT> void Writer<ELFT>::addRelIpltSymbols() {
- if (Config->Relocatable || needsInterpSection())
+ if (config->relocatable || needsInterpSection())
return;
// By default, __rela_iplt_{start,end} belong to a dummy section 0
// because .rela.plt might be empty and thus removed from output.
- // We'll override Out::ElfHeader with In.RelaIplt later when we are
+ // We'll override Out::elfHeader with In.relaIplt later when we are
// sure that .rela.plt exists in output.
- ElfSym::RelaIpltStart = addOptionalRegular(
- Config->IsRela ? "__rela_iplt_start" : "__rel_iplt_start",
- Out::ElfHeader, 0, STV_HIDDEN, STB_WEAK);
+ ElfSym::relaIpltStart = addOptionalRegular(
+ config->isRela ? "__rela_iplt_start" : "__rel_iplt_start",
+ Out::elfHeader, 0, STV_HIDDEN, STB_WEAK);
- ElfSym::RelaIpltEnd = addOptionalRegular(
- Config->IsRela ? "__rela_iplt_end" : "__rel_iplt_end",
- Out::ElfHeader, 0, STV_HIDDEN, STB_WEAK);
+ ElfSym::relaIpltEnd = addOptionalRegular(
+ config->isRela ? "__rela_iplt_end" : "__rel_iplt_end",
+ Out::elfHeader, 0, STV_HIDDEN, STB_WEAK);
}
template <class ELFT>
void Writer<ELFT>::forEachRelSec(
- llvm::function_ref<void(InputSectionBase &)> Fn) {
+ llvm::function_ref<void(InputSectionBase &)> fn) {
// Scan all relocations. Each relocation goes through a series
// of tests to determine if it needs special treatment, such as
// creating GOT, PLT, copy relocations, etc.
// Note that relocations for non-alloc sections are directly
// processed by InputSection::relocateNonAlloc.
- for (InputSectionBase *IS : InputSections)
- if (IS->Live && isa<InputSection>(IS) && (IS->Flags & SHF_ALLOC))
- Fn(*IS);
- for (EhInputSection *ES : In.EhFrame->Sections)
- Fn(*ES);
+ for (InputSectionBase *isec : inputSections)
+ if (isec->isLive() && isa<InputSection>(isec) && (isec->flags & SHF_ALLOC))
+ fn(*isec);
+ for (Partition &part : partitions) {
+ for (EhInputSection *es : part.ehFrame->sections)
+ fn(*es);
+ if (part.armExidx && part.armExidx->isLive())
+ for (InputSection *ex : part.armExidx->exidxSections)
+ fn(*ex);
+ }
}
// This function generates assignments for predefined symbols (e.g. _end or
@@ -945,76 +1060,78 @@ void Writer<ELFT>::forEachRelSec(
// time any references to these symbols are processed and is equivalent to
// defining these symbols explicitly in the linker script.
template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
- if (ElfSym::GlobalOffsetTable) {
+ if (ElfSym::globalOffsetTable) {
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually
// to the start of the .got or .got.plt section.
- InputSection *GotSection = In.GotPlt;
- if (!Target->GotBaseSymInGotPlt)
- GotSection = In.MipsGot ? cast<InputSection>(In.MipsGot)
- : cast<InputSection>(In.Got);
- ElfSym::GlobalOffsetTable->Section = GotSection;
+ InputSection *gotSection = in.gotPlt;
+ if (!target->gotBaseSymInGotPlt)
+ gotSection = in.mipsGot ? cast<InputSection>(in.mipsGot)
+ : cast<InputSection>(in.got);
+ ElfSym::globalOffsetTable->section = gotSection;
}
// .rela_iplt_{start,end} mark the start and the end of .rela.plt section.
- if (ElfSym::RelaIpltStart && !In.RelaIplt->empty()) {
- ElfSym::RelaIpltStart->Section = In.RelaIplt;
- ElfSym::RelaIpltEnd->Section = In.RelaIplt;
- ElfSym::RelaIpltEnd->Value = In.RelaIplt->getSize();
+ if (ElfSym::relaIpltStart && in.relaIplt->isNeeded()) {
+ ElfSym::relaIpltStart->section = in.relaIplt;
+ ElfSym::relaIpltEnd->section = in.relaIplt;
+ ElfSym::relaIpltEnd->value = in.relaIplt->getSize();
}
- PhdrEntry *Last = nullptr;
- PhdrEntry *LastRO = nullptr;
+ PhdrEntry *last = nullptr;
+ PhdrEntry *lastRO = nullptr;
- for (PhdrEntry *P : Phdrs) {
- if (P->p_type != PT_LOAD)
- continue;
- Last = P;
- if (!(P->p_flags & PF_W))
- LastRO = P;
+ for (Partition &part : partitions) {
+ for (PhdrEntry *p : part.phdrs) {
+ if (p->p_type != PT_LOAD)
+ continue;
+ last = p;
+ if (!(p->p_flags & PF_W))
+ lastRO = p;
+ }
}
- if (LastRO) {
+ if (lastRO) {
// _etext is the first location after the last read-only loadable segment.
- if (ElfSym::Etext1)
- ElfSym::Etext1->Section = LastRO->LastSec;
- if (ElfSym::Etext2)
- ElfSym::Etext2->Section = LastRO->LastSec;
+ if (ElfSym::etext1)
+ ElfSym::etext1->section = lastRO->lastSec;
+ if (ElfSym::etext2)
+ ElfSym::etext2->section = lastRO->lastSec;
}
- if (Last) {
+ if (last) {
// _edata points to the end of the last mapped initialized section.
- OutputSection *Edata = nullptr;
- for (OutputSection *OS : OutputSections) {
- if (OS->Type != SHT_NOBITS)
- Edata = OS;
- if (OS == Last->LastSec)
+ OutputSection *edata = nullptr;
+ for (OutputSection *os : outputSections) {
+ if (os->type != SHT_NOBITS)
+ edata = os;
+ if (os == last->lastSec)
break;
}
- if (ElfSym::Edata1)
- ElfSym::Edata1->Section = Edata;
- if (ElfSym::Edata2)
- ElfSym::Edata2->Section = Edata;
+ if (ElfSym::edata1)
+ ElfSym::edata1->section = edata;
+ if (ElfSym::edata2)
+ ElfSym::edata2->section = edata;
// _end is the first location after the uninitialized data region.
- if (ElfSym::End1)
- ElfSym::End1->Section = Last->LastSec;
- if (ElfSym::End2)
- ElfSym::End2->Section = Last->LastSec;
+ if (ElfSym::end1)
+ ElfSym::end1->section = last->lastSec;
+ if (ElfSym::end2)
+ ElfSym::end2->section = last->lastSec;
}
- if (ElfSym::Bss)
- ElfSym::Bss->Section = findSection(".bss");
+ if (ElfSym::bss)
+ ElfSym::bss->section = findSection(".bss");
// Setup MIPS _gp_disp/__gnu_local_gp symbols which should
// be equal to the _gp symbol's value.
- if (ElfSym::MipsGp) {
+ if (ElfSym::mipsGp) {
// Find GP-relative section with the lowest address
// and use this address to calculate default _gp value.
- for (OutputSection *OS : OutputSections) {
- if (OS->Flags & SHF_MIPS_GPREL) {
- ElfSym::MipsGp->Section = OS;
- ElfSym::MipsGp->Value = 0x7ff0;
+ for (OutputSection *os : outputSections) {
+ if (os->flags & SHF_MIPS_GPREL) {
+ ElfSym::mipsGp->section = os;
+ ElfSym::mipsGp->value = 0x7ff0;
break;
}
}
@@ -1025,14 +1142,13 @@ template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
// The more branches in getSectionRank that match, the more similar they are.
// Since each branch corresponds to a bit flag, we can just use
// countLeadingZeros.
-static int getRankProximityAux(OutputSection *A, OutputSection *B) {
- return countLeadingZeros(A->SortRank ^ B->SortRank);
+static int getRankProximityAux(OutputSection *a, OutputSection *b) {
+ return countLeadingZeros(a->sortRank ^ b->sortRank);
}
-static int getRankProximity(OutputSection *A, BaseCommand *B) {
- if (auto *Sec = dyn_cast<OutputSection>(B))
- return getRankProximityAux(A, Sec);
- return -1;
+static int getRankProximity(OutputSection *a, BaseCommand *b) {
+ auto *sec = dyn_cast<OutputSection>(b);
+ return (sec && sec->hasInputSections) ? getRankProximityAux(a, sec) : -1;
}
// When placing orphan sections, we want to place them after symbol assignments
@@ -1049,137 +1165,142 @@ static int getRankProximity(OutputSection *A, BaseCommand *B) {
// /* The RW PT_LOAD starts here*/
// rw_sec : { *(rw_sec) }
// would mean that the RW PT_LOAD would become unaligned.
-static bool shouldSkip(BaseCommand *Cmd) {
- if (auto *Assign = dyn_cast<SymbolAssignment>(Cmd))
- return Assign->Name != ".";
+static bool shouldSkip(BaseCommand *cmd) {
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
+ return assign->name != ".";
return false;
}
// We want to place orphan sections so that they share as much
// characteristics with their neighbors as possible. For example, if
// both are rw, or both are tls.
-template <typename ELFT>
static std::vector<BaseCommand *>::iterator
-findOrphanPos(std::vector<BaseCommand *>::iterator B,
- std::vector<BaseCommand *>::iterator E) {
- OutputSection *Sec = cast<OutputSection>(*E);
+findOrphanPos(std::vector<BaseCommand *>::iterator b,
+ std::vector<BaseCommand *>::iterator e) {
+ OutputSection *sec = cast<OutputSection>(*e);
// Find the first element that has as close a rank as possible.
- auto I = std::max_element(B, E, [=](BaseCommand *A, BaseCommand *B) {
- return getRankProximity(Sec, A) < getRankProximity(Sec, B);
+ auto i = std::max_element(b, e, [=](BaseCommand *a, BaseCommand *b) {
+ return getRankProximity(sec, a) < getRankProximity(sec, b);
});
- if (I == E)
- return E;
+ if (i == e)
+ return e;
// Consider all existing sections with the same proximity.
- int Proximity = getRankProximity(Sec, *I);
- for (; I != E; ++I) {
- auto *CurSec = dyn_cast<OutputSection>(*I);
- if (!CurSec)
+ int proximity = getRankProximity(sec, *i);
+ for (; i != e; ++i) {
+ auto *curSec = dyn_cast<OutputSection>(*i);
+ if (!curSec || !curSec->hasInputSections)
continue;
- if (getRankProximity(Sec, CurSec) != Proximity ||
- Sec->SortRank < CurSec->SortRank)
+ if (getRankProximity(sec, curSec) != proximity ||
+ sec->sortRank < curSec->sortRank)
break;
}
- auto IsOutputSec = [](BaseCommand *Cmd) { return isa<OutputSection>(Cmd); };
- auto J = std::find_if(llvm::make_reverse_iterator(I),
- llvm::make_reverse_iterator(B), IsOutputSec);
- I = J.base();
+ auto isOutputSecWithInputSections = [](BaseCommand *cmd) {
+ auto *os = dyn_cast<OutputSection>(cmd);
+ return os && os->hasInputSections;
+ };
+ auto j = std::find_if(llvm::make_reverse_iterator(i),
+ llvm::make_reverse_iterator(b),
+ isOutputSecWithInputSections);
+ i = j.base();
// As a special case, if the orphan section is the last section, put
// it at the very end, past any other commands.
// This matches bfd's behavior and is convenient when the linker script fully
// specifies the start of the file, but doesn't care about the end (the non
// alloc sections for example).
- auto NextSec = std::find_if(I, E, IsOutputSec);
- if (NextSec == E)
- return E;
+ auto nextSec = std::find_if(i, e, isOutputSecWithInputSections);
+ if (nextSec == e)
+ return e;
- while (I != E && shouldSkip(*I))
- ++I;
- return I;
+ while (i != e && shouldSkip(*i))
+ ++i;
+ return i;
}
// Builds section order for handling --symbol-ordering-file.
static DenseMap<const InputSectionBase *, int> buildSectionOrder() {
- DenseMap<const InputSectionBase *, int> SectionOrder;
+ DenseMap<const InputSectionBase *, int> sectionOrder;
// Use the rarely used option -call-graph-ordering-file to sort sections.
- if (!Config->CallGraphProfile.empty())
+ if (!config->callGraphProfile.empty())
return computeCallGraphProfileOrder();
- if (Config->SymbolOrderingFile.empty())
- return SectionOrder;
+ if (config->symbolOrderingFile.empty())
+ return sectionOrder;
struct SymbolOrderEntry {
- int Priority;
- bool Present;
+ int priority;
+ bool present;
};
// Build a map from symbols to their priorities. Symbols that didn't
// appear in the symbol ordering file have the lowest priority 0.
// All explicitly mentioned symbols have negative (higher) priorities.
- DenseMap<StringRef, SymbolOrderEntry> SymbolOrder;
- int Priority = -Config->SymbolOrderingFile.size();
- for (StringRef S : Config->SymbolOrderingFile)
- SymbolOrder.insert({S, {Priority++, false}});
+ DenseMap<StringRef, SymbolOrderEntry> symbolOrder;
+ int priority = -config->symbolOrderingFile.size();
+ for (StringRef s : config->symbolOrderingFile)
+ symbolOrder.insert({s, {priority++, false}});
// Build a map from sections to their priorities.
- auto AddSym = [&](Symbol &Sym) {
- auto It = SymbolOrder.find(Sym.getName());
- if (It == SymbolOrder.end())
+ auto addSym = [&](Symbol &sym) {
+ auto it = symbolOrder.find(sym.getName());
+ if (it == symbolOrder.end())
return;
- SymbolOrderEntry &Ent = It->second;
- Ent.Present = true;
+ SymbolOrderEntry &ent = it->second;
+ ent.present = true;
- maybeWarnUnorderableSymbol(&Sym);
+ maybeWarnUnorderableSymbol(&sym);
- if (auto *D = dyn_cast<Defined>(&Sym)) {
- if (auto *Sec = dyn_cast_or_null<InputSectionBase>(D->Section)) {
- int &Priority = SectionOrder[cast<InputSectionBase>(Sec->Repl)];
- Priority = std::min(Priority, Ent.Priority);
+ if (auto *d = dyn_cast<Defined>(&sym)) {
+ if (auto *sec = dyn_cast_or_null<InputSectionBase>(d->section)) {
+ int &priority = sectionOrder[cast<InputSectionBase>(sec->repl)];
+ priority = std::min(priority, ent.priority);
}
}
};
// We want both global and local symbols. We get the global ones from the
// symbol table and iterate the object files for the local ones.
- for (Symbol *Sym : Symtab->getSymbols())
- if (!Sym->isLazy())
- AddSym(*Sym);
- for (InputFile *File : ObjectFiles)
- for (Symbol *Sym : File->getSymbols())
- if (Sym->isLocal())
- AddSym(*Sym);
+ symtab->forEachSymbol([&](Symbol *sym) {
+ if (!sym->isLazy())
+ addSym(*sym);
+ });
- if (Config->WarnSymbolOrdering)
- for (auto OrderEntry : SymbolOrder)
- if (!OrderEntry.second.Present)
- warn("symbol ordering file: no such symbol: " + OrderEntry.first);
+ for (InputFile *file : objectFiles)
+ for (Symbol *sym : file->getSymbols())
+ if (sym->isLocal())
+ addSym(*sym);
- return SectionOrder;
+ if (config->warnSymbolOrdering)
+ for (auto orderEntry : symbolOrder)
+ if (!orderEntry.second.present)
+ warn("symbol ordering file: no such symbol: " + orderEntry.first);
+
+ return sectionOrder;
}
// Sorts the sections in ISD according to the provided section order.
static void
-sortISDBySectionOrder(InputSectionDescription *ISD,
- const DenseMap<const InputSectionBase *, int> &Order) {
- std::vector<InputSection *> UnorderedSections;
- std::vector<std::pair<InputSection *, int>> OrderedSections;
- uint64_t UnorderedSize = 0;
-
- for (InputSection *IS : ISD->Sections) {
- auto I = Order.find(IS);
- if (I == Order.end()) {
- UnorderedSections.push_back(IS);
- UnorderedSize += IS->getSize();
+sortISDBySectionOrder(InputSectionDescription *isd,
+ const DenseMap<const InputSectionBase *, int> &order) {
+ std::vector<InputSection *> unorderedSections;
+ std::vector<std::pair<InputSection *, int>> orderedSections;
+ uint64_t unorderedSize = 0;
+
+ for (InputSection *isec : isd->sections) {
+ auto i = order.find(isec);
+ if (i == order.end()) {
+ unorderedSections.push_back(isec);
+ unorderedSize += isec->getSize();
continue;
}
- OrderedSections.push_back({IS, I->second});
+ orderedSections.push_back({isec, i->second});
}
- llvm::sort(OrderedSections, [&](std::pair<InputSection *, int> A,
- std::pair<InputSection *, int> B) {
- return A.second < B.second;
+ llvm::sort(orderedSections, [&](std::pair<InputSection *, int> a,
+ std::pair<InputSection *, int> b) {
+ return a.second < b.second;
});
// Find an insertion point for the ordered section list in the unordered
@@ -1209,96 +1330,114 @@ sortISDBySectionOrder(InputSectionDescription *ISD,
// of the second block of cold code can call the hot code without a thunk. So
// we effectively double the amount of code that could potentially call into
// the hot code without a thunk.
- size_t InsPt = 0;
- if (Target->getThunkSectionSpacing() && !OrderedSections.empty()) {
- uint64_t UnorderedPos = 0;
- for (; InsPt != UnorderedSections.size(); ++InsPt) {
- UnorderedPos += UnorderedSections[InsPt]->getSize();
- if (UnorderedPos > UnorderedSize / 2)
+ size_t insPt = 0;
+ if (target->getThunkSectionSpacing() && !orderedSections.empty()) {
+ uint64_t unorderedPos = 0;
+ for (; insPt != unorderedSections.size(); ++insPt) {
+ unorderedPos += unorderedSections[insPt]->getSize();
+ if (unorderedPos > unorderedSize / 2)
break;
}
}
- ISD->Sections.clear();
- for (InputSection *IS : makeArrayRef(UnorderedSections).slice(0, InsPt))
- ISD->Sections.push_back(IS);
- for (std::pair<InputSection *, int> P : OrderedSections)
- ISD->Sections.push_back(P.first);
- for (InputSection *IS : makeArrayRef(UnorderedSections).slice(InsPt))
- ISD->Sections.push_back(IS);
+ isd->sections.clear();
+ for (InputSection *isec : makeArrayRef(unorderedSections).slice(0, insPt))
+ isd->sections.push_back(isec);
+ for (std::pair<InputSection *, int> p : orderedSections)
+ isd->sections.push_back(p.first);
+ for (InputSection *isec : makeArrayRef(unorderedSections).slice(insPt))
+ isd->sections.push_back(isec);
}
-static void sortSection(OutputSection *Sec,
- const DenseMap<const InputSectionBase *, int> &Order) {
- StringRef Name = Sec->Name;
+static void sortSection(OutputSection *sec,
+ const DenseMap<const InputSectionBase *, int> &order) {
+ StringRef name = sec->name;
// Sort input sections by section name suffixes for
// __attribute__((init_priority(N))).
- if (Name == ".init_array" || Name == ".fini_array") {
- if (!Script->HasSectionsCommand)
- Sec->sortInitFini();
+ if (name == ".init_array" || name == ".fini_array") {
+ if (!script->hasSectionsCommand)
+ sec->sortInitFini();
return;
}
// Sort input sections by the special rule for .ctors and .dtors.
- if (Name == ".ctors" || Name == ".dtors") {
- if (!Script->HasSectionsCommand)
- Sec->sortCtorsDtors();
+ if (name == ".ctors" || name == ".dtors") {
+ if (!script->hasSectionsCommand)
+ sec->sortCtorsDtors();
return;
}
// Never sort these.
- if (Name == ".init" || Name == ".fini")
+ if (name == ".init" || name == ".fini")
+ return;
+
+ // .toc is allocated just after .got and is accessed using GOT-relative
+ // relocations. Object files compiled with small code model have an
+ // addressable range of [.got, .got + 0xFFFC] for GOT-relative relocations.
+ // To reduce the risk of relocation overflow, .toc contents are sorted so that
+ // sections having smaller relocation offsets are at beginning of .toc
+ if (config->emachine == EM_PPC64 && name == ".toc") {
+ if (script->hasSectionsCommand)
+ return;
+ assert(sec->sectionCommands.size() == 1);
+ auto *isd = cast<InputSectionDescription>(sec->sectionCommands[0]);
+ llvm::stable_sort(isd->sections,
+ [](const InputSection *a, const InputSection *b) -> bool {
+ return a->file->ppc64SmallCodeModelTocRelocs &&
+ !b->file->ppc64SmallCodeModelTocRelocs;
+ });
return;
+ }
// Sort input sections by priority using the list provided
// by --symbol-ordering-file.
- if (!Order.empty())
- for (BaseCommand *B : Sec->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(B))
- sortISDBySectionOrder(ISD, Order);
+ if (!order.empty())
+ for (BaseCommand *b : sec->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(b))
+ sortISDBySectionOrder(isd, order);
}
// If no layout was provided by linker script, we want to apply default
// sorting for special input sections. This also handles --symbol-ordering-file.
template <class ELFT> void Writer<ELFT>::sortInputSections() {
// Build the order once since it is expensive.
- DenseMap<const InputSectionBase *, int> Order = buildSectionOrder();
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- sortSection(Sec, Order);
+ DenseMap<const InputSectionBase *, int> order = buildSectionOrder();
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ sortSection(sec, order);
}
template <class ELFT> void Writer<ELFT>::sortSections() {
- Script->adjustSectionsBeforeSorting();
+ script->adjustSectionsBeforeSorting();
// Don't sort if using -r. It is not necessary and we want to preserve the
// relative order for SHF_LINK_ORDER sections.
- if (Config->Relocatable)
+ if (config->relocatable)
return;
sortInputSections();
- for (BaseCommand *Base : Script->SectionCommands) {
- auto *OS = dyn_cast<OutputSection>(Base);
- if (!OS)
+ for (BaseCommand *base : script->sectionCommands) {
+ auto *os = dyn_cast<OutputSection>(base);
+ if (!os)
continue;
- OS->SortRank = getSectionRank(OS);
+ os->sortRank = getSectionRank(os);
- // We want to assign rude approximation values to OutSecOff fields
+ // We want to assign rude approximation values to outSecOff fields
// to know the relative order of the input sections. We use it for
// sorting SHF_LINK_ORDER sections. See resolveShfLinkOrder().
- uint64_t I = 0;
- for (InputSection *Sec : getInputSections(OS))
- Sec->OutSecOff = I++;
+ uint64_t i = 0;
+ for (InputSection *sec : getInputSections(os))
+ sec->outSecOff = i++;
}
- if (!Script->HasSectionsCommand) {
+ if (!script->hasSectionsCommand) {
// We know that all the OutputSections are contiguous in this case.
- auto IsSection = [](BaseCommand *Base) { return isa<OutputSection>(Base); };
+ auto isSection = [](BaseCommand *base) { return isa<OutputSection>(base); };
std::stable_sort(
- llvm::find_if(Script->SectionCommands, IsSection),
- llvm::find_if(llvm::reverse(Script->SectionCommands), IsSection).base(),
+ llvm::find_if(script->sectionCommands, isSection),
+ llvm::find_if(llvm::reverse(script->sectionCommands), isSection).base(),
compareSections);
return;
}
@@ -1342,211 +1481,127 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
// after another commands. For the details, look at shouldSkip
// function.
- auto I = Script->SectionCommands.begin();
- auto E = Script->SectionCommands.end();
- auto NonScriptI = std::find_if(I, E, [](BaseCommand *Base) {
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- return Sec->SectionIndex == UINT32_MAX;
+ auto i = script->sectionCommands.begin();
+ auto e = script->sectionCommands.end();
+ auto nonScriptI = std::find_if(i, e, [](BaseCommand *base) {
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ return sec->sectionIndex == UINT32_MAX;
return false;
});
// Sort the orphan sections.
- std::stable_sort(NonScriptI, E, compareSections);
+ std::stable_sort(nonScriptI, e, compareSections);
// As a horrible special case, skip the first . assignment if it is before any
// section. We do this because it is common to set a load address by starting
// the script with ". = 0xabcd" and the expectation is that every section is
// after that.
- auto FirstSectionOrDotAssignment =
- std::find_if(I, E, [](BaseCommand *Cmd) { return !shouldSkip(Cmd); });
- if (FirstSectionOrDotAssignment != E &&
- isa<SymbolAssignment>(**FirstSectionOrDotAssignment))
- ++FirstSectionOrDotAssignment;
- I = FirstSectionOrDotAssignment;
+ auto firstSectionOrDotAssignment =
+ std::find_if(i, e, [](BaseCommand *cmd) { return !shouldSkip(cmd); });
+ if (firstSectionOrDotAssignment != e &&
+ isa<SymbolAssignment>(**firstSectionOrDotAssignment))
+ ++firstSectionOrDotAssignment;
+ i = firstSectionOrDotAssignment;
- while (NonScriptI != E) {
- auto Pos = findOrphanPos<ELFT>(I, NonScriptI);
- OutputSection *Orphan = cast<OutputSection>(*NonScriptI);
+ while (nonScriptI != e) {
+ auto pos = findOrphanPos(i, nonScriptI);
+ OutputSection *orphan = cast<OutputSection>(*nonScriptI);
// As an optimization, find all sections with the same sort rank
// and insert them with one rotate.
- unsigned Rank = Orphan->SortRank;
- auto End = std::find_if(NonScriptI + 1, E, [=](BaseCommand *Cmd) {
- return cast<OutputSection>(Cmd)->SortRank != Rank;
+ unsigned rank = orphan->sortRank;
+ auto end = std::find_if(nonScriptI + 1, e, [=](BaseCommand *cmd) {
+ return cast<OutputSection>(cmd)->sortRank != rank;
});
- std::rotate(Pos, NonScriptI, End);
- NonScriptI = End;
+ std::rotate(pos, nonScriptI, end);
+ nonScriptI = end;
}
- Script->adjustSectionsAfterSorting();
+ script->adjustSectionsAfterSorting();
}
-static bool compareByFilePosition(InputSection *A, InputSection *B) {
- // Synthetic, i. e. a sentinel section, should go last.
- if (A->kind() == InputSectionBase::Synthetic ||
- B->kind() == InputSectionBase::Synthetic)
- return A->kind() != InputSectionBase::Synthetic;
-
- InputSection *LA = A->getLinkOrderDep();
- InputSection *LB = B->getLinkOrderDep();
- OutputSection *AOut = LA->getParent();
- OutputSection *BOut = LB->getParent();
-
- if (AOut != BOut)
- return AOut->SectionIndex < BOut->SectionIndex;
- return LA->OutSecOff < LB->OutSecOff;
-}
-
-// This function is used by the --merge-exidx-entries to detect duplicate
-// .ARM.exidx sections. It is Arm only.
-//
-// The .ARM.exidx section is of the form:
-// | PREL31 offset to function | Unwind instructions for function |
-// where the unwind instructions are either a small number of unwind
-// instructions inlined into the table entry, the special CANT_UNWIND value of
-// 0x1 or a PREL31 offset into a .ARM.extab Section that contains unwind
-// instructions.
-//
-// We return true if all the unwind instructions in the .ARM.exidx entries of
-// Cur can be merged into the last entry of Prev.
-static bool isDuplicateArmExidxSec(InputSection *Prev, InputSection *Cur) {
-
- // References to .ARM.Extab Sections have bit 31 clear and are not the
- // special EXIDX_CANTUNWIND bit-pattern.
- auto IsExtabRef = [](uint32_t Unwind) {
- return (Unwind & 0x80000000) == 0 && Unwind != 0x1;
- };
-
- struct ExidxEntry {
- ulittle32_t Fn;
- ulittle32_t Unwind;
- };
-
- // Get the last table Entry from the previous .ARM.exidx section.
- const ExidxEntry &PrevEntry = Prev->getDataAs<ExidxEntry>().back();
- if (IsExtabRef(PrevEntry.Unwind))
- return false;
-
- // We consider the unwind instructions of an .ARM.exidx table entry
- // a duplicate if the previous unwind instructions if:
- // - Both are the special EXIDX_CANTUNWIND.
- // - Both are the same inline unwind instructions.
- // We do not attempt to follow and check links into .ARM.extab tables as
- // consecutive identical entries are rare and the effort to check that they
- // are identical is high.
+static bool compareByFilePosition(InputSection *a, InputSection *b) {
+ InputSection *la = a->getLinkOrderDep();
+ InputSection *lb = b->getLinkOrderDep();
+ OutputSection *aOut = la->getParent();
+ OutputSection *bOut = lb->getParent();
- for (const ExidxEntry Entry : Cur->getDataAs<ExidxEntry>())
- if (IsExtabRef(Entry.Unwind) || Entry.Unwind != PrevEntry.Unwind)
- return false;
-
- // All table entries in this .ARM.exidx Section can be merged into the
- // previous Section.
- return true;
+ if (aOut != bOut)
+ return aOut->sectionIndex < bOut->sectionIndex;
+ return la->outSecOff < lb->outSecOff;
}
template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
- for (OutputSection *Sec : OutputSections) {
- if (!(Sec->Flags & SHF_LINK_ORDER))
+ for (OutputSection *sec : outputSections) {
+ if (!(sec->flags & SHF_LINK_ORDER))
continue;
// Link order may be distributed across several InputSectionDescriptions
// but sort must consider them all at once.
- std::vector<InputSection **> ScriptSections;
- std::vector<InputSection *> Sections;
- for (BaseCommand *Base : Sec->SectionCommands) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
- for (InputSection *&IS : ISD->Sections) {
- ScriptSections.push_back(&IS);
- Sections.push_back(IS);
+ std::vector<InputSection **> scriptSections;
+ std::vector<InputSection *> sections;
+ for (BaseCommand *base : sec->sectionCommands) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
+ for (InputSection *&isec : isd->sections) {
+ scriptSections.push_back(&isec);
+ sections.push_back(isec);
}
}
}
- std::stable_sort(Sections.begin(), Sections.end(), compareByFilePosition);
-
- if (!Config->Relocatable && Config->EMachine == EM_ARM &&
- Sec->Type == SHT_ARM_EXIDX) {
-
- if (auto *Sentinel = dyn_cast<ARMExidxSentinelSection>(Sections.back())) {
- assert(Sections.size() >= 2 &&
- "We should create a sentinel section only if there are "
- "alive regular exidx sections.");
- // The last executable section is required to fill the sentinel.
- // Remember it here so that we don't have to find it again.
- Sentinel->Highest = Sections[Sections.size() - 2]->getLinkOrderDep();
- }
-
- // The EHABI for the Arm Architecture permits consecutive identical
- // table entries to be merged. We use a simple implementation that
- // removes a .ARM.exidx Input Section if it can be merged into the
- // previous one. This does not require any rewriting of InputSection
- // contents but misses opportunities for fine grained deduplication
- // where only a subset of the InputSection contents can be merged.
- if (Config->MergeArmExidx) {
- size_t Prev = 0;
- // The last one is a sentinel entry which should not be removed.
- for (size_t I = 1; I < Sections.size() - 1; ++I) {
- if (isDuplicateArmExidxSec(Sections[Prev], Sections[I]))
- Sections[I] = nullptr;
- else
- Prev = I;
- }
- }
- }
+ // The ARM.exidx section use SHF_LINK_ORDER, but we have consolidated
+ // this processing inside the ARMExidxsyntheticsection::finalizeContents().
+ if (!config->relocatable && config->emachine == EM_ARM &&
+ sec->type == SHT_ARM_EXIDX)
+ continue;
- for (int I = 0, N = Sections.size(); I < N; ++I)
- *ScriptSections[I] = Sections[I];
+ llvm::stable_sort(sections, compareByFilePosition);
- // Remove the Sections we marked as duplicate earlier.
- for (BaseCommand *Base : Sec->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
- llvm::erase_if(ISD->Sections, [](InputSection *IS) { return !IS; });
+ for (int i = 0, n = sections.size(); i < n; ++i)
+ *scriptSections[i] = sections[i];
}
}
-// For most RISC ISAs, we need to generate content that depends on the address
-// of InputSections. For example some architectures such as AArch64 use small
-// displacements for jump instructions that is the linker's responsibility for
-// creating range extension thunks for. As the generation of the content may
-// also alter InputSection addresses we must converge to a fixed point.
-template <class ELFT> void Writer<ELFT>::maybeAddThunks() {
- if (!Target->NeedsThunks && !Config->AndroidPackDynRelocs &&
- !Config->RelrPackDynRelocs)
- return;
-
- ThunkCreator TC;
- AArch64Err843419Patcher A64P;
+// We need to generate and finalize the content that depends on the address of
+// InputSections. As the generation of the content may also alter InputSection
+// addresses we must converge to a fixed point. We do that here. See the comment
+// in Writer<ELFT>::finalizeSections().
+template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
+ ThunkCreator tc;
+ AArch64Err843419Patcher a64p;
+ // For some targets, like x86, this loop iterates only once.
for (;;) {
- bool Changed = false;
+ bool changed = false;
- Script->assignAddresses();
+ script->assignAddresses();
- if (Target->NeedsThunks)
- Changed |= TC.createThunks(OutputSections);
+ if (target->needsThunks)
+ changed |= tc.createThunks(outputSections);
- if (Config->FixCortexA53Errata843419) {
- if (Changed)
- Script->assignAddresses();
- Changed |= A64P.createFixes();
+ if (config->fixCortexA53Errata843419) {
+ if (changed)
+ script->assignAddresses();
+ changed |= a64p.createFixes();
}
- if (In.MipsGot)
- In.MipsGot->updateAllocSize();
-
- Changed |= In.RelaDyn->updateAllocSize();
+ if (in.mipsGot)
+ in.mipsGot->updateAllocSize();
- if (In.RelrDyn)
- Changed |= In.RelrDyn->updateAllocSize();
+ for (Partition &part : partitions) {
+ changed |= part.relaDyn->updateAllocSize();
+ if (part.relrDyn)
+ changed |= part.relrDyn->updateAllocSize();
+ }
- if (!Changed)
+ if (!changed)
return;
}
}
-static void finalizeSynthetic(SyntheticSection *Sec) {
- if (Sec && !Sec->empty() && Sec->getParent())
- Sec->finalizeContents();
+static void finalizeSynthetic(SyntheticSection *sec) {
+ if (sec && sec->isNeeded() && sec->getParent())
+ sec->finalizeContents();
}
// In order to allow users to manipulate linker-synthesized sections,
@@ -1565,128 +1620,190 @@ static void removeUnusedSyntheticSections() {
// All input synthetic sections that can be empty are placed after
// all regular ones. We iterate over them all and exit at first
// non-synthetic.
- for (InputSectionBase *S : llvm::reverse(InputSections)) {
- SyntheticSection *SS = dyn_cast<SyntheticSection>(S);
- if (!SS)
+ for (InputSectionBase *s : llvm::reverse(inputSections)) {
+ SyntheticSection *ss = dyn_cast<SyntheticSection>(s);
+ if (!ss)
return;
- OutputSection *OS = SS->getParent();
- if (!OS || !SS->empty())
+ OutputSection *os = ss->getParent();
+ if (!os || ss->isNeeded())
continue;
// If we reach here, then SS is an unused synthetic section and we want to
// remove it from corresponding input section description of output section.
- for (BaseCommand *B : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(B))
- llvm::erase_if(ISD->Sections,
- [=](InputSection *IS) { return IS == SS; });
+ for (BaseCommand *b : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(b))
+ llvm::erase_if(isd->sections,
+ [=](InputSection *isec) { return isec == ss; });
}
}
// Returns true if a symbol can be replaced at load-time by a symbol
// with the same name defined in other ELF executable or DSO.
-static bool computeIsPreemptible(const Symbol &B) {
- assert(!B.isLocal());
+static bool computeIsPreemptible(const Symbol &b) {
+ assert(!b.isLocal());
// Only symbols that appear in dynsym can be preempted.
- if (!B.includeInDynsym())
+ if (!b.includeInDynsym())
return false;
// Only default visibility symbols can be preempted.
- if (B.Visibility != STV_DEFAULT)
+ if (b.visibility != STV_DEFAULT)
return false;
// At this point copy relocations have not been created yet, so any
// symbol that is not defined locally is preemptible.
- if (!B.isDefined())
+ if (!b.isDefined())
return true;
// If we have a dynamic list it specifies which local symbols are preemptible.
- if (Config->HasDynamicList)
+ if (config->hasDynamicList)
return false;
- if (!Config->Shared)
+ if (!config->shared)
return false;
// -Bsymbolic means that definitions are not preempted.
- if (Config->Bsymbolic || (Config->BsymbolicFunctions && B.isFunc()))
+ if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc()))
return false;
return true;
}
// Create output section objects and add them to OutputSections.
template <class ELFT> void Writer<ELFT>::finalizeSections() {
- Out::PreinitArray = findSection(".preinit_array");
- Out::InitArray = findSection(".init_array");
- Out::FiniArray = findSection(".fini_array");
+ Out::preinitArray = findSection(".preinit_array");
+ Out::initArray = findSection(".init_array");
+ Out::finiArray = findSection(".fini_array");
// The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop
// symbols for sections, so that the runtime can get the start and end
// addresses of each section by section name. Add such symbols.
- if (!Config->Relocatable) {
+ if (!config->relocatable) {
addStartEndSymbols();
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- addStartStopSymbols(Sec);
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ addStartStopSymbols(sec);
}
// Add _DYNAMIC symbol. Unlike GNU gold, our _DYNAMIC symbol has no type.
// It should be okay as no one seems to care about the type.
// Even the author of gold doesn't remember why gold behaves that way.
// https://sourceware.org/ml/binutils/2002-03/msg00360.html
- if (In.Dynamic->Parent)
- Symtab->addDefined("_DYNAMIC", STV_HIDDEN, STT_NOTYPE, 0 /*Value*/,
- /*Size=*/0, STB_WEAK, In.Dynamic,
- /*File=*/nullptr);
+ if (mainPart->dynamic->parent)
+ symtab->addSymbol(Defined{/*file=*/nullptr, "_DYNAMIC", STB_WEAK,
+ STV_HIDDEN, STT_NOTYPE,
+ /*value=*/0, /*size=*/0, mainPart->dynamic});
// Define __rel[a]_iplt_{start,end} symbols if needed.
addRelIpltSymbols();
// RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800 if not defined.
- if (Config->EMachine == EM_RISCV)
- if (!dyn_cast_or_null<Defined>(Symtab->find("__global_pointer$")))
- addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800);
+ // This symbol should only be defined in an executable.
+ if (config->emachine == EM_RISCV && !config->shared)
+ ElfSym::riscvGlobalPointer =
+ addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800,
+ STV_DEFAULT, STB_GLOBAL);
+
+ if (config->emachine == EM_X86_64) {
+ // On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
+ // way that:
+ //
+ // 1) Without relaxation: it produces a dynamic TLSDESC relocation that
+ // computes 0.
+ // 2) With LD->LE relaxation: _TLS_MODULE_BASE_@tpoff = 0 (lowest address in
+ // the TLS block).
+ //
+ // 2) is special cased in @tpoff computation. To satisfy 1), we define it as
+ // an absolute symbol of zero. This is different from GNU linkers which
+ // define _TLS_MODULE_BASE_ relative to the first TLS section.
+ Symbol *s = symtab->find("_TLS_MODULE_BASE_");
+ if (s && s->isUndefined()) {
+ s->resolve(Defined{/*file=*/nullptr, s->getName(), STB_GLOBAL, STV_HIDDEN,
+ STT_TLS, /*value=*/0, 0,
+ /*section=*/nullptr});
+ ElfSym::tlsModuleBase = cast<Defined>(s);
+ }
+ }
// This responsible for splitting up .eh_frame section into
// pieces. The relocation scan uses those pieces, so this has to be
// earlier.
- finalizeSynthetic(In.EhFrame);
+ for (Partition &part : partitions)
+ finalizeSynthetic(part.ehFrame);
- for (Symbol *S : Symtab->getSymbols())
- if (!S->IsPreemptible)
- S->IsPreemptible = computeIsPreemptible(*S);
+ symtab->forEachSymbol([](Symbol *s) {
+ if (!s->isPreemptible)
+ s->isPreemptible = computeIsPreemptible(*s);
+ });
// Scan relocations. This must be done after every symbol is declared so that
// we can correctly decide if a dynamic relocation is needed.
- if (!Config->Relocatable)
+ if (!config->relocatable) {
forEachRelSec(scanRelocations<ELFT>);
+ reportUndefinedSymbols<ELFT>();
+ }
- if (In.Plt && !In.Plt->empty())
- In.Plt->addSymbols();
- if (In.Iplt && !In.Iplt->empty())
- In.Iplt->addSymbols();
+ addIRelativeRelocs();
+
+ if (in.plt && in.plt->isNeeded())
+ in.plt->addSymbols();
+ if (in.iplt && in.iplt->isNeeded())
+ in.iplt->addSymbols();
+
+ if (!config->allowShlibUndefined) {
+ // Error on undefined symbols in a shared object, if all of its DT_NEEDED
+ // entires are seen. These cases would otherwise lead to runtime errors
+ // reported by the dynamic linker.
+ //
+ // ld.bfd traces all DT_NEEDED to emulate the logic of the dynamic linker to
+ // catch more cases. That is too much for us. Our approach resembles the one
+ // used in ld.gold, achieves a good balance to be useful but not too smart.
+ for (SharedFile *file : sharedFiles)
+ file->allNeededIsKnown =
+ llvm::all_of(file->dtNeeded, [&](StringRef needed) {
+ return symtab->soNames.count(needed);
+ });
+
+ symtab->forEachSymbol([](Symbol *sym) {
+ if (sym->isUndefined() && !sym->isWeak())
+ if (auto *f = dyn_cast_or_null<SharedFile>(sym->file))
+ if (f->allNeededIsKnown)
+ error(toString(f) + ": undefined reference to " + toString(*sym));
+ });
+ }
// Now that we have defined all possible global symbols including linker-
// synthesized ones. Visit all symbols to give the finishing touches.
- for (Symbol *Sym : Symtab->getSymbols()) {
- if (!includeInSymtab(*Sym))
- continue;
- if (In.SymTab)
- In.SymTab->addSymbol(Sym);
-
- if (Sym->includeInDynsym()) {
- In.DynSymTab->addSymbol(Sym);
- if (auto *File = dyn_cast_or_null<SharedFile<ELFT>>(Sym->File))
- if (File->IsNeeded && !Sym->isUndefined())
- InX<ELFT>::VerNeed->addSymbol(Sym);
+ symtab->forEachSymbol([](Symbol *sym) {
+ if (!includeInSymtab(*sym))
+ return;
+ if (in.symTab)
+ in.symTab->addSymbol(sym);
+
+ if (sym->includeInDynsym()) {
+ partitions[sym->partition - 1].dynSymTab->addSymbol(sym);
+ if (auto *file = dyn_cast_or_null<SharedFile>(sym->file))
+ if (file->isNeeded && !sym->isUndefined())
+ addVerneed(sym);
}
+ });
+
+ // We also need to scan the dynamic relocation tables of the other partitions
+ // and add any referenced symbols to the partition's dynsym.
+ for (Partition &part : MutableArrayRef<Partition>(partitions).slice(1)) {
+ DenseSet<Symbol *> syms;
+ for (const SymbolTableEntry &e : part.dynSymTab->getSymbols())
+ syms.insert(e.sym);
+ for (DynamicReloc &reloc : part.relaDyn->relocs)
+ if (reloc.sym && !reloc.useSymVA && syms.insert(reloc.sym).second)
+ part.dynSymTab->addSymbol(reloc.sym);
}
// Do not proceed if there was an undefined symbol.
if (errorCount())
return;
- if (In.MipsGot)
- In.MipsGot->build<ELFT>();
+ if (in.mipsGot)
+ in.mipsGot->build();
removeUnusedSyntheticSections();
@@ -1694,116 +1811,147 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// Now that we have the final list, create a list of all the
// OutputSections for convenience.
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- OutputSections.push_back(Sec);
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ outputSections.push_back(sec);
// Prefer command line supplied address over other constraints.
- for (OutputSection *Sec : OutputSections) {
- auto I = Config->SectionStartMap.find(Sec->Name);
- if (I != Config->SectionStartMap.end())
- Sec->AddrExpr = [=] { return I->second; };
+ for (OutputSection *sec : outputSections) {
+ auto i = config->sectionStartMap.find(sec->name);
+ if (i != config->sectionStartMap.end())
+ sec->addrExpr = [=] { return i->second; };
}
// This is a bit of a hack. A value of 0 means undef, so we set it
// to 1 to make __ehdr_start defined. The section number is not
// particularly relevant.
- Out::ElfHeader->SectionIndex = 1;
+ Out::elfHeader->sectionIndex = 1;
- for (size_t I = 0, E = OutputSections.size(); I != E; ++I) {
- OutputSection *Sec = OutputSections[I];
- Sec->SectionIndex = I + 1;
- Sec->ShName = In.ShStrTab->addString(Sec->Name);
+ for (size_t i = 0, e = outputSections.size(); i != e; ++i) {
+ OutputSection *sec = outputSections[i];
+ sec->sectionIndex = i + 1;
+ sec->shName = in.shStrTab->addString(sec->name);
}
// Binary and relocatable output does not have PHDRS.
// The headers have to be created before finalize as that can influence the
// image base and the dynamic section on mips includes the image base.
- if (!Config->Relocatable && !Config->OFormatBinary) {
- Phdrs = Script->hasPhdrsCommands() ? Script->createPhdrs() : createPhdrs();
- addPtArmExid(Phdrs);
- Out::ProgramHeaders->Size = sizeof(Elf_Phdr) * Phdrs.size();
+ if (!config->relocatable && !config->oFormatBinary) {
+ for (Partition &part : partitions) {
+ part.phdrs = script->hasPhdrsCommands() ? script->createPhdrs()
+ : createPhdrs(part);
+ if (config->emachine == EM_ARM) {
+ // PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME
+ addPhdrForSection(part, SHT_ARM_EXIDX, PT_ARM_EXIDX, PF_R);
+ }
+ if (config->emachine == EM_MIPS) {
+ // Add separate segments for MIPS-specific sections.
+ addPhdrForSection(part, SHT_MIPS_REGINFO, PT_MIPS_REGINFO, PF_R);
+ addPhdrForSection(part, SHT_MIPS_OPTIONS, PT_MIPS_OPTIONS, PF_R);
+ addPhdrForSection(part, SHT_MIPS_ABIFLAGS, PT_MIPS_ABIFLAGS, PF_R);
+ }
+ }
+ Out::programHeaders->size = sizeof(Elf_Phdr) * mainPart->phdrs.size();
// Find the TLS segment. This happens before the section layout loop so that
- // Android relocation packing can look up TLS symbol addresses.
- for (PhdrEntry *P : Phdrs)
- if (P->p_type == PT_TLS)
- Out::TlsPhdr = P;
+ // Android relocation packing can look up TLS symbol addresses. We only need
+ // to care about the main partition here because all TLS symbols were moved
+ // to the main partition (see MarkLive.cpp).
+ for (PhdrEntry *p : mainPart->phdrs)
+ if (p->p_type == PT_TLS)
+ Out::tlsPhdr = p;
}
// Some symbols are defined in term of program headers. Now that we
// have the headers, we can find out which sections they point to.
setReservedSymbolSections();
+ finalizeSynthetic(in.bss);
+ finalizeSynthetic(in.bssRelRo);
+ finalizeSynthetic(in.symTabShndx);
+ finalizeSynthetic(in.shStrTab);
+ finalizeSynthetic(in.strTab);
+ finalizeSynthetic(in.got);
+ finalizeSynthetic(in.mipsGot);
+ finalizeSynthetic(in.igotPlt);
+ finalizeSynthetic(in.gotPlt);
+ finalizeSynthetic(in.relaIplt);
+ finalizeSynthetic(in.relaPlt);
+ finalizeSynthetic(in.plt);
+ finalizeSynthetic(in.iplt);
+ finalizeSynthetic(in.ppc32Got2);
+ finalizeSynthetic(in.riscvSdata);
+ finalizeSynthetic(in.partIndex);
+
// Dynamic section must be the last one in this list and dynamic
- // symbol table section (DynSymTab) must be the first one.
- finalizeSynthetic(In.DynSymTab);
- finalizeSynthetic(In.Bss);
- finalizeSynthetic(In.BssRelRo);
- finalizeSynthetic(In.GnuHashTab);
- finalizeSynthetic(In.HashTab);
- finalizeSynthetic(In.SymTabShndx);
- finalizeSynthetic(In.ShStrTab);
- finalizeSynthetic(In.StrTab);
- finalizeSynthetic(In.VerDef);
- finalizeSynthetic(In.DynStrTab);
- finalizeSynthetic(In.Got);
- finalizeSynthetic(In.MipsGot);
- finalizeSynthetic(In.IgotPlt);
- finalizeSynthetic(In.GotPlt);
- finalizeSynthetic(In.RelaDyn);
- finalizeSynthetic(In.RelrDyn);
- finalizeSynthetic(In.RelaIplt);
- finalizeSynthetic(In.RelaPlt);
- finalizeSynthetic(In.Plt);
- finalizeSynthetic(In.Iplt);
- finalizeSynthetic(In.EhFrameHdr);
- finalizeSynthetic(InX<ELFT>::VerSym);
- finalizeSynthetic(InX<ELFT>::VerNeed);
- finalizeSynthetic(In.Dynamic);
-
- if (!Script->HasSectionsCommand && !Config->Relocatable)
+ // symbol table section (dynSymTab) must be the first one.
+ for (Partition &part : partitions) {
+ finalizeSynthetic(part.armExidx);
+ finalizeSynthetic(part.dynSymTab);
+ finalizeSynthetic(part.gnuHashTab);
+ finalizeSynthetic(part.hashTab);
+ finalizeSynthetic(part.verDef);
+ finalizeSynthetic(part.relaDyn);
+ finalizeSynthetic(part.relrDyn);
+ finalizeSynthetic(part.ehFrameHdr);
+ finalizeSynthetic(part.verSym);
+ finalizeSynthetic(part.verNeed);
+ finalizeSynthetic(part.dynamic);
+ }
+
+ if (!script->hasSectionsCommand && !config->relocatable)
fixSectionAlignments();
- // After link order processing .ARM.exidx sections can be deduplicated, which
- // needs to be resolved before any other address dependent operation.
+ // SHFLinkOrder processing must be processed after relative section placements are
+ // known but before addresses are allocated.
resolveShfLinkOrder();
- // Jump instructions in many ISAs have small displacements, and therefore they
- // cannot jump to arbitrary addresses in memory. For example, RISC-V JAL
- // instruction can target only +-1 MiB from PC. It is a linker's
- // responsibility to create and insert small pieces of code between sections
- // to extend the ranges if jump targets are out of range. Such code pieces are
- // called "thunks".
+ // This is used to:
+ // 1) Create "thunks":
+ // Jump instructions in many ISAs have small displacements, and therefore
+ // they cannot jump to arbitrary addresses in memory. For example, RISC-V
+ // JAL instruction can target only +-1 MiB from PC. It is a linker's
+ // responsibility to create and insert small pieces of code between
+ // sections to extend the ranges if jump targets are out of range. Such
+ // code pieces are called "thunks".
//
- // We add thunks at this stage. We couldn't do this before this point because
- // this is the earliest point where we know sizes of sections and their
- // layouts (that are needed to determine if jump targets are in range).
- maybeAddThunks();
+ // We add thunks at this stage. We couldn't do this before this point
+ // because this is the earliest point where we know sizes of sections and
+ // their layouts (that are needed to determine if jump targets are in
+ // range).
+ //
+ // 2) Update the sections. We need to generate content that depends on the
+ // address of InputSections. For example, MIPS GOT section content or
+ // android packed relocations sections content.
+ //
+ // 3) Assign the final values for the linker script symbols. Linker scripts
+ // sometimes using forward symbol declarations. We want to set the correct
+ // values. They also might change after adding the thunks.
+ finalizeAddressDependentContent();
- // maybeAddThunks may have added local symbols to the static symbol table.
- finalizeSynthetic(In.SymTab);
- finalizeSynthetic(In.PPC64LongBranchTarget);
+ // finalizeAddressDependentContent may have added local symbols to the static symbol table.
+ finalizeSynthetic(in.symTab);
+ finalizeSynthetic(in.ppc64LongBranchTarget);
// Fill other section headers. The dynamic table is finalized
// at the end because some tags like RELSZ depend on result
// of finalizing other sections.
- for (OutputSection *Sec : OutputSections)
- Sec->finalize<ELFT>();
+ for (OutputSection *sec : outputSections)
+ sec->finalize();
}
// Ensure data sections are not mixed with executable sections when
// -execute-only is used. -execute-only is a feature to make pages executable
// but not readable, and the feature is currently supported only on AArch64.
template <class ELFT> void Writer<ELFT>::checkExecuteOnly() {
- if (!Config->ExecuteOnly)
+ if (!config->executeOnly)
return;
- for (OutputSection *OS : OutputSections)
- if (OS->Flags & SHF_EXECINSTR)
- for (InputSection *IS : getInputSections(OS))
- if (!(IS->Flags & SHF_EXECINSTR))
- error("cannot place " + toString(IS) + " into " + toString(OS->Name) +
+ for (OutputSection *os : outputSections)
+ if (os->flags & SHF_EXECINSTR)
+ for (InputSection *isec : getInputSections(os))
+ if (!(isec->flags & SHF_EXECINSTR))
+ error("cannot place " + toString(isec) + " into " + toString(os->name) +
": -execute-only does not support intermingling data and code");
}
@@ -1828,24 +1976,24 @@ template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
// case, use the image base address as a last resort.
OutputSection *Default = findSection(".text");
if (!Default)
- Default = Out::ElfHeader;
+ Default = Out::elfHeader;
- auto Define = [=](StringRef Start, StringRef End, OutputSection *OS) {
- if (OS) {
- addOptionalRegular(Start, OS, 0);
- addOptionalRegular(End, OS, -1);
+ auto define = [=](StringRef start, StringRef end, OutputSection *os) {
+ if (os) {
+ addOptionalRegular(start, os, 0);
+ addOptionalRegular(end, os, -1);
} else {
- addOptionalRegular(Start, Default, 0);
- addOptionalRegular(End, Default, 0);
+ addOptionalRegular(start, Default, 0);
+ addOptionalRegular(end, Default, 0);
}
};
- Define("__preinit_array_start", "__preinit_array_end", Out::PreinitArray);
- Define("__init_array_start", "__init_array_end", Out::InitArray);
- Define("__fini_array_start", "__fini_array_end", Out::FiniArray);
+ define("__preinit_array_start", "__preinit_array_end", Out::preinitArray);
+ define("__init_array_start", "__init_array_end", Out::initArray);
+ define("__fini_array_start", "__fini_array_end", Out::finiArray);
- if (OutputSection *Sec = findSection(".ARM.exidx"))
- Define("__exidx_start", "__exidx_end", Sec);
+ if (OutputSection *sec = findSection(".ARM.exidx"))
+ define("__exidx_start", "__exidx_end", sec);
}
// If a section name is valid as a C identifier (which is rare because of
@@ -1854,22 +2002,22 @@ template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
// respectively. This is not requested by the ELF standard, but GNU ld and
// gold provide the feature, and used by many programs.
template <class ELFT>
-void Writer<ELFT>::addStartStopSymbols(OutputSection *Sec) {
- StringRef S = Sec->Name;
- if (!isValidCIdentifier(S))
+void Writer<ELFT>::addStartStopSymbols(OutputSection *sec) {
+ StringRef s = sec->name;
+ if (!isValidCIdentifier(s))
return;
- addOptionalRegular(Saver.save("__start_" + S), Sec, 0, STV_PROTECTED);
- addOptionalRegular(Saver.save("__stop_" + S), Sec, -1, STV_PROTECTED);
+ addOptionalRegular(saver.save("__start_" + s), sec, 0, STV_PROTECTED);
+ addOptionalRegular(saver.save("__stop_" + s), sec, -1, STV_PROTECTED);
}
-static bool needsPtLoad(OutputSection *Sec) {
- if (!(Sec->Flags & SHF_ALLOC) || Sec->Noload)
+static bool needsPtLoad(OutputSection *sec) {
+ if (!(sec->flags & SHF_ALLOC) || sec->noload)
return false;
// Don't allocate VA space for TLS NOBITS sections. The PT_TLS PHDR is
// responsible for allocating space for them, not the PT_LOAD that
// contains the TLS initialization image.
- if ((Sec->Flags & SHF_TLS) && Sec->Type == SHT_NOBITS)
+ if ((sec->flags & SHF_TLS) && sec->type == SHT_NOBITS)
return false;
return true;
}
@@ -1878,46 +2026,93 @@ static bool needsPtLoad(OutputSection *Sec) {
// linker scripts are designed for creating two PT_LOADs only, one RX and one
// RW. This means that there is no alignment in the RO to RX transition and we
// cannot create a PT_LOAD there.
-static uint64_t computeFlags(uint64_t Flags) {
- if (Config->Omagic)
+static uint64_t computeFlags(uint64_t flags) {
+ if (config->omagic)
return PF_R | PF_W | PF_X;
- if (Config->ExecuteOnly && (Flags & PF_X))
- return Flags & ~PF_R;
- if (Config->SingleRoRx && !(Flags & PF_W))
- return Flags | PF_X;
- return Flags;
+ if (config->executeOnly && (flags & PF_X))
+ return flags & ~PF_R;
+ if (config->singleRoRx && !(flags & PF_W))
+ return flags | PF_X;
+ return flags;
}
// Decide which program headers to create and which sections to include in each
// one.
-template <class ELFT> std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs() {
- std::vector<PhdrEntry *> Ret;
- auto AddHdr = [&](unsigned Type, unsigned Flags) -> PhdrEntry * {
- Ret.push_back(make<PhdrEntry>(Type, Flags));
- return Ret.back();
+template <class ELFT>
+std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs(Partition &part) {
+ std::vector<PhdrEntry *> ret;
+ auto addHdr = [&](unsigned type, unsigned flags) -> PhdrEntry * {
+ ret.push_back(make<PhdrEntry>(type, flags));
+ return ret.back();
};
+ unsigned partNo = part.getNumber();
+ bool isMain = partNo == 1;
+
// The first phdr entry is PT_PHDR which describes the program header itself.
- AddHdr(PT_PHDR, PF_R)->add(Out::ProgramHeaders);
+ if (isMain)
+ addHdr(PT_PHDR, PF_R)->add(Out::programHeaders);
+ else
+ addHdr(PT_PHDR, PF_R)->add(part.programHeaders->getParent());
// PT_INTERP must be the second entry if exists.
- if (OutputSection *Cmd = findSection(".interp"))
- AddHdr(PT_INTERP, Cmd->getPhdrFlags())->add(Cmd);
+ if (OutputSection *cmd = findSection(".interp", partNo))
+ addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd);
// Add the first PT_LOAD segment for regular output sections.
- uint64_t Flags = computeFlags(PF_R);
- PhdrEntry *Load = AddHdr(PT_LOAD, Flags);
+ uint64_t flags = computeFlags(PF_R);
+ PhdrEntry *load = nullptr;
// Add the headers. We will remove them if they don't fit.
- Load->add(Out::ElfHeader);
- Load->add(Out::ProgramHeaders);
+ // In the other partitions the headers are ordinary sections, so they don't
+ // need to be added here.
+ if (isMain) {
+ load = addHdr(PT_LOAD, flags);
+ load->add(Out::elfHeader);
+ load->add(Out::programHeaders);
+ }
+
+ // PT_GNU_RELRO includes all sections that should be marked as
+ // read-only by dynamic linker after proccessing relocations.
+ // Current dynamic loaders only support one PT_GNU_RELRO PHDR, give
+ // an error message if more than one PT_GNU_RELRO PHDR is required.
+ PhdrEntry *relRo = make<PhdrEntry>(PT_GNU_RELRO, PF_R);
+ bool inRelroPhdr = false;
+ OutputSection *relroEnd = nullptr;
+ for (OutputSection *sec : outputSections) {
+ if (sec->partition != partNo || !needsPtLoad(sec))
+ continue;
+ if (isRelroSection(sec)) {
+ inRelroPhdr = true;
+ if (!relroEnd)
+ relRo->add(sec);
+ else
+ error("section: " + sec->name + " is not contiguous with other relro" +
+ " sections");
+ } else if (inRelroPhdr) {
+ inRelroPhdr = false;
+ relroEnd = sec;
+ }
+ }
- for (OutputSection *Sec : OutputSections) {
- if (!(Sec->Flags & SHF_ALLOC))
+ for (OutputSection *sec : outputSections) {
+ if (!(sec->flags & SHF_ALLOC))
break;
- if (!needsPtLoad(Sec))
+ if (!needsPtLoad(sec))
continue;
+ // Normally, sections in partitions other than the current partition are
+ // ignored. But partition number 255 is a special case: it contains the
+ // partition end marker (.part.end). It needs to be added to the main
+ // partition so that a segment is created for it in the main partition,
+ // which will cause the dynamic loader to reserve space for the other
+ // partitions.
+ if (sec->partition != partNo) {
+ if (isMain && sec->partition == 255)
+ addHdr(PT_LOAD, computeFlags(sec->getPhdrFlags()))->add(sec);
+ continue;
+ }
+
// Segments are contiguous memory regions that has the same attributes
// (e.g. executable or writable). There is one phdr for each segment.
// Therefore, we need to create a new phdr when the next section has
@@ -1925,222 +2120,187 @@ template <class ELFT> std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs() {
// region using AT or AT> linker script command, respectively. At the same
// time, we don't want to create a separate load segment for the headers,
// even if the first output section has an AT or AT> attribute.
- uint64_t NewFlags = computeFlags(Sec->getPhdrFlags());
- if (((Sec->LMAExpr ||
- (Sec->LMARegion && (Sec->LMARegion != Load->FirstSec->LMARegion))) &&
- Load->LastSec != Out::ProgramHeaders) ||
- Sec->MemRegion != Load->FirstSec->MemRegion || Flags != NewFlags) {
-
- Load = AddHdr(PT_LOAD, NewFlags);
- Flags = NewFlags;
+ uint64_t newFlags = computeFlags(sec->getPhdrFlags());
+ if (!load ||
+ ((sec->lmaExpr ||
+ (sec->lmaRegion && (sec->lmaRegion != load->firstSec->lmaRegion))) &&
+ load->lastSec != Out::programHeaders) ||
+ sec->memRegion != load->firstSec->memRegion || flags != newFlags ||
+ sec == relroEnd) {
+ load = addHdr(PT_LOAD, newFlags);
+ flags = newFlags;
}
- Load->add(Sec);
+ load->add(sec);
}
// Add a TLS segment if any.
- PhdrEntry *TlsHdr = make<PhdrEntry>(PT_TLS, PF_R);
- for (OutputSection *Sec : OutputSections)
- if (Sec->Flags & SHF_TLS)
- TlsHdr->add(Sec);
- if (TlsHdr->FirstSec)
- Ret.push_back(TlsHdr);
+ PhdrEntry *tlsHdr = make<PhdrEntry>(PT_TLS, PF_R);
+ for (OutputSection *sec : outputSections)
+ if (sec->partition == partNo && sec->flags & SHF_TLS)
+ tlsHdr->add(sec);
+ if (tlsHdr->firstSec)
+ ret.push_back(tlsHdr);
// Add an entry for .dynamic.
- if (OutputSection *Sec = In.Dynamic->getParent())
- AddHdr(PT_DYNAMIC, Sec->getPhdrFlags())->add(Sec);
+ if (OutputSection *sec = part.dynamic->getParent())
+ addHdr(PT_DYNAMIC, sec->getPhdrFlags())->add(sec);
- // PT_GNU_RELRO includes all sections that should be marked as
- // read-only by dynamic linker after proccessing relocations.
- // Current dynamic loaders only support one PT_GNU_RELRO PHDR, give
- // an error message if more than one PT_GNU_RELRO PHDR is required.
- PhdrEntry *RelRo = make<PhdrEntry>(PT_GNU_RELRO, PF_R);
- bool InRelroPhdr = false;
- bool IsRelroFinished = false;
- for (OutputSection *Sec : OutputSections) {
- if (!needsPtLoad(Sec))
- continue;
- if (isRelroSection(Sec)) {
- InRelroPhdr = true;
- if (!IsRelroFinished)
- RelRo->add(Sec);
- else
- error("section: " + Sec->Name + " is not contiguous with other relro" +
- " sections");
- } else if (InRelroPhdr) {
- InRelroPhdr = false;
- IsRelroFinished = true;
- }
- }
- if (RelRo->FirstSec)
- Ret.push_back(RelRo);
+ if (relRo->firstSec)
+ ret.push_back(relRo);
// PT_GNU_EH_FRAME is a special section pointing on .eh_frame_hdr.
- if (!In.EhFrame->empty() && In.EhFrameHdr && In.EhFrame->getParent() &&
- In.EhFrameHdr->getParent())
- AddHdr(PT_GNU_EH_FRAME, In.EhFrameHdr->getParent()->getPhdrFlags())
- ->add(In.EhFrameHdr->getParent());
+ if (part.ehFrame->isNeeded() && part.ehFrameHdr &&
+ part.ehFrame->getParent() && part.ehFrameHdr->getParent())
+ addHdr(PT_GNU_EH_FRAME, part.ehFrameHdr->getParent()->getPhdrFlags())
+ ->add(part.ehFrameHdr->getParent());
// PT_OPENBSD_RANDOMIZE is an OpenBSD-specific feature. That makes
// the dynamic linker fill the segment with random data.
- if (OutputSection *Cmd = findSection(".openbsd.randomdata"))
- AddHdr(PT_OPENBSD_RANDOMIZE, Cmd->getPhdrFlags())->add(Cmd);
+ if (OutputSection *cmd = findSection(".openbsd.randomdata", partNo))
+ addHdr(PT_OPENBSD_RANDOMIZE, cmd->getPhdrFlags())->add(cmd);
// PT_GNU_STACK is a special section to tell the loader to make the
// pages for the stack non-executable. If you really want an executable
// stack, you can pass -z execstack, but that's not recommended for
// security reasons.
- unsigned Perm = PF_R | PF_W;
- if (Config->ZExecstack)
- Perm |= PF_X;
- AddHdr(PT_GNU_STACK, Perm)->p_memsz = Config->ZStackSize;
+ unsigned perm = PF_R | PF_W;
+ if (config->zExecstack)
+ perm |= PF_X;
+ addHdr(PT_GNU_STACK, perm)->p_memsz = config->zStackSize;
// PT_OPENBSD_WXNEEDED is a OpenBSD-specific header to mark the executable
// is expected to perform W^X violations, such as calling mprotect(2) or
// mmap(2) with PROT_WRITE | PROT_EXEC, which is prohibited by default on
// OpenBSD.
- if (Config->ZWxneeded)
- AddHdr(PT_OPENBSD_WXNEEDED, PF_X);
-
- // Create one PT_NOTE per a group of contiguous .note sections.
- PhdrEntry *Note = nullptr;
- for (OutputSection *Sec : OutputSections) {
- if (Sec->Type == SHT_NOTE && (Sec->Flags & SHF_ALLOC)) {
- if (!Note || Sec->LMAExpr)
- Note = AddHdr(PT_NOTE, PF_R);
- Note->add(Sec);
+ if (config->zWxneeded)
+ addHdr(PT_OPENBSD_WXNEEDED, PF_X);
+
+ // Create one PT_NOTE per a group of contiguous SHT_NOTE sections with the
+ // same alignment.
+ PhdrEntry *note = nullptr;
+ for (OutputSection *sec : outputSections) {
+ if (sec->partition != partNo)
+ continue;
+ if (sec->type == SHT_NOTE && (sec->flags & SHF_ALLOC)) {
+ if (!note || sec->lmaExpr || note->lastSec->alignment != sec->alignment)
+ note = addHdr(PT_NOTE, PF_R);
+ note->add(sec);
} else {
- Note = nullptr;
+ note = nullptr;
}
}
- return Ret;
+ return ret;
}
template <class ELFT>
-void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry *> &Phdrs) {
- if (Config->EMachine != EM_ARM)
- return;
- auto I = llvm::find_if(OutputSections, [](OutputSection *Cmd) {
- return Cmd->Type == SHT_ARM_EXIDX;
+void Writer<ELFT>::addPhdrForSection(Partition &part, unsigned shType,
+ unsigned pType, unsigned pFlags) {
+ unsigned partNo = part.getNumber();
+ auto i = llvm::find_if(outputSections, [=](OutputSection *cmd) {
+ return cmd->partition == partNo && cmd->type == shType;
});
- if (I == OutputSections.end())
+ if (i == outputSections.end())
return;
- // PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME
- PhdrEntry *ARMExidx = make<PhdrEntry>(PT_ARM_EXIDX, PF_R);
- ARMExidx->add(*I);
- Phdrs.push_back(ARMExidx);
+ PhdrEntry *entry = make<PhdrEntry>(pType, pFlags);
+ entry->add(*i);
+ part.phdrs.push_back(entry);
}
// The first section of each PT_LOAD, the first section in PT_GNU_RELRO and the
// first section after PT_GNU_RELRO have to be page aligned so that the dynamic
// linker can set the permissions.
template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
- auto PageAlign = [](OutputSection *Cmd) {
- if (Cmd && !Cmd->AddrExpr)
- Cmd->AddrExpr = [=] {
- return alignTo(Script->getDot(), Config->MaxPageSize);
+ auto pageAlign = [](OutputSection *cmd) {
+ if (cmd && !cmd->addrExpr)
+ cmd->addrExpr = [=] {
+ return alignTo(script->getDot(), config->maxPageSize);
};
};
- for (const PhdrEntry *P : Phdrs)
- if (P->p_type == PT_LOAD && P->FirstSec)
- PageAlign(P->FirstSec);
-
- for (const PhdrEntry *P : Phdrs) {
- if (P->p_type != PT_GNU_RELRO)
- continue;
-
- if (P->FirstSec)
- PageAlign(P->FirstSec);
-
- // Find the first section after PT_GNU_RELRO. If it is in a PT_LOAD we
- // have to align it to a page.
- auto End = OutputSections.end();
- auto I = std::find(OutputSections.begin(), End, P->LastSec);
- if (I == End || (I + 1) == End)
- continue;
-
- OutputSection *Cmd = (*(I + 1));
- if (needsPtLoad(Cmd))
- PageAlign(Cmd);
+ for (Partition &part : partitions) {
+ for (const PhdrEntry *p : part.phdrs)
+ if (p->p_type == PT_LOAD && p->firstSec)
+ pageAlign(p->firstSec);
}
}
// Compute an in-file position for a given section. The file offset must be the
// same with its virtual address modulo the page size, so that the loader can
// load executables without any address adjustment.
-static uint64_t computeFileOffset(OutputSection *OS, uint64_t Off) {
+static uint64_t computeFileOffset(OutputSection *os, uint64_t off) {
// File offsets are not significant for .bss sections. By convention, we keep
// section offsets monotonically increasing rather than setting to zero.
- if (OS->Type == SHT_NOBITS)
- return Off;
+ if (os->type == SHT_NOBITS)
+ return off;
// If the section is not in a PT_LOAD, we just have to align it.
- if (!OS->PtLoad)
- return alignTo(Off, OS->Alignment);
+ if (!os->ptLoad)
+ return alignTo(off, os->alignment);
// The first section in a PT_LOAD has to have congruent offset and address
// module the page size.
- OutputSection *First = OS->PtLoad->FirstSec;
- if (OS == First) {
- uint64_t Alignment = std::max<uint64_t>(OS->Alignment, Config->MaxPageSize);
- return alignTo(Off, Alignment, OS->Addr);
+ OutputSection *first = os->ptLoad->firstSec;
+ if (os == first) {
+ uint64_t alignment = std::max<uint64_t>(os->alignment, config->maxPageSize);
+ return alignTo(off, alignment, os->addr);
}
// If two sections share the same PT_LOAD the file offset is calculated
// using this formula: Off2 = Off1 + (VA2 - VA1).
- return First->Offset + OS->Addr - First->Addr;
+ return first->offset + os->addr - first->addr;
}
// Set an in-file position to a given section and returns the end position of
// the section.
-static uint64_t setFileOffset(OutputSection *OS, uint64_t Off) {
- Off = computeFileOffset(OS, Off);
- OS->Offset = Off;
+static uint64_t setFileOffset(OutputSection *os, uint64_t off) {
+ off = computeFileOffset(os, off);
+ os->offset = off;
- if (OS->Type == SHT_NOBITS)
- return Off;
- return Off + OS->Size;
+ if (os->type == SHT_NOBITS)
+ return off;
+ return off + os->size;
}
template <class ELFT> void Writer<ELFT>::assignFileOffsetsBinary() {
- uint64_t Off = 0;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Flags & SHF_ALLOC)
- Off = setFileOffset(Sec, Off);
- FileSize = alignTo(Off, Config->Wordsize);
+ uint64_t off = 0;
+ for (OutputSection *sec : outputSections)
+ if (sec->flags & SHF_ALLOC)
+ off = setFileOffset(sec, off);
+ fileSize = alignTo(off, config->wordsize);
}
-static std::string rangeToString(uint64_t Addr, uint64_t Len) {
- return "[0x" + utohexstr(Addr) + ", 0x" + utohexstr(Addr + Len - 1) + "]";
+static std::string rangeToString(uint64_t addr, uint64_t len) {
+ return "[0x" + utohexstr(addr) + ", 0x" + utohexstr(addr + len - 1) + "]";
}
// Assign file offsets to output sections.
template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
- uint64_t Off = 0;
- Off = setFileOffset(Out::ElfHeader, Off);
- Off = setFileOffset(Out::ProgramHeaders, Off);
-
- PhdrEntry *LastRX = nullptr;
- for (PhdrEntry *P : Phdrs)
- if (P->p_type == PT_LOAD && (P->p_flags & PF_X))
- LastRX = P;
-
- for (OutputSection *Sec : OutputSections) {
- Off = setFileOffset(Sec, Off);
- if (Script->HasSectionsCommand)
+ uint64_t off = 0;
+ off = setFileOffset(Out::elfHeader, off);
+ off = setFileOffset(Out::programHeaders, off);
+
+ PhdrEntry *lastRX = nullptr;
+ for (Partition &part : partitions)
+ for (PhdrEntry *p : part.phdrs)
+ if (p->p_type == PT_LOAD && (p->p_flags & PF_X))
+ lastRX = p;
+
+ for (OutputSection *sec : outputSections) {
+ off = setFileOffset(sec, off);
+ if (script->hasSectionsCommand)
continue;
// If this is a last section of the last executable segment and that
// segment is the last loadable segment, align the offset of the
// following section to avoid loading non-segments parts of the file.
- if (LastRX && LastRX->LastSec == Sec)
- Off = alignTo(Off, Target->PageSize);
+ if (lastRX && lastRX->lastSec == sec)
+ off = alignTo(off, config->commonPageSize);
}
- SectionHeaderOff = alignTo(Off, Config->Wordsize);
- FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr);
+ sectionHeaderOff = alignTo(off, config->wordsize);
+ fileSize = sectionHeaderOff + (outputSections.size() + 1) * sizeof(Elf_Shdr);
// Our logic assumes that sections have rising VA within the same segment.
// With use of linker scripts it is possible to violate this rule and get file
@@ -2151,62 +2311,49 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
// backwards, so we have to allow doing that to support linking them. We
// perform non-critical checks for overlaps in checkSectionOverlap(), but here
// we want to prevent file size overflows because it would crash the linker.
- for (OutputSection *Sec : OutputSections) {
- if (Sec->Type == SHT_NOBITS)
+ for (OutputSection *sec : outputSections) {
+ if (sec->type == SHT_NOBITS)
continue;
- if ((Sec->Offset > FileSize) || (Sec->Offset + Sec->Size > FileSize))
- error("unable to place section " + Sec->Name + " at file offset " +
- rangeToString(Sec->Offset, Sec->Size) +
+ if ((sec->offset > fileSize) || (sec->offset + sec->size > fileSize))
+ error("unable to place section " + sec->name + " at file offset " +
+ rangeToString(sec->offset, sec->size) +
"; check your linker script for overflows");
}
}
// Finalize the program headers. We call this function after we assign
// file offsets and VAs to all sections.
-template <class ELFT> void Writer<ELFT>::setPhdrs() {
- for (PhdrEntry *P : Phdrs) {
- OutputSection *First = P->FirstSec;
- OutputSection *Last = P->LastSec;
-
- if (First) {
- P->p_filesz = Last->Offset - First->Offset;
- if (Last->Type != SHT_NOBITS)
- P->p_filesz += Last->Size;
-
- P->p_memsz = Last->Addr + Last->Size - First->Addr;
- P->p_offset = First->Offset;
- P->p_vaddr = First->Addr;
-
- if (!P->HasLMA)
- P->p_paddr = First->getLMA();
+template <class ELFT> void Writer<ELFT>::setPhdrs(Partition &part) {
+ for (PhdrEntry *p : part.phdrs) {
+ OutputSection *first = p->firstSec;
+ OutputSection *last = p->lastSec;
+
+ if (first) {
+ p->p_filesz = last->offset - first->offset;
+ if (last->type != SHT_NOBITS)
+ p->p_filesz += last->size;
+
+ p->p_memsz = last->addr + last->size - first->addr;
+ p->p_offset = first->offset;
+ p->p_vaddr = first->addr;
+
+ // File offsets in partitions other than the main partition are relative
+ // to the offset of the ELF headers. Perform that adjustment now.
+ if (part.elfHeader)
+ p->p_offset -= part.elfHeader->getParent()->offset;
+
+ if (!p->hasLMA)
+ p->p_paddr = first->getLMA();
}
- if (P->p_type == PT_LOAD) {
- P->p_align = std::max<uint64_t>(P->p_align, Config->MaxPageSize);
- } else if (P->p_type == PT_GNU_RELRO) {
- P->p_align = 1;
+ if (p->p_type == PT_LOAD) {
+ p->p_align = std::max<uint64_t>(p->p_align, config->maxPageSize);
+ } else if (p->p_type == PT_GNU_RELRO) {
+ p->p_align = 1;
// The glibc dynamic loader rounds the size down, so we need to round up
// to protect the last page. This is a no-op on FreeBSD which always
// rounds up.
- P->p_memsz = alignTo(P->p_memsz, Target->PageSize);
- }
-
- if (P->p_type == PT_TLS && P->p_memsz) {
- if (!Config->Shared &&
- (Config->EMachine == EM_ARM || Config->EMachine == EM_AARCH64)) {
- // On ARM/AArch64, reserve extra space (8 words) between the thread
- // pointer and an executable's TLS segment by overaligning the segment.
- // This reservation is needed for backwards compatibility with Android's
- // TCB, which allocates several slots after the thread pointer (e.g.
- // TLS_SLOT_STACK_GUARD==5). For simplicity, this overalignment is also
- // done on other operating systems.
- P->p_align = std::max<uint64_t>(P->p_align, Config->Wordsize * 8);
- }
-
- // The TLS pointer goes after PT_TLS for variant 2 targets. At least glibc
- // will align it, so round up the size to make sure the offsets are
- // correct.
- P->p_memsz = alignTo(P->p_memsz, P->p_align);
+ p->p_memsz = alignTo(p->p_memsz, config->commonPageSize);
}
}
}
@@ -2214,37 +2361,37 @@ template <class ELFT> void Writer<ELFT>::setPhdrs() {
// A helper struct for checkSectionOverlap.
namespace {
struct SectionOffset {
- OutputSection *Sec;
- uint64_t Offset;
+ OutputSection *sec;
+ uint64_t offset;
};
} // namespace
// Check whether sections overlap for a specific address range (file offsets,
// load and virtual adresses).
-static void checkOverlap(StringRef Name, std::vector<SectionOffset> &Sections,
- bool IsVirtualAddr) {
- llvm::sort(Sections, [=](const SectionOffset &A, const SectionOffset &B) {
- return A.Offset < B.Offset;
+static void checkOverlap(StringRef name, std::vector<SectionOffset> &sections,
+ bool isVirtualAddr) {
+ llvm::sort(sections, [=](const SectionOffset &a, const SectionOffset &b) {
+ return a.offset < b.offset;
});
// Finding overlap is easy given a vector is sorted by start position.
// If an element starts before the end of the previous element, they overlap.
- for (size_t I = 1, End = Sections.size(); I < End; ++I) {
- SectionOffset A = Sections[I - 1];
- SectionOffset B = Sections[I];
- if (B.Offset >= A.Offset + A.Sec->Size)
+ for (size_t i = 1, end = sections.size(); i < end; ++i) {
+ SectionOffset a = sections[i - 1];
+ SectionOffset b = sections[i];
+ if (b.offset >= a.offset + a.sec->size)
continue;
// If both sections are in OVERLAY we allow the overlapping of virtual
// addresses, because it is what OVERLAY was designed for.
- if (IsVirtualAddr && A.Sec->InOverlay && B.Sec->InOverlay)
+ if (isVirtualAddr && a.sec->inOverlay && b.sec->inOverlay)
continue;
- errorOrWarn("section " + A.Sec->Name + " " + Name +
- " range overlaps with " + B.Sec->Name + "\n>>> " + A.Sec->Name +
- " range is " + rangeToString(A.Offset, A.Sec->Size) + "\n>>> " +
- B.Sec->Name + " range is " +
- rangeToString(B.Offset, B.Sec->Size));
+ errorOrWarn("section " + a.sec->name + " " + name +
+ " range overlaps with " + b.sec->name + "\n>>> " + a.sec->name +
+ " range is " + rangeToString(a.offset, a.sec->size) + "\n>>> " +
+ b.sec->name + " range is " +
+ rangeToString(b.offset, b.sec->size));
}
}
@@ -2255,11 +2402,11 @@ static void checkOverlap(StringRef Name, std::vector<SectionOffset> &Sections,
// ranges and the virtual address ranges don't overlap
template <class ELFT> void Writer<ELFT>::checkSections() {
// First, check that section's VAs fit in available address space for target.
- for (OutputSection *OS : OutputSections)
- if ((OS->Addr + OS->Size < OS->Addr) ||
- (!ELFT::Is64Bits && OS->Addr + OS->Size > UINT32_MAX))
- errorOrWarn("section " + OS->Name + " at 0x" + utohexstr(OS->Addr) +
- " of size 0x" + utohexstr(OS->Size) +
+ for (OutputSection *os : outputSections)
+ if ((os->addr + os->size < os->addr) ||
+ (!ELFT::Is64Bits && os->addr + os->size > UINT32_MAX))
+ errorOrWarn("section " + os->name + " at 0x" + utohexstr(os->addr) +
+ " of size 0x" + utohexstr(os->size) +
" exceeds available address space");
// Check for overlapping file offsets. In this case we need to skip any
@@ -2267,17 +2414,17 @@ template <class ELFT> void Writer<ELFT>::checkSections() {
// the file so Sec->Offset + Sec->Size can overlap with others. If --oformat
// binary is specified only add SHF_ALLOC sections are added to the output
// file so we skip any non-allocated sections in that case.
- std::vector<SectionOffset> FileOffs;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Size > 0 && Sec->Type != SHT_NOBITS &&
- (!Config->OFormatBinary || (Sec->Flags & SHF_ALLOC)))
- FileOffs.push_back({Sec, Sec->Offset});
- checkOverlap("file", FileOffs, false);
+ std::vector<SectionOffset> fileOffs;
+ for (OutputSection *sec : outputSections)
+ if (sec->size > 0 && sec->type != SHT_NOBITS &&
+ (!config->oFormatBinary || (sec->flags & SHF_ALLOC)))
+ fileOffs.push_back({sec, sec->offset});
+ checkOverlap("file", fileOffs, false);
// When linking with -r there is no need to check for overlapping virtual/load
// addresses since those addresses will only be assigned when the final
// executable/shared object is created.
- if (Config->Relocatable)
+ if (config->relocatable)
return;
// Checking for overlapping virtual and load addresses only needs to take
@@ -2285,20 +2432,20 @@ template <class ELFT> void Writer<ELFT>::checkSections() {
// Furthermore, we also need to skip SHF_TLS sections since these will be
// mapped to other addresses at runtime and can therefore have overlapping
// ranges in the file.
- std::vector<SectionOffset> VMAs;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Size > 0 && (Sec->Flags & SHF_ALLOC) && !(Sec->Flags & SHF_TLS))
- VMAs.push_back({Sec, Sec->Addr});
- checkOverlap("virtual address", VMAs, true);
+ std::vector<SectionOffset> vmas;
+ for (OutputSection *sec : outputSections)
+ if (sec->size > 0 && (sec->flags & SHF_ALLOC) && !(sec->flags & SHF_TLS))
+ vmas.push_back({sec, sec->addr});
+ checkOverlap("virtual address", vmas, true);
// Finally, check that the load addresses don't overlap. This will usually be
// the same as the virtual addresses but can be different when using a linker
// script with AT().
- std::vector<SectionOffset> LMAs;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Size > 0 && (Sec->Flags & SHF_ALLOC) && !(Sec->Flags & SHF_TLS))
- LMAs.push_back({Sec, Sec->getLMA()});
- checkOverlap("load address", LMAs, false);
+ std::vector<SectionOffset> lmas;
+ for (OutputSection *sec : outputSections)
+ if (sec->size > 0 && (sec->flags & SHF_ALLOC) && !(sec->flags & SHF_TLS))
+ lmas.push_back({sec, sec->getLMA()});
+ checkOverlap("load address", lmas, false);
}
// The entry point address is chosen in the following ways.
@@ -2311,89 +2458,45 @@ template <class ELFT> void Writer<ELFT>::checkSections() {
// 6. the address 0.
static uint64_t getEntryAddr() {
// Case 1, 2 or 3
- if (Symbol *B = Symtab->find(Config->Entry))
- return B->getVA();
+ if (Symbol *b = symtab->find(config->entry))
+ return b->getVA();
// Case 4
- uint64_t Addr;
- if (to_integer(Config->Entry, Addr))
- return Addr;
+ uint64_t addr;
+ if (to_integer(config->entry, addr))
+ return addr;
// Case 5
- if (OutputSection *Sec = findSection(".text")) {
- if (Config->WarnMissingEntry)
- warn("cannot find entry symbol " + Config->Entry + "; defaulting to 0x" +
- utohexstr(Sec->Addr));
- return Sec->Addr;
+ if (OutputSection *sec = findSection(".text")) {
+ if (config->warnMissingEntry)
+ warn("cannot find entry symbol " + config->entry + "; defaulting to 0x" +
+ utohexstr(sec->addr));
+ return sec->addr;
}
// Case 6
- if (Config->WarnMissingEntry)
- warn("cannot find entry symbol " + Config->Entry +
+ if (config->warnMissingEntry)
+ warn("cannot find entry symbol " + config->entry +
"; not setting start address");
return 0;
}
static uint16_t getELFType() {
- if (Config->Pic)
+ if (config->isPic)
return ET_DYN;
- if (Config->Relocatable)
+ if (config->relocatable)
return ET_REL;
return ET_EXEC;
}
-static uint8_t getAbiVersion() {
- // MIPS non-PIC executable gets ABI version 1.
- if (Config->EMachine == EM_MIPS && getELFType() == ET_EXEC &&
- (Config->EFlags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC)
- return 1;
- return 0;
-}
-
template <class ELFT> void Writer<ELFT>::writeHeader() {
- uint8_t *Buf = Buffer->getBufferStart();
-
- // For executable segments, the trap instructions are written before writing
- // the header. Setting Elf header bytes to zero ensures that any unused bytes
- // in header are zero-cleared, instead of having trap instructions.
- memset(Buf, 0, sizeof(Elf_Ehdr));
- memcpy(Buf, "\177ELF", 4);
-
- // Write the ELF header.
- auto *EHdr = reinterpret_cast<Elf_Ehdr *>(Buf);
- EHdr->e_ident[EI_CLASS] = Config->Is64 ? ELFCLASS64 : ELFCLASS32;
- EHdr->e_ident[EI_DATA] = Config->IsLE ? ELFDATA2LSB : ELFDATA2MSB;
- EHdr->e_ident[EI_VERSION] = EV_CURRENT;
- EHdr->e_ident[EI_OSABI] = Config->OSABI;
- EHdr->e_ident[EI_ABIVERSION] = getAbiVersion();
- EHdr->e_type = getELFType();
- EHdr->e_machine = Config->EMachine;
- EHdr->e_version = EV_CURRENT;
- EHdr->e_entry = getEntryAddr();
- EHdr->e_shoff = SectionHeaderOff;
- EHdr->e_flags = Config->EFlags;
- EHdr->e_ehsize = sizeof(Elf_Ehdr);
- EHdr->e_phnum = Phdrs.size();
- EHdr->e_shentsize = sizeof(Elf_Shdr);
-
- if (!Config->Relocatable) {
- EHdr->e_phoff = sizeof(Elf_Ehdr);
- EHdr->e_phentsize = sizeof(Elf_Phdr);
- }
-
- // Write the program header table.
- auto *HBuf = reinterpret_cast<Elf_Phdr *>(Buf + EHdr->e_phoff);
- for (PhdrEntry *P : Phdrs) {
- HBuf->p_type = P->p_type;
- HBuf->p_flags = P->p_flags;
- HBuf->p_offset = P->p_offset;
- HBuf->p_vaddr = P->p_vaddr;
- HBuf->p_paddr = P->p_paddr;
- HBuf->p_filesz = P->p_filesz;
- HBuf->p_memsz = P->p_memsz;
- HBuf->p_align = P->p_align;
- ++HBuf;
- }
+ writeEhdr<ELFT>(Out::bufferStart, *mainPart);
+ writePhdrs<ELFT>(Out::bufferStart + sizeof(Elf_Ehdr), *mainPart);
+
+ auto *eHdr = reinterpret_cast<Elf_Ehdr *>(Out::bufferStart);
+ eHdr->e_type = getELFType();
+ eHdr->e_entry = getEntryAddr();
+ eHdr->e_shoff = sectionHeaderOff;
// Write the section header table.
//
@@ -2404,57 +2507,58 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
// the value. The sentinel values and fields are:
// e_shnum = 0, SHdrs[0].sh_size = number of sections.
// e_shstrndx = SHN_XINDEX, SHdrs[0].sh_link = .shstrtab section index.
- auto *SHdrs = reinterpret_cast<Elf_Shdr *>(Buf + EHdr->e_shoff);
- size_t Num = OutputSections.size() + 1;
- if (Num >= SHN_LORESERVE)
- SHdrs->sh_size = Num;
+ auto *sHdrs = reinterpret_cast<Elf_Shdr *>(Out::bufferStart + eHdr->e_shoff);
+ size_t num = outputSections.size() + 1;
+ if (num >= SHN_LORESERVE)
+ sHdrs->sh_size = num;
else
- EHdr->e_shnum = Num;
+ eHdr->e_shnum = num;
- uint32_t StrTabIndex = In.ShStrTab->getParent()->SectionIndex;
- if (StrTabIndex >= SHN_LORESERVE) {
- SHdrs->sh_link = StrTabIndex;
- EHdr->e_shstrndx = SHN_XINDEX;
+ uint32_t strTabIndex = in.shStrTab->getParent()->sectionIndex;
+ if (strTabIndex >= SHN_LORESERVE) {
+ sHdrs->sh_link = strTabIndex;
+ eHdr->e_shstrndx = SHN_XINDEX;
} else {
- EHdr->e_shstrndx = StrTabIndex;
+ eHdr->e_shstrndx = strTabIndex;
}
- for (OutputSection *Sec : OutputSections)
- Sec->writeHeaderTo<ELFT>(++SHdrs);
+ for (OutputSection *sec : outputSections)
+ sec->writeHeaderTo<ELFT>(++sHdrs);
}
// Open a result file.
template <class ELFT> void Writer<ELFT>::openFile() {
- uint64_t MaxSize = Config->Is64 ? INT64_MAX : UINT32_MAX;
- if (MaxSize < FileSize) {
- error("output file too large: " + Twine(FileSize) + " bytes");
+ uint64_t maxSize = config->is64 ? INT64_MAX : UINT32_MAX;
+ if (fileSize != size_t(fileSize) || maxSize < fileSize) {
+ error("output file too large: " + Twine(fileSize) + " bytes");
return;
}
- unlinkAsync(Config->OutputFile);
- unsigned Flags = 0;
- if (!Config->Relocatable)
- Flags = FileOutputBuffer::F_executable;
- Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(Config->OutputFile, FileSize, Flags);
+ unlinkAsync(config->outputFile);
+ unsigned flags = 0;
+ if (!config->relocatable)
+ flags = FileOutputBuffer::F_executable;
+ Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr =
+ FileOutputBuffer::create(config->outputFile, fileSize, flags);
- if (!BufferOrErr)
- error("failed to open " + Config->OutputFile + ": " +
- llvm::toString(BufferOrErr.takeError()));
- else
- Buffer = std::move(*BufferOrErr);
+ if (!bufferOrErr) {
+ error("failed to open " + config->outputFile + ": " +
+ llvm::toString(bufferOrErr.takeError()));
+ return;
+ }
+ buffer = std::move(*bufferOrErr);
+ Out::bufferStart = buffer->getBufferStart();
}
template <class ELFT> void Writer<ELFT>::writeSectionsBinary() {
- uint8_t *Buf = Buffer->getBufferStart();
- for (OutputSection *Sec : OutputSections)
- if (Sec->Flags & SHF_ALLOC)
- Sec->writeTo<ELFT>(Buf + Sec->Offset);
+ for (OutputSection *sec : outputSections)
+ if (sec->flags & SHF_ALLOC)
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
}
-static void fillTrap(uint8_t *I, uint8_t *End) {
- for (; I + 4 <= End; I += 4)
- memcpy(I, &Target->TrapInstr, 4);
+static void fillTrap(uint8_t *i, uint8_t *end) {
+ for (; i + 4 <= end; i += 4)
+ memcpy(i, &target->trapInstr, 4);
}
// Fill the last page of executable segments with trap instructions
@@ -2464,61 +2568,119 @@ static void fillTrap(uint8_t *I, uint8_t *End) {
// We'll leave other pages in segments as-is because the rest will be
// overwritten by output sections.
template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
- if (Script->HasSectionsCommand)
+ if (script->hasSectionsCommand)
return;
- // Fill the last page.
- uint8_t *Buf = Buffer->getBufferStart();
- for (PhdrEntry *P : Phdrs)
- if (P->p_type == PT_LOAD && (P->p_flags & PF_X))
- fillTrap(Buf + alignDown(P->p_offset + P->p_filesz, Target->PageSize),
- Buf + alignTo(P->p_offset + P->p_filesz, Target->PageSize));
-
- // Round up the file size of the last segment to the page boundary iff it is
- // an executable segment to ensure that other tools don't accidentally
- // trim the instruction padding (e.g. when stripping the file).
- PhdrEntry *Last = nullptr;
- for (PhdrEntry *P : Phdrs)
- if (P->p_type == PT_LOAD)
- Last = P;
-
- if (Last && (Last->p_flags & PF_X))
- Last->p_memsz = Last->p_filesz = alignTo(Last->p_filesz, Target->PageSize);
+ for (Partition &part : partitions) {
+ // Fill the last page.
+ for (PhdrEntry *p : part.phdrs)
+ if (p->p_type == PT_LOAD && (p->p_flags & PF_X))
+ fillTrap(Out::bufferStart + alignDown(p->firstSec->offset + p->p_filesz,
+ config->commonPageSize),
+ Out::bufferStart + alignTo(p->firstSec->offset + p->p_filesz,
+ config->commonPageSize));
+
+ // Round up the file size of the last segment to the page boundary iff it is
+ // an executable segment to ensure that other tools don't accidentally
+ // trim the instruction padding (e.g. when stripping the file).
+ PhdrEntry *last = nullptr;
+ for (PhdrEntry *p : part.phdrs)
+ if (p->p_type == PT_LOAD)
+ last = p;
+
+ if (last && (last->p_flags & PF_X))
+ last->p_memsz = last->p_filesz =
+ alignTo(last->p_filesz, config->commonPageSize);
+ }
}
// Write section contents to a mmap'ed file.
template <class ELFT> void Writer<ELFT>::writeSections() {
- uint8_t *Buf = Buffer->getBufferStart();
-
- OutputSection *EhFrameHdr = nullptr;
- if (In.EhFrameHdr && !In.EhFrameHdr->empty())
- EhFrameHdr = In.EhFrameHdr->getParent();
-
// In -r or -emit-relocs mode, write the relocation sections first as in
// ELf_Rel targets we might find out that we need to modify the relocated
// section while doing it.
- for (OutputSection *Sec : OutputSections)
- if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA)
- Sec->writeTo<ELFT>(Buf + Sec->Offset);
+ for (OutputSection *sec : outputSections)
+ if (sec->type == SHT_REL || sec->type == SHT_RELA)
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
+
+ for (OutputSection *sec : outputSections)
+ if (sec->type != SHT_REL && sec->type != SHT_RELA)
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
+}
+
+// Split one uint8 array into small pieces of uint8 arrays.
+static std::vector<ArrayRef<uint8_t>> split(ArrayRef<uint8_t> arr,
+ size_t chunkSize) {
+ std::vector<ArrayRef<uint8_t>> ret;
+ while (arr.size() > chunkSize) {
+ ret.push_back(arr.take_front(chunkSize));
+ arr = arr.drop_front(chunkSize);
+ }
+ if (!arr.empty())
+ ret.push_back(arr);
+ return ret;
+}
- for (OutputSection *Sec : OutputSections)
- if (Sec != EhFrameHdr && Sec->Type != SHT_REL && Sec->Type != SHT_RELA)
- Sec->writeTo<ELFT>(Buf + Sec->Offset);
+// Computes a hash value of Data using a given hash function.
+// In order to utilize multiple cores, we first split data into 1MB
+// chunks, compute a hash for each chunk, and then compute a hash value
+// of the hash values.
+static void
+computeHash(llvm::MutableArrayRef<uint8_t> hashBuf,
+ llvm::ArrayRef<uint8_t> data,
+ std::function<void(uint8_t *dest, ArrayRef<uint8_t> arr)> hashFn) {
+ std::vector<ArrayRef<uint8_t>> chunks = split(data, 1024 * 1024);
+ std::vector<uint8_t> hashes(chunks.size() * hashBuf.size());
+
+ // Compute hash values.
+ parallelForEachN(0, chunks.size(), [&](size_t i) {
+ hashFn(hashes.data() + i * hashBuf.size(), chunks[i]);
+ });
- // The .eh_frame_hdr depends on .eh_frame section contents, therefore
- // it should be written after .eh_frame is written.
- if (EhFrameHdr)
- EhFrameHdr->writeTo<ELFT>(Buf + EhFrameHdr->Offset);
+ // Write to the final output buffer.
+ hashFn(hashBuf.data(), hashes);
}
template <class ELFT> void Writer<ELFT>::writeBuildId() {
- if (!In.BuildId || !In.BuildId->getParent())
+ if (!mainPart->buildId || !mainPart->buildId->getParent())
return;
+ if (config->buildId == BuildIdKind::Hexstring) {
+ for (Partition &part : partitions)
+ part.buildId->writeBuildId(config->buildIdVector);
+ return;
+ }
+
// Compute a hash of all sections of the output file.
- uint8_t *Start = Buffer->getBufferStart();
- uint8_t *End = Start + FileSize;
- In.BuildId->writeBuildId({Start, End});
+ size_t hashSize = mainPart->buildId->hashSize;
+ std::vector<uint8_t> buildId(hashSize);
+ llvm::ArrayRef<uint8_t> buf{Out::bufferStart, size_t(fileSize)};
+
+ switch (config->buildId) {
+ case BuildIdKind::Fast:
+ computeHash(buildId, buf, [](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ write64le(dest, xxHash64(arr));
+ });
+ break;
+ case BuildIdKind::Md5:
+ computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ memcpy(dest, MD5::hash(arr).data(), hashSize);
+ });
+ break;
+ case BuildIdKind::Sha1:
+ computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ memcpy(dest, SHA1::hash(arr).data(), hashSize);
+ });
+ break;
+ case BuildIdKind::Uuid:
+ if (auto ec = llvm::getRandomBytes(buildId.data(), hashSize))
+ error("entropy source failure: " + ec.message());
+ break;
+ default:
+ llvm_unreachable("unknown BuildIdKind");
+ }
+ for (Partition &part : partitions)
+ part.buildId->writeBuildId(buildId);
}
template void elf::writeResult<ELF32LE>();
diff --git a/ELF/Writer.h b/ELF/Writer.h
index 7806f824c58f9..784fba9c75a6d 100644
--- a/ELF/Writer.h
+++ b/ELF/Writer.h
@@ -1,9 +1,8 @@
//===- Writer.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -20,16 +19,14 @@ namespace elf {
class InputFile;
class OutputSection;
class InputSectionBase;
-template <class ELFT> class ObjFile;
-class SymbolTable;
template <class ELFT> void writeResult();
// This describes a program header entry.
// Each contains type, access flags and range of output sections that will be
// placed in it.
struct PhdrEntry {
- PhdrEntry(unsigned Type, unsigned Flags) : p_type(Type), p_flags(Flags) {}
- void add(OutputSection *Sec);
+ PhdrEntry(unsigned type, unsigned flags) : p_type(type), p_flags(flags) {}
+ void add(OutputSection *sec);
uint64_t p_paddr = 0;
uint64_t p_vaddr = 0;
@@ -40,22 +37,22 @@ struct PhdrEntry {
uint32_t p_type = 0;
uint32_t p_flags = 0;
- OutputSection *FirstSec = nullptr;
- OutputSection *LastSec = nullptr;
- bool HasLMA = false;
+ OutputSection *firstSec = nullptr;
+ OutputSection *lastSec = nullptr;
+ bool hasLMA = false;
- uint64_t LMAOffset = 0;
+ uint64_t lmaOffset = 0;
};
void addReservedSymbols();
-llvm::StringRef getOutputSectionName(const InputSectionBase *S);
+llvm::StringRef getOutputSectionName(const InputSectionBase *s);
template <class ELFT> uint32_t calcMipsEFlags();
-uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
- llvm::StringRef FileName);
+uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
+ llvm::StringRef fileName);
-bool isMipsN32Abi(const InputFile *F);
+bool isMipsN32Abi(const InputFile *f);
bool isMicroMips();
bool isMipsR6();
} // namespace elf