diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:51:32 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:51:32 +0000 |
commit | f1e1c239e31b467e17f1648b1f524fc9ab5b431a (patch) | |
tree | a855e7a2a8808555da60e6aa9601d6867eb23bac /ELF | |
parent | 7d6988fdd2aee0e033034e147f16fe05594a60e4 (diff) | |
download | src-f1e1c239e31b467e17f1648b1f524fc9ab5b431a.tar.gz src-f1e1c239e31b467e17f1648b1f524fc9ab5b431a.zip |
Vendor import of stripped lld trunk r366426 (just before the release_90vendor/lld/lld-trunk-r366426
branch point):
https://llvm.org/svn/llvm-project/lld/trunk@366426
Notes
Notes:
svn path=/vendor/lld/dist/; revision=351288
svn path=/vendor/lld/lld-trunk-r366426/; revision=351289; tag=vendor/lld/lld-trunk-r366426
Diffstat (limited to 'ELF')
65 files changed, 15824 insertions, 13284 deletions
diff --git a/ELF/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp index ac753cb58265..b2eda4dcbc4e 100644 --- a/ELF/AArch64ErrataFix.cpp +++ b/ELF/AArch64ErrataFix.cpp @@ -1,9 +1,8 @@ //===- AArch64ErrataFix.cpp -----------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This file implements Section Patching for the purpose of working around @@ -57,8 +56,8 @@ using namespace lld::elf; // ADRP // | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) | -static bool isADRP(uint32_t Instr) { - return (Instr & 0x9f000000) == 0x90000000; +static bool isADRP(uint32_t instr) { + return (instr & 0x9f000000) == 0x90000000; } // Load and store bit patterns from ARMv8-A ARM ARM. @@ -67,8 +66,8 @@ static bool isADRP(uint32_t Instr) { // All loads and stores have 1 (at bit postion 27), (0 at bit position 25). // | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) | -static bool isLoadStoreClass(uint32_t Instr) { - return (Instr & 0x0a000000) == 0x08000000; +static bool isLoadStoreClass(uint32_t instr) { + return (instr & 0x0a000000) == 0x08000000; } // LDN/STN multiple no offset @@ -83,20 +82,20 @@ static bool isLoadStoreClass(uint32_t Instr) { // opcode == 0110 ST1 3 registers. // opcode == 0111 ST1 1 register. // opcode == 1010 ST1 2 registers. -static bool isST1MultipleOpcode(uint32_t Instr) { - return (Instr & 0x0000f000) == 0x00002000 || - (Instr & 0x0000f000) == 0x00006000 || - (Instr & 0x0000f000) == 0x00007000 || - (Instr & 0x0000f000) == 0x0000a000; +static bool isST1MultipleOpcode(uint32_t instr) { + return (instr & 0x0000f000) == 0x00002000 || + (instr & 0x0000f000) == 0x00006000 || + (instr & 0x0000f000) == 0x00007000 || + (instr & 0x0000f000) == 0x0000a000; } -static bool isST1Multiple(uint32_t Instr) { - return (Instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(Instr); +static bool isST1Multiple(uint32_t instr) { + return (instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(instr); } // Writes to Rn (writeback). -static bool isST1MultiplePost(uint32_t Instr) { - return (Instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(Instr); +static bool isST1MultiplePost(uint32_t instr) { + return (instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(instr); } // LDN/STN single no offset @@ -111,41 +110,41 @@ static bool isST1MultiplePost(uint32_t Instr) { // opcode == 000 ST1 8-bit. // opcode == 010 ST1 16-bit. // opcode == 100 ST1 32 or 64-bit (Size determines which). -static bool isST1SingleOpcode(uint32_t Instr) { - return (Instr & 0x0040e000) == 0x00000000 || - (Instr & 0x0040e000) == 0x00004000 || - (Instr & 0x0040e000) == 0x00008000; +static bool isST1SingleOpcode(uint32_t instr) { + return (instr & 0x0040e000) == 0x00000000 || + (instr & 0x0040e000) == 0x00004000 || + (instr & 0x0040e000) == 0x00008000; } -static bool isST1Single(uint32_t Instr) { - return (Instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(Instr); +static bool isST1Single(uint32_t instr) { + return (instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(instr); } // Writes to Rn (writeback). -static bool isST1SinglePost(uint32_t Instr) { - return (Instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(Instr); +static bool isST1SinglePost(uint32_t instr) { + return (instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(instr); } -static bool isST1(uint32_t Instr) { - return isST1Multiple(Instr) || isST1MultiplePost(Instr) || - isST1Single(Instr) || isST1SinglePost(Instr); +static bool isST1(uint32_t instr) { + return isST1Multiple(instr) || isST1MultiplePost(instr) || + isST1Single(instr) || isST1SinglePost(instr); } // Load/store exclusive // | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for Stores. -static bool isLoadStoreExclusive(uint32_t Instr) { - return (Instr & 0x3f000000) == 0x08000000; +static bool isLoadStoreExclusive(uint32_t instr) { + return (instr & 0x3f000000) == 0x08000000; } -static bool isLoadExclusive(uint32_t Instr) { - return (Instr & 0x3f400000) == 0x08400000; +static bool isLoadExclusive(uint32_t instr) { + return (instr & 0x3f400000) == 0x08400000; } // Load register literal // | opc (2) 01 | 1 V 00 | imm19 | Rt (5) | -static bool isLoadLiteral(uint32_t Instr) { - return (Instr & 0x3b000000) == 0x18000000; +static bool isLoadLiteral(uint32_t instr) { + return (instr & 0x3b000000) == 0x18000000; } // Load/store no-allocate pair @@ -153,8 +152,8 @@ static bool isLoadLiteral(uint32_t Instr) { // | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for stores. // Never writes to register -static bool isSTNP(uint32_t Instr) { - return (Instr & 0x3bc00000) == 0x28000000; +static bool isSTNP(uint32_t instr) { + return (instr & 0x3bc00000) == 0x28000000; } // Load/store register pair @@ -162,69 +161,69 @@ static bool isSTNP(uint32_t Instr) { // | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP // Writes to Rn. -static bool isSTPPost(uint32_t Instr) { - return (Instr & 0x3bc00000) == 0x28800000; +static bool isSTPPost(uint32_t instr) { + return (instr & 0x3bc00000) == 0x28800000; } // (offset) // | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | -static bool isSTPOffset(uint32_t Instr) { - return (Instr & 0x3bc00000) == 0x29000000; +static bool isSTPOffset(uint32_t instr) { + return (instr & 0x3bc00000) == 0x29000000; } // (pre-index) // | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // Writes to Rn. -static bool isSTPPre(uint32_t Instr) { - return (Instr & 0x3bc00000) == 0x29800000; +static bool isSTPPre(uint32_t instr) { + return (instr & 0x3bc00000) == 0x29800000; } -static bool isSTP(uint32_t Instr) { - return isSTPPost(Instr) || isSTPOffset(Instr) || isSTPPre(Instr); +static bool isSTP(uint32_t instr) { + return isSTPPost(instr) || isSTPOffset(instr) || isSTPPre(instr); } // Load/store register (unscaled immediate) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) | // V == 0 for Scalar, V == 1 for Simd/FP. -static bool isLoadStoreUnscaled(uint32_t Instr) { - return (Instr & 0x3b000c00) == 0x38000000; +static bool isLoadStoreUnscaled(uint32_t instr) { + return (instr & 0x3b000c00) == 0x38000000; } // Load/store register (immediate post-indexed) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) | -static bool isLoadStoreImmediatePost(uint32_t Instr) { - return (Instr & 0x3b200c00) == 0x38000400; +static bool isLoadStoreImmediatePost(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000400; } // Load/store register (unprivileged) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) | -static bool isLoadStoreUnpriv(uint32_t Instr) { - return (Instr & 0x3b200c00) == 0x38000800; +static bool isLoadStoreUnpriv(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000800; } // Load/store register (immediate pre-indexed) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) | -static bool isLoadStoreImmediatePre(uint32_t Instr) { - return (Instr & 0x3b200c00) == 0x38000c00; +static bool isLoadStoreImmediatePre(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000c00; } // Load/store register (register offset) // | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt | -static bool isLoadStoreRegisterOff(uint32_t Instr) { - return (Instr & 0x3b200c00) == 0x38200800; +static bool isLoadStoreRegisterOff(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38200800; } // Load/store register (unsigned immediate) // | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) | -static bool isLoadStoreRegisterUnsigned(uint32_t Instr) { - return (Instr & 0x3b000000) == 0x39000000; +static bool isLoadStoreRegisterUnsigned(uint32_t instr) { + return (instr & 0x3b000000) == 0x39000000; } // Rt is always in bit position 0 - 4. -static uint32_t getRt(uint32_t Instr) { return (Instr & 0x1f); } +static uint32_t getRt(uint32_t instr) { return (instr & 0x1f); } // Rn is always in bit position 5 - 9. -static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; } +static uint32_t getRn(uint32_t instr) { return (instr >> 5) & 0x1f; } // C4.1.2 Branches, Exception Generating and System instructions // | op0 (3) 1 | 01 op1 (4) | x (22) | @@ -233,41 +232,41 @@ static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; } // op0 == x00 101 op1 == xxxx Unconditional Branch immediate. // op0 == x01 101 op1 == 0xxx Compare and branch immediate. // op0 == x01 101 op1 == 1xxx Test and branch immediate. -static bool isBranch(uint32_t Instr) { - return ((Instr & 0xfe000000) == 0xd6000000) || // Cond branch. - ((Instr & 0xfe000000) == 0x54000000) || // Uncond branch reg. - ((Instr & 0x7c000000) == 0x14000000) || // Uncond branch imm. - ((Instr & 0x7c000000) == 0x34000000); // Compare and test branch. +static bool isBranch(uint32_t instr) { + return ((instr & 0xfe000000) == 0xd6000000) || // Cond branch. + ((instr & 0xfe000000) == 0x54000000) || // Uncond branch reg. + ((instr & 0x7c000000) == 0x14000000) || // Uncond branch imm. + ((instr & 0x7c000000) == 0x34000000); // Compare and test branch. } -static bool isV8SingleRegisterNonStructureLoadStore(uint32_t Instr) { - return isLoadStoreUnscaled(Instr) || isLoadStoreImmediatePost(Instr) || - isLoadStoreUnpriv(Instr) || isLoadStoreImmediatePre(Instr) || - isLoadStoreRegisterOff(Instr) || isLoadStoreRegisterUnsigned(Instr); +static bool isV8SingleRegisterNonStructureLoadStore(uint32_t instr) { + return isLoadStoreUnscaled(instr) || isLoadStoreImmediatePost(instr) || + isLoadStoreUnpriv(instr) || isLoadStoreImmediatePre(instr) || + isLoadStoreRegisterOff(instr) || isLoadStoreRegisterUnsigned(instr); } // Note that this function refers to v8.0 only and does not include the // additional load and store instructions added for in later revisions of // the architecture such as the Atomic memory operations introduced // in v8.1. -static bool isV8NonStructureLoad(uint32_t Instr) { - if (isLoadExclusive(Instr)) +static bool isV8NonStructureLoad(uint32_t instr) { + if (isLoadExclusive(instr)) return true; - if (isLoadLiteral(Instr)) + if (isLoadLiteral(instr)) return true; - else if (isV8SingleRegisterNonStructureLoadStore(Instr)) { + else if (isV8SingleRegisterNonStructureLoadStore(instr)) { // For Load and Store single register, Loads are derived from a // combination of the Size, V and Opc fields. - uint32_t Size = (Instr >> 30) & 0xff; - uint32_t V = (Instr >> 26) & 0x1; - uint32_t Opc = (Instr >> 22) & 0x3; + uint32_t size = (instr >> 30) & 0xff; + uint32_t v = (instr >> 26) & 0x1; + uint32_t opc = (instr >> 22) & 0x3; // For the load and store instructions that we are decoding. // Opc == 0 are all stores. // Opc == 1 with a couple of exceptions are loads. The exceptions are: // Size == 00 (0), V == 1, Opc == 10 (2) which is a store and // Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch. - return Opc != 0 && !(Size == 0 && V == 1 && Opc == 2) && - !(Size == 3 && V == 0 && Opc == 2); + return opc != 0 && !(size == 0 && v == 1 && opc == 2) && + !(size == 3 && v == 0 && opc == 2); } return false; } @@ -276,18 +275,18 @@ static bool isV8NonStructureLoad(uint32_t Instr) { // needed for errata 843419. // Instruction with writeback updates the index register after the load/store. -static bool hasWriteback(uint32_t Instr) { - return isLoadStoreImmediatePre(Instr) || isLoadStoreImmediatePost(Instr) || - isSTPPre(Instr) || isSTPPost(Instr) || isST1SinglePost(Instr) || - isST1MultiplePost(Instr); +static bool hasWriteback(uint32_t instr) { + return isLoadStoreImmediatePre(instr) || isLoadStoreImmediatePost(instr) || + isSTPPre(instr) || isSTPPost(instr) || isST1SinglePost(instr) || + isST1MultiplePost(instr); } // For the load and store class of instructions, a load can write to the // destination register, a load and a store can write to the base register when // the instruction has writeback. -static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) { - return (isV8NonStructureLoad(Instr) && getRt(Instr) == Reg) || - (hasWriteback(Instr) && getRn(Instr) == Reg); +static bool doesLoadStoreWriteToReg(uint32_t instr, uint32_t reg) { + return (isV8NonStructureLoad(instr) && getRt(instr) == reg) || + (hasWriteback(instr) && getRn(instr) == reg); } // Scanner for Cortex-A53 errata 843419 @@ -319,18 +318,18 @@ static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) { // Return true if the Instruction sequence Adrp, Instr2, and Instr4 match // the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.), // and 4.) in the Scanner for Cortex-A53 errata comment above. -static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2, - uint32_t Instr4) { - if (!isADRP(Instr1)) +static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2, + uint32_t instr4) { + if (!isADRP(instr1)) return false; - uint32_t Rn = getRt(Instr1); - return isLoadStoreClass(Instr2) && - (isLoadStoreExclusive(Instr2) || isLoadLiteral(Instr2) || - isV8SingleRegisterNonStructureLoadStore(Instr2) || isSTP(Instr2) || - isSTNP(Instr2) || isST1(Instr2)) && - !doesLoadStoreWriteToReg(Instr2, Rn) && - isLoadStoreRegisterUnsigned(Instr4) && getRn(Instr4) == Rn; + uint32_t rn = getRt(instr1); + return isLoadStoreClass(instr2) && + (isLoadStoreExclusive(instr2) || isLoadLiteral(instr2) || + isV8SingleRegisterNonStructureLoadStore(instr2) || isSTP(instr2) || + isSTNP(instr2) || isST1(instr2)) && + !doesLoadStoreWriteToReg(instr2, rn) && + isLoadStoreRegisterUnsigned(instr4) && getRn(instr4) == rn; } // Scan the instruction sequence starting at Offset Off from the base of @@ -339,143 +338,143 @@ static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2, // instructions we've scanned. // Return the offset of the load or store instruction in IS that we want to // patch or 0 if no patch required. -static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off, - uint64_t Limit) { - uint64_t ISAddr = IS->getVA(0); +static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, + uint64_t limit) { + uint64_t isecAddr = isec->getVA(0); // Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8. - uint64_t InitialPageOff = (ISAddr + Off) & 0xfff; - if (InitialPageOff < 0xff8) - Off += 0xff8 - InitialPageOff; + uint64_t initialPageOff = (isecAddr + off) & 0xfff; + if (initialPageOff < 0xff8) + off += 0xff8 - initialPageOff; - bool OptionalAllowed = Limit - Off > 12; - if (Off >= Limit || Limit - Off < 12) { + bool optionalAllowed = limit - off > 12; + if (off >= limit || limit - off < 12) { // Need at least 3 4-byte sized instructions to trigger erratum. - Off = Limit; + off = limit; return 0; } - uint64_t PatchOff = 0; - const uint8_t *Buf = IS->data().begin(); - const ulittle32_t *InstBuf = reinterpret_cast<const ulittle32_t *>(Buf + Off); - uint32_t Instr1 = *InstBuf++; - uint32_t Instr2 = *InstBuf++; - uint32_t Instr3 = *InstBuf++; - if (is843419ErratumSequence(Instr1, Instr2, Instr3)) { - PatchOff = Off + 8; - } else if (OptionalAllowed && !isBranch(Instr3)) { - uint32_t Instr4 = *InstBuf++; - if (is843419ErratumSequence(Instr1, Instr2, Instr4)) - PatchOff = Off + 12; + uint64_t patchOff = 0; + const uint8_t *buf = isec->data().begin(); + const ulittle32_t *instBuf = reinterpret_cast<const ulittle32_t *>(buf + off); + uint32_t instr1 = *instBuf++; + uint32_t instr2 = *instBuf++; + uint32_t instr3 = *instBuf++; + if (is843419ErratumSequence(instr1, instr2, instr3)) { + patchOff = off + 8; + } else if (optionalAllowed && !isBranch(instr3)) { + uint32_t instr4 = *instBuf++; + if (is843419ErratumSequence(instr1, instr2, instr4)) + patchOff = off + 12; } - if (((ISAddr + Off) & 0xfff) == 0xff8) - Off += 4; + if (((isecAddr + off) & 0xfff) == 0xff8) + off += 4; else - Off += 0xffc; - return PatchOff; + off += 0xffc; + return patchOff; } class lld::elf::Patch843419Section : public SyntheticSection { public: - Patch843419Section(InputSection *P, uint64_t Off); + Patch843419Section(InputSection *p, uint64_t off); - void writeTo(uint8_t *Buf) override; + void writeTo(uint8_t *buf) override; size_t getSize() const override { return 8; } uint64_t getLDSTAddr() const; // The Section we are patching. - const InputSection *Patchee; + const InputSection *patchee; // The offset of the instruction in the Patchee section we are patching. - uint64_t PatcheeOffset; + uint64_t patcheeOffset; // A label for the start of the Patch that we can use as a relocation target. - Symbol *PatchSym; + Symbol *patchSym; }; -lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off) +lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off) : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4, ".text.patch"), - Patchee(P), PatcheeOffset(Off) { - this->Parent = P->getParent(); - PatchSym = addSyntheticLocal( - Saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0, + patchee(p), patcheeOffset(off) { + this->parent = p->getParent(); + patchSym = addSyntheticLocal( + saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0, getSize(), *this); - addSyntheticLocal(Saver.save("$x"), STT_NOTYPE, 0, 0, *this); + addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this); } uint64_t lld::elf::Patch843419Section::getLDSTAddr() const { - return Patchee->getVA(PatcheeOffset); + return patchee->getVA(patcheeOffset); } -void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) { +void lld::elf::Patch843419Section::writeTo(uint8_t *buf) { // Copy the instruction that we will be replacing with a branch in the // Patchee Section. - write32le(Buf, read32le(Patchee->data().begin() + PatcheeOffset)); + write32le(buf, read32le(patchee->data().begin() + patcheeOffset)); // Apply any relocation transferred from the original PatcheeSection. - // For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc - // also adds OutSecOff so we need to subtract to avoid double counting. - this->relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + getSize()); + // For a SyntheticSection Buf already has outSecOff added, but relocateAlloc + // also adds outSecOff so we need to subtract to avoid double counting. + this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize()); // Return address is the next instruction after the one we have just copied. - uint64_t S = getLDSTAddr() + 4; - uint64_t P = PatchSym->getVA() + 4; - Target->relocateOne(Buf + 4, R_AARCH64_JUMP26, S - P); + uint64_t s = getLDSTAddr() + 4; + uint64_t p = patchSym->getVA() + 4; + target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p); } void AArch64Err843419Patcher::init() { // The AArch64 ABI permits data in executable sections. We must avoid scanning // this data as if it were instructions to avoid false matches. We use the // mapping symbols in the InputObjects to identify this data, caching the - // results in SectionMap so we don't have to recalculate it each pass. + // results in sectionMap so we don't have to recalculate it each pass. // The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe // half open intervals [Symbol Value, Next Symbol Value) of code and data // within sections. If there is no next symbol then the half open interval is // [Symbol Value, End of section). The type, code or data, is determined by // the mapping symbol name, $x for code, $d for data. - auto IsCodeMapSymbol = [](const Symbol *B) { - return B->getName() == "$x" || B->getName().startswith("$x."); + auto isCodeMapSymbol = [](const Symbol *b) { + return b->getName() == "$x" || b->getName().startswith("$x."); }; - auto IsDataMapSymbol = [](const Symbol *B) { - return B->getName() == "$d" || B->getName().startswith("$d."); + auto isDataMapSymbol = [](const Symbol *b) { + return b->getName() == "$d" || b->getName().startswith("$d."); }; // Collect mapping symbols for every executable InputSection. - for (InputFile *File : ObjectFiles) { - auto *F = cast<ObjFile<ELF64LE>>(File); - for (Symbol *B : F->getLocalSymbols()) { - auto *Def = dyn_cast<Defined>(B); - if (!Def) + for (InputFile *file : objectFiles) { + auto *f = cast<ObjFile<ELF64LE>>(file); + for (Symbol *b : f->getLocalSymbols()) { + auto *def = dyn_cast<Defined>(b); + if (!def) continue; - if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def)) + if (!isCodeMapSymbol(def) && !isDataMapSymbol(def)) continue; - if (auto *Sec = dyn_cast_or_null<InputSection>(Def->Section)) - if (Sec->Flags & SHF_EXECINSTR) - SectionMap[Sec].push_back(Def); + if (auto *sec = dyn_cast_or_null<InputSection>(def->section)) + if (sec->flags & SHF_EXECINSTR) + sectionMap[sec].push_back(def); } } // For each InputSection make sure the mapping symbols are in sorted in // ascending order and free from consecutive runs of mapping symbols with // the same type. For example we must remove the redundant $d.1 from $x.0 // $d.0 $d.1 $x.1. - for (auto &KV : SectionMap) { - std::vector<const Defined *> &MapSyms = KV.second; - if (MapSyms.size() <= 1) + for (auto &kv : sectionMap) { + std::vector<const Defined *> &mapSyms = kv.second; + if (mapSyms.size() <= 1) continue; - std::stable_sort( - MapSyms.begin(), MapSyms.end(), - [](const Defined *A, const Defined *B) { return A->Value < B->Value; }); - MapSyms.erase( - std::unique(MapSyms.begin(), MapSyms.end(), - [=](const Defined *A, const Defined *B) { - return (IsCodeMapSymbol(A) && IsCodeMapSymbol(B)) || - (IsDataMapSymbol(A) && IsDataMapSymbol(B)); + llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) { + return a->value < b->value; + }); + mapSyms.erase( + std::unique(mapSyms.begin(), mapSyms.end(), + [=](const Defined *a, const Defined *b) { + return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) || + (isDataMapSymbol(a) && isDataMapSymbol(b)); }), - MapSyms.end()); + mapSyms.end()); } - Initialized = true; + initialized = true; } // Insert the PatchSections we have created back into the @@ -484,60 +483,60 @@ void AArch64Err843419Patcher::init() { // executable sections, although we may need to insert them earlier if the // InputSectionDescription is larger than the maximum branch range. void AArch64Err843419Patcher::insertPatches( - InputSectionDescription &ISD, std::vector<Patch843419Section *> &Patches) { - uint64_t ISLimit; - uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff; - uint64_t PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing(); - uint64_t OutSecAddr = ISD.Sections.front()->getParent()->Addr; + InputSectionDescription &isd, std::vector<Patch843419Section *> &patches) { + uint64_t isecLimit; + uint64_t prevIsecLimit = isd.sections.front()->outSecOff; + uint64_t patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); + uint64_t outSecAddr = isd.sections.front()->getParent()->addr; - // Set the OutSecOff of patches to the place where we want to insert them. + // Set the outSecOff of patches to the place where we want to insert them. // We use a similar strategy to Thunk placement. Place patches roughly // every multiple of maximum branch range. - auto PatchIt = Patches.begin(); - auto PatchEnd = Patches.end(); - for (const InputSection *IS : ISD.Sections) { - ISLimit = IS->OutSecOff + IS->getSize(); - if (ISLimit > PatchUpperBound) { - while (PatchIt != PatchEnd) { - if ((*PatchIt)->getLDSTAddr() - OutSecAddr >= PrevISLimit) + auto patchIt = patches.begin(); + auto patchEnd = patches.end(); + for (const InputSection *isec : isd.sections) { + isecLimit = isec->outSecOff + isec->getSize(); + if (isecLimit > patchUpperBound) { + while (patchIt != patchEnd) { + if ((*patchIt)->getLDSTAddr() - outSecAddr >= prevIsecLimit) break; - (*PatchIt)->OutSecOff = PrevISLimit; - ++PatchIt; + (*patchIt)->outSecOff = prevIsecLimit; + ++patchIt; } - PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing(); + patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); } - PrevISLimit = ISLimit; + prevIsecLimit = isecLimit; } - for (; PatchIt != PatchEnd; ++PatchIt) { - (*PatchIt)->OutSecOff = ISLimit; + for (; patchIt != patchEnd; ++patchIt) { + (*patchIt)->outSecOff = isecLimit; } - // merge all patch sections. We use the OutSecOff assigned above to + // merge all patch sections. We use the outSecOff assigned above to // determine the insertion point. This is ok as we only merge into an // InputSectionDescription once per pass, and at the end of the pass - // assignAddresses() will recalculate all the OutSecOff values. - std::vector<InputSection *> Tmp; - Tmp.reserve(ISD.Sections.size() + Patches.size()); - auto MergeCmp = [](const InputSection *A, const InputSection *B) { - if (A->OutSecOff < B->OutSecOff) + // assignAddresses() will recalculate all the outSecOff values. + std::vector<InputSection *> tmp; + tmp.reserve(isd.sections.size() + patches.size()); + auto mergeCmp = [](const InputSection *a, const InputSection *b) { + if (a->outSecOff < b->outSecOff) return true; - if (A->OutSecOff == B->OutSecOff && isa<Patch843419Section>(A) && - !isa<Patch843419Section>(B)) + if (a->outSecOff == b->outSecOff && isa<Patch843419Section>(a) && + !isa<Patch843419Section>(b)) return true; return false; }; - std::merge(ISD.Sections.begin(), ISD.Sections.end(), Patches.begin(), - Patches.end(), std::back_inserter(Tmp), MergeCmp); - ISD.Sections = std::move(Tmp); + std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(), + patches.end(), std::back_inserter(tmp), mergeCmp); + isd.sections = std::move(tmp); } -// Given an erratum sequence that starts at address AdrpAddr, with an -// instruction that we need to patch at PatcheeOffset from the start of +// Given an erratum sequence that starts at address adrpAddr, with an +// instruction that we need to patch at patcheeOffset from the start of // InputSection IS, create a Patch843419 Section and add it to the // Patches that we need to insert. -static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset, - InputSection *IS, - std::vector<Patch843419Section *> &Patches) { +static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset, + InputSection *isec, + std::vector<Patch843419Section *> &patches) { // There may be a relocation at the same offset that we are patching. There // are four cases that we need to consider. // Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this @@ -552,29 +551,29 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset, // and replace the relocation with a R_AARCH_JUMP26 branch relocation. // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch // relocation at the offset. - auto RelIt = std::find_if( - IS->Relocations.begin(), IS->Relocations.end(), - [=](const Relocation &R) { return R.Offset == PatcheeOffset; }); - if (RelIt != IS->Relocations.end() && - (RelIt->Type == R_AARCH64_JUMP26 || RelIt->Expr == R_RELAX_TLS_IE_TO_LE)) + auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) { + return r.offset == patcheeOffset; + }); + if (relIt != isec->relocations.end() && + (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE)) return; log("detected cortex-a53-843419 erratum sequence starting at " + - utohexstr(AdrpAddr) + " in unpatched output."); + utohexstr(adrpAddr) + " in unpatched output."); - auto *PS = make<Patch843419Section>(IS, PatcheeOffset); - Patches.push_back(PS); + auto *ps = make<Patch843419Section>(isec, patcheeOffset); + patches.push_back(ps); - auto MakeRelToPatch = [](uint64_t Offset, Symbol *PatchSym) { - return Relocation{R_PC, R_AARCH64_JUMP26, Offset, 0, PatchSym}; + auto makeRelToPatch = [](uint64_t offset, Symbol *patchSym) { + return Relocation{R_PC, R_AARCH64_JUMP26, offset, 0, patchSym}; }; - if (RelIt != IS->Relocations.end()) { - PS->Relocations.push_back( - {RelIt->Expr, RelIt->Type, 0, RelIt->Addend, RelIt->Sym}); - *RelIt = MakeRelToPatch(PatcheeOffset, PS->PatchSym); + if (relIt != isec->relocations.end()) { + ps->relocations.push_back( + {relIt->expr, relIt->type, 0, relIt->addend, relIt->sym}); + *relIt = makeRelToPatch(patcheeOffset, ps->patchSym); } else - IS->Relocations.push_back(MakeRelToPatch(PatcheeOffset, PS->PatchSym)); + isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym)); } // Scan all the instructions in InputSectionDescription, for each instance of @@ -582,40 +581,40 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset, // Patch843419Sections that need to be applied to ISD. std::vector<Patch843419Section *> AArch64Err843419Patcher::patchInputSectionDescription( - InputSectionDescription &ISD) { - std::vector<Patch843419Section *> Patches; - for (InputSection *IS : ISD.Sections) { + InputSectionDescription &isd) { + std::vector<Patch843419Section *> patches; + for (InputSection *isec : isd.sections) { // LLD doesn't use the erratum sequence in SyntheticSections. - if (isa<SyntheticSection>(IS)) + if (isa<SyntheticSection>(isec)) continue; - // Use SectionMap to make sure we only scan code and not inline data. + // Use sectionMap to make sure we only scan code and not inline data. // We have already sorted MapSyms in ascending order and removed consecutive // mapping symbols of the same type. Our range of executable instructions to - // scan is therefore [CodeSym->Value, DataSym->Value) or [CodeSym->Value, + // scan is therefore [codeSym->value, dataSym->value) or [codeSym->value, // section size). - std::vector<const Defined *> &MapSyms = SectionMap[IS]; + std::vector<const Defined *> &mapSyms = sectionMap[isec]; - auto CodeSym = llvm::find_if(MapSyms, [&](const Defined *MS) { - return MS->getName().startswith("$x"); + auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) { + return ms->getName().startswith("$x"); }); - while (CodeSym != MapSyms.end()) { - auto DataSym = std::next(CodeSym); - uint64_t Off = (*CodeSym)->Value; - uint64_t Limit = - (DataSym == MapSyms.end()) ? IS->data().size() : (*DataSym)->Value; + while (codeSym != mapSyms.end()) { + auto dataSym = std::next(codeSym); + uint64_t off = (*codeSym)->value; + uint64_t limit = + (dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value; - while (Off < Limit) { - uint64_t StartAddr = IS->getVA(Off); - if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit)) - implementPatch(StartAddr, PatcheeOffset, IS, Patches); + while (off < limit) { + uint64_t startAddr = isec->getVA(off); + if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit)) + implementPatch(startAddr, patcheeOffset, isec, patches); } - if (DataSym == MapSyms.end()) + if (dataSym == mapSyms.end()) break; - CodeSym = std::next(DataSym); + codeSym = std::next(dataSym); } } - return Patches; + return patches; } // For each InputSectionDescription make one pass over the executable sections @@ -631,22 +630,22 @@ AArch64Err843419Patcher::patchInputSectionDescription( // Ouptut and Input Sections may have been changed. // Returns false if no patches were required and no changes were made. bool AArch64Err843419Patcher::createFixes() { - if (Initialized == false) + if (initialized == false) init(); - bool AddressesChanged = false; - for (OutputSection *OS : OutputSections) { - if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR)) + bool addressesChanged = false; + for (OutputSection *os : outputSections) { + if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR)) continue; - for (BaseCommand *BC : OS->SectionCommands) - if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) { - std::vector<Patch843419Section *> Patches = - patchInputSectionDescription(*ISD); - if (!Patches.empty()) { - insertPatches(*ISD, Patches); - AddressesChanged = true; + for (BaseCommand *bc : os->sectionCommands) + if (auto *isd = dyn_cast<InputSectionDescription>(bc)) { + std::vector<Patch843419Section *> patches = + patchInputSectionDescription(*isd); + if (!patches.empty()) { + insertPatches(*isd, patches); + addressesChanged = true; } } } - return AddressesChanged; + return addressesChanged; } diff --git a/ELF/AArch64ErrataFix.h b/ELF/AArch64ErrataFix.h index edd154d4cab3..0548b58751ff 100644 --- a/ELF/AArch64ErrataFix.h +++ b/ELF/AArch64ErrataFix.h @@ -1,9 +1,8 @@ //===- AArch64ErrataFix.h ---------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -30,19 +29,19 @@ public: private: std::vector<Patch843419Section *> - patchInputSectionDescription(InputSectionDescription &ISD); + patchInputSectionDescription(InputSectionDescription &isd); - void insertPatches(InputSectionDescription &ISD, - std::vector<Patch843419Section *> &Patches); + void insertPatches(InputSectionDescription &isd, + std::vector<Patch843419Section *> &patches); void init(); - // A cache of the mapping symbols defined by the InputSecion sorted in order + // A cache of the mapping symbols defined by the InputSection sorted in order // of ascending value with redundant symbols removed. These describe // the ranges of code and data in an executable InputSection. - std::map<InputSection *, std::vector<const Defined *>> SectionMap; + std::map<InputSection *, std::vector<const Defined *>> sectionMap; - bool Initialized = false; + bool initialized = false; }; } // namespace elf diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp index 08ffe2a08c0f..4d4789702f03 100644 --- a/ELF/Arch/AArch64.cpp +++ b/ELF/Arch/AArch64.cpp @@ -1,9 +1,8 @@ //===- AArch64.cpp --------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -24,60 +23,59 @@ using namespace lld::elf; // Page(Expr) is the page address of the expression Expr, defined // as (Expr & ~0xFFF). (This applies even if the machine page size // supported by the platform has a different value.) -uint64_t elf::getAArch64Page(uint64_t Expr) { - return Expr & ~static_cast<uint64_t>(0xFFF); +uint64_t elf::getAArch64Page(uint64_t expr) { + return expr & ~static_cast<uint64_t>(0xFFF); } namespace { -class AArch64 final : public TargetInfo { +class AArch64 : public TargetInfo { public: AArch64(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - bool usesOnlyLowPageBits(RelType Type) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + bool usesOnlyLowPageBits(RelType type) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace AArch64::AArch64() { - CopyRel = R_AARCH64_COPY; - RelativeRel = R_AARCH64_RELATIVE; - IRelativeRel = R_AARCH64_IRELATIVE; - GotRel = R_AARCH64_GLOB_DAT; - NoneRel = R_AARCH64_NONE; - PltRel = R_AARCH64_JUMP_SLOT; - TlsDescRel = R_AARCH64_TLSDESC; - TlsGotRel = R_AARCH64_TLS_TPREL64; - GotEntrySize = 8; - GotPltEntrySize = 8; - PltEntrySize = 16; - PltHeaderSize = 32; - DefaultMaxPageSize = 65536; + copyRel = R_AARCH64_COPY; + relativeRel = R_AARCH64_RELATIVE; + iRelativeRel = R_AARCH64_IRELATIVE; + gotRel = R_AARCH64_GLOB_DAT; + noneRel = R_AARCH64_NONE; + pltRel = R_AARCH64_JUMP_SLOT; + symbolicRel = R_AARCH64_ABS64; + tlsDescRel = R_AARCH64_TLSDESC; + tlsGotRel = R_AARCH64_TLS_TPREL64; + pltEntrySize = 16; + pltHeaderSize = 32; + defaultMaxPageSize = 65536; // Align to the 2 MiB page size (known as a superpage or huge page). // FreeBSD automatically promotes 2 MiB-aligned allocations. - DefaultImageBase = 0x200000; + defaultImageBase = 0x200000; - NeedsThunks = true; + needsThunks = true; } -RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_AARCH64_TLSDESC_ADR_PAGE21: return R_AARCH64_TLSDESC_PAGE; case R_AARCH64_TLSDESC_LD64_LO12: @@ -105,6 +103,7 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, case R_AARCH64_LD_PREL_LO19: return R_PC; case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADR_PREL_PG_HI21_NC: return R_AARCH64_PAGE_PC; case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: @@ -119,18 +118,18 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, } } -RelExpr AArch64::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const { - if (Expr == R_RELAX_TLS_GD_TO_IE) { - if (Type == R_AARCH64_TLSDESC_ADR_PAGE21) +RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) { + if (type == R_AARCH64_TLSDESC_ADR_PAGE21) return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC; return R_RELAX_TLS_GD_TO_IE_ABS; } - return Expr; + return expr; } -bool AArch64::usesOnlyLowPageBits(RelType Type) const { - switch (Type) { +bool AArch64::usesOnlyLowPageBits(RelType type) const { + switch (type) { default: return false; case R_AARCH64_ADD_ABS_LO12_NC: @@ -147,18 +146,18 @@ bool AArch64::usesOnlyLowPageBits(RelType Type) const { } } -RelType AArch64::getDynRel(RelType Type) const { - if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64) - return Type; +RelType AArch64::getDynRel(RelType type) const { + if (type == R_AARCH64_ABS64) + return type; return R_AARCH64_NONE; } -void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const { - write64le(Buf, In.Plt->getVA()); +void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const { + write64le(buf, in.plt->getVA()); } -void AArch64::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void AArch64::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] @@ -168,42 +167,42 @@ void AArch64::writePltHeader(uint8_t *Buf) const { 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5 // nop }; - memcpy(Buf, PltData, sizeof(PltData)); - - uint64_t Got = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(Got + 16) - getAArch64Page(Plt + 4)); - relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16); - relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16); + memcpy(buf, pltData, sizeof(pltData)); + + uint64_t got = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 4)); + relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); } -void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void AArch64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n])) 0x20, 0x02, 0x1f, 0xd6 // br x17 }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr)); - relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr); - relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr); + relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); + relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); } -bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { +bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { // ELF for the ARM 64-bit architecture, section Call and Jump relocations // only permits range extension thunks for R_AARCH64_CALL26 and // R_AARCH64_JUMP26 relocation types. - if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26) + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) return false; - uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); - return !inBranchRange(Type, BranchAddr, Dst); + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); } uint32_t AArch64::getThunkSectionSpacing() const { @@ -213,71 +212,72 @@ uint32_t AArch64::getThunkSectionSpacing() const { return (128 * 1024 * 1024) - 0x30000; } -bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26) +bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) return true; // The AArch64 call and unconditional branch instructions have a range of // +/- 128 MiB. - uint64_t Range = 128 * 1024 * 1024; - if (Dst > Src) { + uint64_t range = 128 * 1024 * 1024; + if (dst > src) { // Immediate of branch is signed. - Range -= 4; - return Dst - Src <= Range; + range -= 4; + return dst - src <= range; } - return Src - Dst <= Range; + return src - dst <= range; } -static void write32AArch64Addr(uint8_t *L, uint64_t Imm) { - uint32_t ImmLo = (Imm & 0x3) << 29; - uint32_t ImmHi = (Imm & 0x1FFFFC) << 3; - uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3); - write32le(L, (read32le(L) & ~Mask) | ImmLo | ImmHi); +static void write32AArch64Addr(uint8_t *l, uint64_t imm) { + uint32_t immLo = (imm & 0x3) << 29; + uint32_t immHi = (imm & 0x1FFFFC) << 3; + uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3); + write32le(l, (read32le(l) & ~mask) | immLo | immHi); } // Return the bits [Start, End] from Val shifted Start bits. // For instance, getBits(0xF0, 4, 8) returns 0xF. -static uint64_t getBits(uint64_t Val, int Start, int End) { - uint64_t Mask = ((uint64_t)1 << (End + 1 - Start)) - 1; - return (Val >> Start) & Mask; +static uint64_t getBits(uint64_t val, int start, int end) { + uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1; + return (val >> start) & mask; } -static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } +static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } // Update the immediate field in a AARCH64 ldr, str, and add instruction. -static void or32AArch64Imm(uint8_t *L, uint64_t Imm) { - or32le(L, (Imm & 0xFFF) << 10); +static void or32AArch64Imm(uint8_t *l, uint64_t imm) { + or32le(l, (imm & 0xFFF) << 10); } -void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_AARCH64_ABS32: case R_AARCH64_PREL32: - checkIntUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkIntUInt(loc, val, 32, type); + write32le(loc, val); break; case R_AARCH64_ABS64: - case R_AARCH64_GLOB_DAT: case R_AARCH64_PREL64: - write64le(Loc, Val); + write64le(loc, val); break; case R_AARCH64_ADD_ABS_LO12_NC: - or32AArch64Imm(Loc, Val); + or32AArch64Imm(loc, val); break; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: - checkInt(Loc, Val, 33, Type); - write32AArch64Addr(Loc, Val >> 12); + checkInt(loc, val, 33, type); + LLVM_FALLTHROUGH; + case R_AARCH64_ADR_PREL_PG_HI21_NC: + write32AArch64Addr(loc, val >> 12); break; case R_AARCH64_ADR_PREL_LO21: - checkInt(Loc, Val, 21, Type); - write32AArch64Addr(Loc, Val); + checkInt(loc, val, 21, type); + write32AArch64Addr(loc, val); break; case R_AARCH64_JUMP26: // Normally we would just write the bits of the immediate field, however @@ -287,75 +287,75 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { // opcode and the immediate (0 001 | 01 imm26) we can do this // transformation by placing a R_AARCH64_JUMP26 relocation at the offset of // the instruction we want to patch. - write32le(Loc, 0x14000000); + write32le(loc, 0x14000000); LLVM_FALLTHROUGH; case R_AARCH64_CALL26: - checkInt(Loc, Val, 28, Type); - or32le(Loc, (Val & 0x0FFFFFFC) >> 2); + checkInt(loc, val, 28, type); + or32le(loc, (val & 0x0FFFFFFC) >> 2); break; case R_AARCH64_CONDBR19: case R_AARCH64_LD_PREL_LO19: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 21, Type); - or32le(Loc, (Val & 0x1FFFFC) << 3); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 21, type); + or32le(loc, (val & 0x1FFFFC) << 3); break; case R_AARCH64_LDST8_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: - or32AArch64Imm(Loc, getBits(Val, 0, 11)); + or32AArch64Imm(loc, getBits(val, 0, 11)); break; case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: - checkAlignment(Loc, Val, 2, Type); - or32AArch64Imm(Loc, getBits(Val, 1, 11)); + checkAlignment(loc, val, 2, type); + or32AArch64Imm(loc, getBits(val, 1, 11)); break; case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: - checkAlignment(Loc, Val, 4, Type); - or32AArch64Imm(Loc, getBits(Val, 2, 11)); + checkAlignment(loc, val, 4, type); + or32AArch64Imm(loc, getBits(val, 2, 11)); break; case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSDESC_LD64_LO12: - checkAlignment(Loc, Val, 8, Type); - or32AArch64Imm(Loc, getBits(Val, 3, 11)); + checkAlignment(loc, val, 8, type); + or32AArch64Imm(loc, getBits(val, 3, 11)); break; case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: - checkAlignment(Loc, Val, 16, Type); - or32AArch64Imm(Loc, getBits(Val, 4, 11)); + checkAlignment(loc, val, 16, type); + or32AArch64Imm(loc, getBits(val, 4, 11)); break; case R_AARCH64_MOVW_UABS_G0_NC: - or32le(Loc, (Val & 0xFFFF) << 5); + or32le(loc, (val & 0xFFFF) << 5); break; case R_AARCH64_MOVW_UABS_G1_NC: - or32le(Loc, (Val & 0xFFFF0000) >> 11); + or32le(loc, (val & 0xFFFF0000) >> 11); break; case R_AARCH64_MOVW_UABS_G2_NC: - or32le(Loc, (Val & 0xFFFF00000000) >> 27); + or32le(loc, (val & 0xFFFF00000000) >> 27); break; case R_AARCH64_MOVW_UABS_G3: - or32le(Loc, (Val & 0xFFFF000000000000) >> 43); + or32le(loc, (val & 0xFFFF000000000000) >> 43); break; case R_AARCH64_TSTBR14: - checkInt(Loc, Val, 16, Type); - or32le(Loc, (Val & 0xFFFC) << 3); + checkInt(loc, val, 16, type); + or32le(loc, (val & 0xFFFC) << 3); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: - checkUInt(Loc, Val, 24, Type); - or32AArch64Imm(Loc, Val >> 12); + checkUInt(loc, val, 24, type); + or32AArch64Imm(loc, val >> 12); break; case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: case R_AARCH64_TLSDESC_ADD_LO12: - or32AArch64Imm(Loc, Val); + or32AArch64Imm(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // TLSDESC Global-Dynamic relocation are in the form: // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] @@ -367,25 +367,25 @@ void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // movk x0, #0x10 // nop // nop - checkUInt(Loc, Val, 32, Type); + checkUInt(loc, val, 32, type); - switch (Type) { + switch (type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: - write32le(Loc, 0xd503201f); // nop + write32le(loc, 0xd503201f); // nop return; case R_AARCH64_TLSDESC_ADR_PAGE21: - write32le(Loc, 0xd2a00000 | (((Val >> 16) & 0xffff) << 5)); // movz + write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz return; case R_AARCH64_TLSDESC_LD64_LO12: - write32le(Loc, 0xf2800000 | ((Val & 0xffff) << 5)); // movk + write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk return; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // TLSDESC Global-Dynamic relocation are in the form: // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] @@ -398,43 +398,193 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { // nop // nop - switch (Type) { + switch (type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: - write32le(Loc, 0xd503201f); // nop + write32le(loc, 0xd503201f); // nop break; case R_AARCH64_TLSDESC_ADR_PAGE21: - write32le(Loc, 0x90000000); // adrp - relocateOne(Loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, Val); + write32le(loc, 0x90000000); // adrp + relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val); break; case R_AARCH64_TLSDESC_LD64_LO12: - write32le(Loc, 0xf9400000); // ldr - relocateOne(Loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, Val); + write32le(loc, 0xf9400000); // ldr + relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { - checkUInt(Loc, Val, 32, Type); +void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + checkUInt(loc, val, 32, type); - if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { + if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { // Generate MOVZ. - uint32_t RegNo = read32le(Loc) & 0x1f; - write32le(Loc, (0xd2a00000 | RegNo) | (((Val >> 16) & 0xffff) << 5)); + uint32_t regNo = read32le(loc) & 0x1f; + write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5)); return; } - if (Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { + if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { // Generate MOVK. - uint32_t RegNo = read32le(Loc) & 0x1f; - write32le(Loc, (0xf2800000 | RegNo) | ((Val & 0xffff) << 5)); + uint32_t regNo = read32le(loc) & 0x1f; + write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5)); return; } llvm_unreachable("invalid relocation for TLS IE to LE relaxation"); } -TargetInfo *elf::getAArch64TargetInfo() { - static AArch64 Target; - return &Target; +// AArch64 may use security features in variant PLT sequences. These are: +// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target +// Indicator (BTI) introduced in armv8.5-a. The additional instructions used +// in the variant Plt sequences are encoded in the Hint space so they can be +// deployed on older architectures, which treat the instructions as a nop. +// PAC and BTI can be combined leading to the following combinations: +// writePltHeader +// writePltHeaderBti (no PAC Header needed) +// writePlt +// writePltBti (BTI only) +// writePltPac (PAC only) +// writePltBtiPac (BTI and PAC) +// +// When PAC is enabled the dynamic loader encrypts the address that it places +// in the .got.plt using the pacia1716 instruction which encrypts the value in +// x17 using the modifier in x16. The static linker places autia1716 before the +// indirect branch to x17 to authenticate the address in x17 with the modifier +// in x16. This makes it more difficult for an attacker to modify the value in +// the .got.plt. +// +// When BTI is enabled all indirect branches must land on a bti instruction. +// The static linker must place a bti instruction at the start of any PLT entry +// that may be the target of an indirect branch. As the PLT entries call the +// lazy resolver indirectly this must have a bti instruction at start. In +// general a bti instruction is not needed for a PLT entry as indirect calls +// are resolved to the function address and not the PLT entry for the function. +// There are a small number of cases where the PLT address can escape, such as +// taking the address of a function or ifunc via a non got-generating +// relocation, and a shared library refers to that symbol. +// +// We use the bti c variant of the instruction which permits indirect branches +// (br) via x16/x17 and indirect function calls (blr) via any register. The ABI +// guarantees that all indirect branches from code requiring BTI protection +// will go via x16/x17 + +namespace { +class AArch64BtiPac final : public AArch64 { +public: + AArch64BtiPac(); + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + +private: + bool btiHeader; // bti instruction needed in PLT Header + bool btiEntry; // bti instruction needed in PLT Entry + bool pacEntry; // autia1716 instruction needed in PLT Entry +}; +} // namespace + +AArch64BtiPac::AArch64BtiPac() { + btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + // A BTI (Branch Target Indicator) Plt Entry is only required if the + // address of the PLT entry can be taken by the program, which permits an + // indirect jump to the PLT entry. This can happen when the address + // of the PLT entry for a function is canonicalised due to the address of + // the function in an executable being taken by a shared library. + // FIXME: There is a potential optimization to omit the BTI if we detect + // that the address of the PLT entry isn't taken. + btiEntry = btiHeader && !config->shared; + pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); + + if (btiEntry || pacEntry) + pltEntrySize = 24; } + +void AArch64BtiPac::writePltHeader(uint8_t *buf) const { + const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t pltData[] = { + 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + uint64_t got = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + + if (btiHeader) { + // PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C + // instruction. + memcpy(buf, btiData, sizeof(btiData)); + buf += sizeof(btiData); + plt += sizeof(btiData); + } + memcpy(buf, pltData, sizeof(pltData)); + + relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 8)); + relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); + if (!btiHeader) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(buf + sizeof(pltData), nopData, sizeof(nopData)); +} + +void AArch64BtiPac::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // The PLT entry is of the form: + // [btiData] addrInst (pacBr | stdBr) [nopData] + const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t addrInst[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] + 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n])) + }; + const uint8_t pacBr[] = { + 0x9f, 0x21, 0x03, 0xd5, // autia1716 + 0x20, 0x02, 0x1f, 0xd6 // br x17 + }; + const uint8_t stdBr[] = { + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + if (btiEntry) { + memcpy(buf, btiData, sizeof(btiData)); + buf += sizeof(btiData); + pltEntryAddr += sizeof(btiData); + } + + memcpy(buf, addrInst, sizeof(addrInst)); + relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - + getAArch64Page(pltEntryAddr)); + relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); + + if (pacEntry) + memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr)); + else + memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr)); + if (!btiEntry) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData)); +} + +static TargetInfo *getTargetInfo() { + if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | + GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { + static AArch64BtiPac t; + return &t; + } + static AArch64 t; + return &t; +} + +TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); } diff --git a/ELF/Arch/AMDGPU.cpp b/ELF/Arch/AMDGPU.cpp index a7c6c84ceecd..f2e32ca0996d 100644 --- a/ELF/Arch/AMDGPU.cpp +++ b/ELF/Arch/AMDGPU.cpp @@ -1,9 +1,8 @@ //===- AMDGPU.cpp ---------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -26,62 +25,63 @@ class AMDGPU final : public TargetInfo { public: AMDGPU(); uint32_t calcEFlags() const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; }; } // namespace AMDGPU::AMDGPU() { - RelativeRel = R_AMDGPU_RELATIVE64; - GotRel = R_AMDGPU_ABS64; - NoneRel = R_AMDGPU_NONE; - GotEntrySize = 8; + relativeRel = R_AMDGPU_RELATIVE64; + gotRel = R_AMDGPU_ABS64; + noneRel = R_AMDGPU_NONE; + symbolicRel = R_AMDGPU_ABS64; } -static uint32_t getEFlags(InputFile *File) { - return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags; +static uint32_t getEFlags(InputFile *file) { + return cast<ObjFile<ELF64LE>>(file)->getObj().getHeader()->e_flags; } uint32_t AMDGPU::calcEFlags() const { - assert(!ObjectFiles.empty()); - uint32_t Ret = getEFlags(ObjectFiles[0]); + assert(!objectFiles.empty()); + uint32_t ret = getEFlags(objectFiles[0]); // Verify that all input files have the same e_flags. - for (InputFile *F : makeArrayRef(ObjectFiles).slice(1)) { - if (Ret == getEFlags(F)) + for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { + if (ret == getEFlags(f)) continue; - error("incompatible e_flags: " + toString(F)); + error("incompatible e_flags: " + toString(f)); return 0; } - return Ret; + return ret; } -void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AMDGPU_ABS32: case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: case R_AMDGPU_REL32: case R_AMDGPU_REL32_LO: - write32le(Loc, Val); + write32le(loc, val); break; case R_AMDGPU_ABS64: case R_AMDGPU_REL64: - write64le(Loc, Val); + write64le(loc, val); break; case R_AMDGPU_GOTPCREL32_HI: case R_AMDGPU_REL32_HI: - write32le(Loc, Val >> 32); + write32le(loc, val >> 32); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_AMDGPU_ABS32: case R_AMDGPU_ABS64: return R_ABS; @@ -95,11 +95,19 @@ RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S, case R_AMDGPU_GOTPCREL32_HI: return R_GOT_PC; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } +RelType AMDGPU::getDynRel(RelType type) const { + if (type == R_AMDGPU_ABS64) + return type; + return R_AMDGPU_NONE; +} + TargetInfo *elf::getAMDGPUTargetInfo() { - static AMDGPU Target; - return &Target; + static AMDGPU target; + return ⌖ } diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp index 120caca671af..64adc33c07ae 100644 --- a/ELF/Arch/ARM.cpp +++ b/ELF/Arch/ARM.cpp @@ -1,9 +1,8 @@ //===- ARM.cpp ------------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -27,63 +26,62 @@ class ARM final : public TargetInfo { public: ARM(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - RelType getDynRel(RelType Type) const override; - int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void addPltSymbols(InputSection &IS, uint64_t Off) const override; - void addPltHeaderSymbols(InputSection &ISD) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void addPltSymbols(InputSection &isec, uint64_t off) const override; + void addPltHeaderSymbols(InputSection &isd) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace ARM::ARM() { - CopyRel = R_ARM_COPY; - RelativeRel = R_ARM_RELATIVE; - IRelativeRel = R_ARM_IRELATIVE; - GotRel = R_ARM_GLOB_DAT; - NoneRel = R_ARM_NONE; - PltRel = R_ARM_JUMP_SLOT; - TlsGotRel = R_ARM_TLS_TPOFF32; - TlsModuleIndexRel = R_ARM_TLS_DTPMOD32; - TlsOffsetRel = R_ARM_TLS_DTPOFF32; - GotBaseSymInGotPlt = false; - GotEntrySize = 4; - GotPltEntrySize = 4; - PltEntrySize = 16; - PltHeaderSize = 32; - TrapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; - NeedsThunks = true; + copyRel = R_ARM_COPY; + relativeRel = R_ARM_RELATIVE; + iRelativeRel = R_ARM_IRELATIVE; + gotRel = R_ARM_GLOB_DAT; + noneRel = R_ARM_NONE; + pltRel = R_ARM_JUMP_SLOT; + symbolicRel = R_ARM_ABS32; + tlsGotRel = R_ARM_TLS_TPOFF32; + tlsModuleIndexRel = R_ARM_TLS_DTPMOD32; + tlsOffsetRel = R_ARM_TLS_DTPOFF32; + gotBaseSymInGotPlt = false; + pltEntrySize = 16; + pltHeaderSize = 32; + trapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; + needsThunks = true; } uint32_t ARM::calcEFlags() const { // The ABIFloatType is used by loaders to detect the floating point calling // convention. - uint32_t ABIFloatType = 0; - if (Config->ARMVFPArgs == ARMVFPArgKind::Base || - Config->ARMVFPArgs == ARMVFPArgKind::Default) - ABIFloatType = EF_ARM_ABI_FLOAT_SOFT; - else if (Config->ARMVFPArgs == ARMVFPArgKind::VFP) - ABIFloatType = EF_ARM_ABI_FLOAT_HARD; + uint32_t abiFloatType = 0; + if (config->armVFPArgs == ARMVFPArgKind::Base || + config->armVFPArgs == ARMVFPArgKind::Default) + abiFloatType = EF_ARM_ABI_FLOAT_SOFT; + else if (config->armVFPArgs == ARMVFPArgKind::VFP) + abiFloatType = EF_ARM_ABI_FLOAT_HARD; // We don't currently use any features incompatible with EF_ARM_EABI_VER5, // but we don't have any firm guarantees of conformance. Linux AArch64 // kernels (as of 2016) require an EABI version to be set. - return EF_ARM_EABI_VER5 | ABIFloatType; + return EF_ARM_EABI_VER5 | abiFloatType; } -RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr ARM::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_ARM_THM_JUMP11: return R_PC; case R_ARM_CALL: @@ -108,11 +106,11 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, case R_ARM_SBREL32: return R_ARM_SBREL; case R_ARM_TARGET1: - return Config->Target1Rel ? R_PC : R_ABS; + return config->target1Rel ? R_PC : R_ABS; case R_ARM_TARGET2: - if (Config->Target2 == Target2Policy::Rel) + if (config->target2 == Target2Policy::Rel) return R_PC; - if (Config->Target2 == Target2Policy::Abs) + if (config->target2 == Target2Policy::Abs) return R_ABS; return R_GOT_PC; case R_ARM_TLS_GD32: @@ -145,25 +143,25 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, } } -RelType ARM::getDynRel(RelType Type) const { - if ((Type == R_ARM_ABS32) || (Type == R_ARM_TARGET1 && !Config->Target1Rel)) +RelType ARM::getDynRel(RelType type) const { + if ((type == R_ARM_ABS32) || (type == R_ARM_TARGET1 && !config->target1Rel)) return R_ARM_ABS32; return R_ARM_NONE; } -void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const { - write32le(Buf, In.Plt->getVA()); +void ARM::writeGotPlt(uint8_t *buf, const Symbol &) const { + write32le(buf, in.plt->getVA()); } -void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const { +void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const { // An ARM entry is the address of the ifunc resolver function. - write32le(Buf, S.getVA()); + write32le(buf, s.getVA()); } // Long form PLT Header that does not have any restrictions on the displacement // of the .plt from the .plt.got. -static void writePltHeaderLong(uint8_t *Buf) { - const uint8_t PltData[] = { +static void writePltHeaderLong(uint8_t *buf) { + const uint8_t pltData[] = { 0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]! 0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2 0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr @@ -172,128 +170,128 @@ static void writePltHeaderLong(uint8_t *Buf) { 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary 0xd4, 0xd4, 0xd4, 0xd4}; - memcpy(Buf, PltData, sizeof(PltData)); - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t L1 = In.Plt->getVA() + 8; - write32le(Buf + 16, GotPlt - L1 - 8); + memcpy(buf, pltData, sizeof(pltData)); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t l1 = in.plt->getVA() + 8; + write32le(buf + 16, gotPlt - l1 - 8); } // The default PLT header requires the .plt.got to be within 128 Mb of the // .plt in the positive direction. -void ARM::writePltHeader(uint8_t *Buf) const { +void ARM::writePltHeader(uint8_t *buf) const { // Use a similar sequence to that in writePlt(), the difference is the calling // conventions mean we use lr instead of ip. The PLT entry is responsible for // saving lr on the stack, the dynamic loader is responsible for reloading // it. - const uint32_t PltData[] = { + const uint32_t pltData[] = { 0xe52de004, // L1: str lr, [sp,#-4]! 0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4) 0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4) 0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4) }; - uint64_t Offset = In.GotPlt->getVA() - In.Plt->getVA() - 4; - if (!llvm::isUInt<27>(Offset)) { + uint64_t offset = in.gotPlt->getVA() - in.plt->getVA() - 4; + if (!llvm::isUInt<27>(offset)) { // We cannot encode the Offset, use the long form. - writePltHeaderLong(Buf); + writePltHeaderLong(buf); return; } - write32le(Buf + 0, PltData[0]); - write32le(Buf + 4, PltData[1] | ((Offset >> 20) & 0xff)); - write32le(Buf + 8, PltData[2] | ((Offset >> 12) & 0xff)); - write32le(Buf + 12, PltData[3] | (Offset & 0xfff)); - memcpy(Buf + 16, TrapInstr.data(), 4); // Pad to 32-byte boundary - memcpy(Buf + 20, TrapInstr.data(), 4); - memcpy(Buf + 24, TrapInstr.data(), 4); - memcpy(Buf + 28, TrapInstr.data(), 4); + write32le(buf + 0, pltData[0]); + write32le(buf + 4, pltData[1] | ((offset >> 20) & 0xff)); + write32le(buf + 8, pltData[2] | ((offset >> 12) & 0xff)); + write32le(buf + 12, pltData[3] | (offset & 0xfff)); + memcpy(buf + 16, trapInstr.data(), 4); // Pad to 32-byte boundary + memcpy(buf + 20, trapInstr.data(), 4); + memcpy(buf + 24, trapInstr.data(), 4); + memcpy(buf + 28, trapInstr.data(), 4); } -void ARM::addPltHeaderSymbols(InputSection &IS) const { - addSyntheticLocal("$a", STT_NOTYPE, 0, 0, IS); - addSyntheticLocal("$d", STT_NOTYPE, 16, 0, IS); +void ARM::addPltHeaderSymbols(InputSection &isec) const { + addSyntheticLocal("$a", STT_NOTYPE, 0, 0, isec); + addSyntheticLocal("$d", STT_NOTYPE, 16, 0, isec); } // Long form PLT entries that do not have any restrictions on the displacement // of the .plt from the .plt.got. -static void writePltLong(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) { - const uint8_t PltData[] = { +static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) { + const uint8_t pltData[] = { 0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc 0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - L1 - 8 }; - memcpy(Buf, PltData, sizeof(PltData)); - uint64_t L1 = PltEntryAddr + 4; - write32le(Buf + 12, GotPltEntryAddr - L1 - 8); + memcpy(buf, pltData, sizeof(pltData)); + uint64_t l1 = pltEntryAddr + 4; + write32le(buf + 12, gotPltEntryAddr - l1 - 8); } // The default PLT entries require the .plt.got to be within 128 Mb of the // .plt in the positive direction. -void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { +void ARM::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { // The PLT entry is similar to the example given in Appendix A of ELF for // the Arm Architecture. Instead of using the Group Relocations to find the // optimal rotation for the 8-bit immediate used in the add instructions we // hard code the most compact rotations for simplicity. This saves a load // instruction over the long plt sequences. - const uint32_t PltData[] = { + const uint32_t pltData[] = { 0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8 0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8 0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8 }; - uint64_t Offset = GotPltEntryAddr - PltEntryAddr - 8; - if (!llvm::isUInt<27>(Offset)) { + uint64_t offset = gotPltEntryAddr - pltEntryAddr - 8; + if (!llvm::isUInt<27>(offset)) { // We cannot encode the Offset, use the long form. - writePltLong(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff); + writePltLong(buf, gotPltEntryAddr, pltEntryAddr, index, relOff); return; } - write32le(Buf + 0, PltData[0] | ((Offset >> 20) & 0xff)); - write32le(Buf + 4, PltData[1] | ((Offset >> 12) & 0xff)); - write32le(Buf + 8, PltData[2] | (Offset & 0xfff)); - memcpy(Buf + 12, TrapInstr.data(), 4); // Pad to 16-byte boundary + write32le(buf + 0, pltData[0] | ((offset >> 20) & 0xff)); + write32le(buf + 4, pltData[1] | ((offset >> 12) & 0xff)); + write32le(buf + 8, pltData[2] | (offset & 0xfff)); + memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary } -void ARM::addPltSymbols(InputSection &IS, uint64_t Off) const { - addSyntheticLocal("$a", STT_NOTYPE, Off, 0, IS); - addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS); +void ARM::addPltSymbols(InputSection &isec, uint64_t off) const { + addSyntheticLocal("$a", STT_NOTYPE, off, 0, isec); + addSyntheticLocal("$d", STT_NOTYPE, off + 12, 0, isec); } -bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { +bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { // If S is an undefined weak symbol and does not have a PLT entry then it // will be resolved as a branch to the next instruction. - if (S.isUndefWeak() && !S.isInPlt()) + if (s.isUndefWeak() && !s.isInPlt()) return false; // A state change from ARM to Thumb and vice versa must go through an // interworking thunk if the relocation type is not R_ARM_CALL or // R_ARM_THM_CALL. - switch (Type) { + switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: // Source is ARM, all PLT entries are ARM so no interworking required. // Otherwise we need to interwork if Symbol has bit 0 set (Thumb). - if (Expr == R_PC && ((S.getVA() & 1) == 1)) + if (expr == R_PC && ((s.getVA() & 1) == 1)) return true; LLVM_FALLTHROUGH; case R_ARM_CALL: { - uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); - return !inBranchRange(Type, BranchAddr, Dst); + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); } case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: // Source is Thumb, all PLT entries are ARM so interworking is required. // Otherwise we need to interwork if Symbol has bit 0 clear (ARM). - if (Expr == R_PLT_PC || ((S.getVA() & 1) == 0)) + if (expr == R_PLT_PC || ((s.getVA() & 1) == 0)) return true; LLVM_FALLTHROUGH; case R_ARM_THM_CALL: { - uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); - return !inBranchRange(Type, BranchAddr, Dst); + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); } } return false; @@ -301,13 +299,13 @@ bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint32_t ARM::getThunkSectionSpacing() const { // The placing of pre-created ThunkSections is controlled by the value - // ThunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to + // thunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to // place the ThunkSection such that all branches from the InputSections // prior to the ThunkSection can reach a Thunk placed at the end of the // ThunkSection. Graphically: - // | up to ThunkSectionSpacing .text input sections | + // | up to thunkSectionSpacing .text input sections | // | ThunkSection | - // | up to ThunkSectionSpacing .text input sections | + // | up to thunkSectionSpacing .text input sections | // | ThunkSection | // Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This @@ -318,69 +316,68 @@ uint32_t ARM::getThunkSectionSpacing() const { // Thumb B<cc>.W range +/- 1MiB // If a branch cannot reach a pre-created ThunkSection a new one will be // created so we can handle the rare cases of a Thumb 2 conditional branch. - // We intentionally use a lower size for ThunkSectionSpacing than the maximum + // We intentionally use a lower size for thunkSectionSpacing than the maximum // branch range so the end of the ThunkSection is more likely to be within // range of the branch instruction that is furthest away. The value we shorten - // ThunkSectionSpacing by is set conservatively to allow us to create 16,384 + // thunkSectionSpacing by is set conservatively to allow us to create 16,384 // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to // one of the Thunks going out of range. - // On Arm the ThunkSectionSpacing depends on the range of the Thumb Branch + // On Arm the thunkSectionSpacing depends on the range of the Thumb Branch // range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except // ARMv6T2) the range is +/- 4MiB. - return (Config->ARMJ1J2BranchEncoding) ? 0x1000000 - 0x30000 + return (config->armJ1J2BranchEncoding) ? 0x1000000 - 0x30000 : 0x400000 - 0x7500; } -bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - uint64_t Range; - uint64_t InstrSize; +bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + uint64_t range; + uint64_t instrSize; - switch (Type) { + switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: case R_ARM_CALL: - Range = 0x2000000; - InstrSize = 4; + range = 0x2000000; + instrSize = 4; break; case R_ARM_THM_JUMP19: - Range = 0x100000; - InstrSize = 2; + range = 0x100000; + instrSize = 2; break; case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: - Range = Config->ARMJ1J2BranchEncoding ? 0x1000000 : 0x400000; - InstrSize = 2; + range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000; + instrSize = 2; break; default: return true; } // PC at Src is 2 instructions ahead, immediate of branch is signed - if (Src > Dst) - Range -= 2 * InstrSize; + if (src > dst) + range -= 2 * instrSize; else - Range += InstrSize; + range += instrSize; - if ((Dst & 0x1) == 0) + if ((dst & 0x1) == 0) // Destination is ARM, if ARM caller then Src is already 4-byte aligned. // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure // destination will be 4 byte aligned. - Src &= ~0x3; + src &= ~0x3; else // Bit 0 == 1 denotes Thumb state, it is not part of the range - Dst &= ~0x1; + dst &= ~0x1; - uint64_t Distance = (Src > Dst) ? Src - Dst : Dst - Src; - return Distance <= Range; + uint64_t distance = (src > dst) ? src - dst : dst - src; + return distance <= range; } -void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_ARM_ABS32: case R_ARM_BASE_PREL: - case R_ARM_GLOB_DAT: case R_ARM_GOTOFF32: case R_ARM_GOT_BREL: case R_ARM_GOT_PREL: @@ -396,135 +393,132 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_ARM_TLS_LE32: case R_ARM_TLS_TPOFF32: case R_ARM_TLS_DTPOFF32: - write32le(Loc, Val); - break; - case R_ARM_TLS_DTPMOD32: - write32le(Loc, 1); + write32le(loc, val); break; case R_ARM_PREL31: - checkInt(Loc, Val, 31, Type); - write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000)); + checkInt(loc, val, 31, type); + write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000)); break; case R_ARM_CALL: // R_ARM_CALL is used for BL and BLX instructions, depending on the // value of bit 0 of Val, we must select a BL or BLX instruction - if (Val & 1) { + if (val & 1) { // If bit 0 of Val is 1 the target is Thumb, we must select a BLX. // The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1' - checkInt(Loc, Val, 26, Type); - write32le(Loc, 0xfa000000 | // opcode - ((Val & 2) << 23) | // H - ((Val >> 2) & 0x00ffffff)); // imm24 + checkInt(loc, val, 26, type); + write32le(loc, 0xfa000000 | // opcode + ((val & 2) << 23) | // H + ((val >> 2) & 0x00ffffff)); // imm24 break; } - if ((read32le(Loc) & 0xfe000000) == 0xfa000000) + if ((read32le(loc) & 0xfe000000) == 0xfa000000) // BLX (always unconditional) instruction to an ARM Target, select an // unconditional BL. - write32le(Loc, 0xeb000000 | (read32le(Loc) & 0x00ffffff)); + write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); // fall through as BL encoding is shared with B LLVM_FALLTHROUGH; case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: - checkInt(Loc, Val, 26, Type); - write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff)); + checkInt(loc, val, 26, type); + write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff)); break; case R_ARM_THM_JUMP11: - checkInt(Loc, Val, 12, Type); - write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff)); + checkInt(loc, val, 12, type); + write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff)); break; case R_ARM_THM_JUMP19: // Encoding T3: Val = S:J2:J1:imm6:imm11:0 - checkInt(Loc, Val, 21, Type); - write16le(Loc, - (read16le(Loc) & 0xfbc0) | // opcode cond - ((Val >> 10) & 0x0400) | // S - ((Val >> 12) & 0x003f)); // imm6 - write16le(Loc + 2, + checkInt(loc, val, 21, type); + write16le(loc, + (read16le(loc) & 0xfbc0) | // opcode cond + ((val >> 10) & 0x0400) | // S + ((val >> 12) & 0x003f)); // imm6 + write16le(loc + 2, 0x8000 | // opcode - ((Val >> 8) & 0x0800) | // J2 - ((Val >> 5) & 0x2000) | // J1 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 8) & 0x0800) | // J2 + ((val >> 5) & 0x2000) | // J1 + ((val >> 1) & 0x07ff)); // imm11 break; case R_ARM_THM_CALL: // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the // value of bit 0 of Val, we must select a BL or BLX instruction - if ((Val & 1) == 0) { + if ((val & 1) == 0) { // Ensure BLX destination is 4-byte aligned. As BLX instruction may // only be two byte aligned. This must be done before overflow check - Val = alignTo(Val, 4); + val = alignTo(val, 4); } // Bit 12 is 0 for BLX, 1 for BL - write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12); - if (!Config->ARMJ1J2BranchEncoding) { + write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12); + if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. - checkInt(Loc, Val, 23, Type); - write16le(Loc, + checkInt(loc, val, 23, type); + write16le(loc, 0xf000 | // opcode - ((Val >> 12) & 0x07ff)); // imm11 - write16le(Loc + 2, - (read16le(Loc + 2) & 0xd000) | // opcode + ((val >> 12) & 0x07ff)); // imm11 + write16le(loc + 2, + (read16le(loc + 2) & 0xd000) | // opcode 0x2800 | // J1 == J2 == 1 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 1) & 0x07ff)); // imm11 break; } // Fall through as rest of encoding is the same as B.W LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: // Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0 - checkInt(Loc, Val, 25, Type); - write16le(Loc, + checkInt(loc, val, 25, type); + write16le(loc, 0xf000 | // opcode - ((Val >> 14) & 0x0400) | // S - ((Val >> 12) & 0x03ff)); // imm10 - write16le(Loc + 2, - (read16le(Loc + 2) & 0xd000) | // opcode - (((~(Val >> 10)) ^ (Val >> 11)) & 0x2000) | // J1 - (((~(Val >> 11)) ^ (Val >> 13)) & 0x0800) | // J2 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 14) & 0x0400) | // S + ((val >> 12) & 0x03ff)); // imm10 + write16le(loc + 2, + (read16le(loc + 2) & 0xd000) | // opcode + (((~(val >> 10)) ^ (val >> 11)) & 0x2000) | // J1 + (((~(val >> 11)) ^ (val >> 13)) & 0x0800) | // J2 + ((val >> 1) & 0x07ff)); // imm11 break; case R_ARM_MOVW_ABS_NC: case R_ARM_MOVW_PREL_NC: - write32le(Loc, (read32le(Loc) & ~0x000f0fff) | ((Val & 0xf000) << 4) | - (Val & 0x0fff)); + write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) | + (val & 0x0fff)); break; case R_ARM_MOVT_ABS: case R_ARM_MOVT_PREL: - write32le(Loc, (read32le(Loc) & ~0x000f0fff) | - (((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff)); + write32le(loc, (read32le(loc) & ~0x000f0fff) | + (((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff)); break; case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVT_PREL: // Encoding T1: A = imm4:i:imm3:imm8 - write16le(Loc, + write16le(loc, 0xf2c0 | // opcode - ((Val >> 17) & 0x0400) | // i - ((Val >> 28) & 0x000f)); // imm4 - write16le(Loc + 2, - (read16le(Loc + 2) & 0x8f00) | // opcode - ((Val >> 12) & 0x7000) | // imm3 - ((Val >> 16) & 0x00ff)); // imm8 + ((val >> 17) & 0x0400) | // i + ((val >> 28) & 0x000f)); // imm4 + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | // opcode + ((val >> 12) & 0x7000) | // imm3 + ((val >> 16) & 0x00ff)); // imm8 break; case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVW_PREL_NC: // Encoding T3: A = imm4:i:imm3:imm8 - write16le(Loc, + write16le(loc, 0xf240 | // opcode - ((Val >> 1) & 0x0400) | // i - ((Val >> 12) & 0x000f)); // imm4 - write16le(Loc + 2, - (read16le(Loc + 2) & 0x8f00) | // opcode - ((Val << 4) & 0x7000) | // imm3 - (Val & 0x00ff)); // imm8 + ((val >> 1) & 0x0400) | // i + ((val >> 12) & 0x000f)); // imm4 + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | // opcode + ((val << 4) & 0x7000) | // imm3 + (val & 0x00ff)); // imm8 break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - switch (Type) { +int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { default: return 0; case R_ARM_ABS32: @@ -540,47 +534,47 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const { case R_ARM_TLS_LDO32: case R_ARM_TLS_IE32: case R_ARM_TLS_LE32: - return SignExtend64<32>(read32le(Buf)); + return SignExtend64<32>(read32le(buf)); case R_ARM_PREL31: - return SignExtend64<31>(read32le(Buf)); + return SignExtend64<31>(read32le(buf)); case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: - return SignExtend64<26>(read32le(Buf) << 2); + return SignExtend64<26>(read32le(buf) << 2); case R_ARM_THM_JUMP11: - return SignExtend64<12>(read16le(Buf) << 1); + return SignExtend64<12>(read16le(buf) << 1); case R_ARM_THM_JUMP19: { // Encoding T3: A = S:J2:J1:imm10:imm6:0 - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<20>(((Hi & 0x0400) << 10) | // S - ((Lo & 0x0800) << 8) | // J2 - ((Lo & 0x2000) << 5) | // J1 - ((Hi & 0x003f) << 12) | // imm6 - ((Lo & 0x07ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<20>(((hi & 0x0400) << 10) | // S + ((lo & 0x0800) << 8) | // J2 + ((lo & 0x2000) << 5) | // J1 + ((hi & 0x003f) << 12) | // imm6 + ((lo & 0x07ff) << 1)); // imm11:0 } case R_ARM_THM_CALL: - if (!Config->ARMJ1J2BranchEncoding) { + if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<22>(((Hi & 0x7ff) << 12) | // imm11 - ((Lo & 0x7ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<22>(((hi & 0x7ff) << 12) | // imm11 + ((lo & 0x7ff) << 1)); // imm11:0 break; } LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: { // Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0 // I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S) - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<24>(((Hi & 0x0400) << 14) | // S - (~((Lo ^ (Hi << 3)) << 10) & 0x00800000) | // I1 - (~((Lo ^ (Hi << 1)) << 11) & 0x00400000) | // I2 - ((Hi & 0x003ff) << 12) | // imm0 - ((Lo & 0x007ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<24>(((hi & 0x0400) << 14) | // S + (~((lo ^ (hi << 3)) << 10) & 0x00800000) | // I1 + (~((lo ^ (hi << 1)) << 11) & 0x00400000) | // I2 + ((hi & 0x003ff) << 12) | // imm0 + ((lo & 0x007ff) << 1)); // imm11:0 } // ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and // MOVT is in the range -32768 <= A < 32768 @@ -588,25 +582,25 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const { case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: { - uint64_t Val = read32le(Buf) & 0x000f0fff; - return SignExtend64<16>(((Val & 0x000f0000) >> 4) | (Val & 0x00fff)); + uint64_t val = read32le(buf) & 0x000f0fff; + return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff)); } case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: { // Encoding T3: A = imm4:i:imm3:imm8 - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<16>(((Hi & 0x000f) << 12) | // imm4 - ((Hi & 0x0400) << 1) | // i - ((Lo & 0x7000) >> 4) | // imm3 - (Lo & 0x00ff)); // imm8 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<16>(((hi & 0x000f) << 12) | // imm4 + ((hi & 0x0400) << 1) | // i + ((lo & 0x7000) >> 4) | // imm3 + (lo & 0x00ff)); // imm8 } } } TargetInfo *elf::getARMTargetInfo() { - static ARM Target; - return &Target; + static ARM target; + return ⌖ } diff --git a/ELF/Arch/AVR.cpp b/ELF/Arch/AVR.cpp index 637da3778bd2..869f0fe0c525 100644 --- a/ELF/Arch/AVR.cpp +++ b/ELF/Arch/AVR.cpp @@ -1,9 +1,8 @@ //===- AVR.cpp ------------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -44,34 +43,34 @@ namespace { class AVR final : public TargetInfo { public: AVR(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace -AVR::AVR() { NoneRel = R_AVR_NONE; } +AVR::AVR() { noneRel = R_AVR_NONE; } -RelExpr AVR::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { +RelExpr AVR::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { return R_ABS; } -void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AVR_CALL: { - uint16_t Hi = Val >> 17; - uint16_t Lo = Val >> 1; - write16le(Loc, read16le(Loc) | ((Hi >> 1) << 4) | (Hi & 1)); - write16le(Loc + 2, Lo); + uint16_t hi = val >> 17; + uint16_t lo = val >> 1; + write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1)); + write16le(loc + 2, lo); break; } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } TargetInfo *elf::getAVRTargetInfo() { - static AVR Target; - return &Target; + static AVR target; + return ⌖ } diff --git a/ELF/Arch/Hexagon.cpp b/ELF/Arch/Hexagon.cpp index b4d33be2ad39..c497a6df7987 100644 --- a/ELF/Arch/Hexagon.cpp +++ b/ELF/Arch/Hexagon.cpp @@ -1,9 +1,8 @@ //===-- Hexagon.cpp -------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -28,65 +27,65 @@ class Hexagon final : public TargetInfo { public: Hexagon(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace Hexagon::Hexagon() { - PltRel = R_HEX_JMP_SLOT; - RelativeRel = R_HEX_RELATIVE; - GotRel = R_HEX_GLOB_DAT; - GotEntrySize = 4; + pltRel = R_HEX_JMP_SLOT; + relativeRel = R_HEX_RELATIVE; + gotRel = R_HEX_GLOB_DAT; + symbolicRel = R_HEX_32; + // The zero'th GOT entry is reserved for the address of _DYNAMIC. The // next 3 are reserved for the dynamic loader. - GotPltHeaderEntriesNum = 4; - GotPltEntrySize = 4; + gotPltHeaderEntriesNum = 4; - PltEntrySize = 16; - PltHeaderSize = 32; + pltEntrySize = 16; + pltHeaderSize = 32; // Hexagon Linux uses 64K pages by default. - DefaultMaxPageSize = 0x10000; - NoneRel = R_HEX_NONE; + defaultMaxPageSize = 0x10000; + noneRel = R_HEX_NONE; } uint32_t Hexagon::calcEFlags() const { - assert(!ObjectFiles.empty()); + assert(!objectFiles.empty()); // The architecture revision must always be equal to or greater than // greatest revision in the list of inputs. - uint32_t Ret = 0; - for (InputFile *F : ObjectFiles) { - uint32_t EFlags = cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags; - if (EFlags > Ret) - Ret = EFlags; + uint32_t ret = 0; + for (InputFile *f : objectFiles) { + uint32_t eflags = cast<ObjFile<ELF32LE>>(f)->getObj().getHeader()->e_flags; + if (eflags > ret) + ret = eflags; } - return Ret; + return ret; } -static uint32_t applyMask(uint32_t Mask, uint32_t Data) { - uint32_t Result = 0; - size_t Off = 0; +static uint32_t applyMask(uint32_t mask, uint32_t data) { + uint32_t result = 0; + size_t off = 0; - for (size_t Bit = 0; Bit != 32; ++Bit) { - uint32_t ValBit = (Data >> Off) & 1; - uint32_t MaskBit = (Mask >> Bit) & 1; - if (MaskBit) { - Result |= (ValBit << Bit); - ++Off; + for (size_t bit = 0; bit != 32; ++bit) { + uint32_t valBit = (data >> off) & 1; + uint32_t maskBit = (mask >> bit) & 1; + if (maskBit) { + result |= (valBit << bit); + ++off; } } - return Result; + return result; } -RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_HEX_B9_PCREL: case R_HEX_B9_PCREL_X: case R_HEX_B13_PCREL: @@ -109,16 +108,16 @@ RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S, } } -static uint32_t findMaskR6(uint32_t Insn) { +static uint32_t findMaskR6(uint32_t insn) { // There are (arguably too) many relocation masks for the DSP's // R_HEX_6_X type. The table below is used to select the correct mask // for the given instruction. struct InstructionMask { - uint32_t CmpMask; - uint32_t RelocMask; + uint32_t cmpMask; + uint32_t relocMask; }; - static const InstructionMask R6[] = { + static const InstructionMask r6[] = { {0x38000000, 0x0000201f}, {0x39000000, 0x0000201f}, {0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80}, {0x40000000, 0x000020f8}, {0x41000000, 0x000007e0}, @@ -136,124 +135,124 @@ static uint32_t findMaskR6(uint32_t Insn) { // Duplex forms have a fixed mask and parse bits 15:14 are always // zero. Non-duplex insns will always have at least one bit set in the // parse field. - if ((0xC000 & Insn) == 0x0) + if ((0xC000 & insn) == 0x0) return 0x03f00000; - for (InstructionMask I : R6) - if ((0xff000000 & Insn) == I.CmpMask) - return I.RelocMask; + for (InstructionMask i : r6) + if ((0xff000000 & insn) == i.cmpMask) + return i.relocMask; error("unrecognized instruction for R_HEX_6 relocation: 0x" + - utohexstr(Insn)); + utohexstr(insn)); return 0; } -static uint32_t findMaskR8(uint32_t Insn) { - if ((0xff000000 & Insn) == 0xde000000) +static uint32_t findMaskR8(uint32_t insn) { + if ((0xff000000 & insn) == 0xde000000) return 0x00e020e8; - if ((0xff000000 & Insn) == 0x3c000000) + if ((0xff000000 & insn) == 0x3c000000) return 0x0000207f; return 0x00001fe0; } -static uint32_t findMaskR11(uint32_t Insn) { - if ((0xff000000 & Insn) == 0xa1000000) +static uint32_t findMaskR11(uint32_t insn) { + if ((0xff000000 & insn) == 0xa1000000) return 0x060020ff; return 0x06003fe0; } -static uint32_t findMaskR16(uint32_t Insn) { - if ((0xff000000 & Insn) == 0x48000000) +static uint32_t findMaskR16(uint32_t insn) { + if ((0xff000000 & insn) == 0x48000000) return 0x061f20ff; - if ((0xff000000 & Insn) == 0x49000000) + if ((0xff000000 & insn) == 0x49000000) return 0x061f3fe0; - if ((0xff000000 & Insn) == 0x78000000) + if ((0xff000000 & insn) == 0x78000000) return 0x00df3fe0; - if ((0xff000000 & Insn) == 0xb0000000) + if ((0xff000000 & insn) == 0xb0000000) return 0x0fe03fe0; error("unrecognized instruction for R_HEX_16_X relocation: 0x" + - utohexstr(Insn)); + utohexstr(insn)); return 0; } -static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } +static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } -void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_HEX_NONE: break; case R_HEX_6_PCREL_X: case R_HEX_6_X: - or32le(Loc, applyMask(findMaskR6(read32le(Loc)), Val)); + or32le(loc, applyMask(findMaskR6(read32le(loc)), val)); break; case R_HEX_8_X: - or32le(Loc, applyMask(findMaskR8(read32le(Loc)), Val)); + or32le(loc, applyMask(findMaskR8(read32le(loc)), val)); break; case R_HEX_9_X: - or32le(Loc, applyMask(0x00003fe0, Val & 0x3f)); + or32le(loc, applyMask(0x00003fe0, val & 0x3f)); break; case R_HEX_10_X: - or32le(Loc, applyMask(0x00203fe0, Val & 0x3f)); + or32le(loc, applyMask(0x00203fe0, val & 0x3f)); break; case R_HEX_11_X: case R_HEX_GOT_11_X: - or32le(Loc, applyMask(findMaskR11(read32le(Loc)), Val & 0x3f)); + or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f)); break; case R_HEX_12_X: - or32le(Loc, applyMask(0x000007e0, Val)); + or32le(loc, applyMask(0x000007e0, val)); break; case R_HEX_16_X: // These relocs only have 6 effective bits. case R_HEX_GOT_16_X: - or32le(Loc, applyMask(findMaskR16(read32le(Loc)), Val & 0x3f)); + or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f)); break; case R_HEX_32: case R_HEX_32_PCREL: - or32le(Loc, Val); + or32le(loc, val); break; case R_HEX_32_6_X: case R_HEX_GOT_32_6_X: - or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); + or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_B9_PCREL: - or32le(Loc, applyMask(0x003000fe, Val >> 2)); + or32le(loc, applyMask(0x003000fe, val >> 2)); break; case R_HEX_B9_PCREL_X: - or32le(Loc, applyMask(0x003000fe, Val & 0x3f)); + or32le(loc, applyMask(0x003000fe, val & 0x3f)); break; case R_HEX_B13_PCREL: - or32le(Loc, applyMask(0x00202ffe, Val >> 2)); + or32le(loc, applyMask(0x00202ffe, val >> 2)); break; case R_HEX_B15_PCREL: - or32le(Loc, applyMask(0x00df20fe, Val >> 2)); + or32le(loc, applyMask(0x00df20fe, val >> 2)); break; case R_HEX_B15_PCREL_X: - or32le(Loc, applyMask(0x00df20fe, Val & 0x3f)); + or32le(loc, applyMask(0x00df20fe, val & 0x3f)); break; case R_HEX_B22_PCREL: case R_HEX_PLT_B22_PCREL: - or32le(Loc, applyMask(0x1ff3ffe, Val >> 2)); + or32le(loc, applyMask(0x1ff3ffe, val >> 2)); break; case R_HEX_B22_PCREL_X: - or32le(Loc, applyMask(0x1ff3ffe, Val & 0x3f)); + or32le(loc, applyMask(0x1ff3ffe, val & 0x3f)); break; case R_HEX_B32_PCREL_X: - or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); + or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_HI16: - or32le(Loc, applyMask(0x00c03fff, Val >> 16)); + or32le(loc, applyMask(0x00c03fff, val >> 16)); break; case R_HEX_LO16: - or32le(Loc, applyMask(0x00c03fff, Val)); + or32le(loc, applyMask(0x00c03fff, val)); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); break; } } -void Hexagon::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void Hexagon::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0x00, 0x40, 0x00, 0x00, // { immext (#0) 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn @@ -263,30 +262,30 @@ void Hexagon::writePltHeader(uint8_t *Buf) const { 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker 0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment }; - memcpy(Buf, PltData, sizeof(PltData)); + memcpy(buf, pltData, sizeof(pltData)); // Offset from PLT0 to the GOT. - uint64_t Off = In.GotPlt->getVA() - In.Plt->getVA(); - relocateOne(Buf, R_HEX_B32_PCREL_X, Off); - relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off); + uint64_t off = in.gotPlt->getVA() - in.plt->getVA(); + relocateOne(buf, R_HEX_B32_PCREL_X, off); + relocateOne(buf + 4, R_HEX_6_PCREL_X, off); } -void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0x00, 0x40, 0x00, 0x00, // { immext (#0) 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) } 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14) 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr); - relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr); + relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr); + relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr); } TargetInfo *elf::getHexagonTargetInfo() { - static Hexagon Target; - return &Target; + static Hexagon target; + return ⌖ } diff --git a/ELF/Arch/MSP430.cpp b/ELF/Arch/MSP430.cpp index fe0c0fe64daf..90664396c85e 100644 --- a/ELF/Arch/MSP430.cpp +++ b/ELF/Arch/MSP430.cpp @@ -1,9 +1,8 @@ //===- MSP430.cpp ---------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -34,20 +33,20 @@ namespace { class MSP430 final : public TargetInfo { public: MSP430(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace MSP430::MSP430() { // mov.b #0, r3 - TrapInstr = {0x43, 0x43, 0x43, 0x43}; + trapInstr = {0x43, 0x43, 0x43, 0x43}; } -RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr MSP430::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_MSP430_10_PCREL: case R_MSP430_16_PCREL: case R_MSP430_16_PCREL_BYTE: @@ -60,35 +59,35 @@ RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S, } } -void MSP430::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_MSP430_8: - checkIntUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; break; case R_MSP430_16: case R_MSP430_16_PCREL: case R_MSP430_16_BYTE: case R_MSP430_16_PCREL_BYTE: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_MSP430_32: - checkIntUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkIntUInt(loc, val, 32, type); + write32le(loc, val); break; case R_MSP430_10_PCREL: { - int16_t Offset = ((int16_t)Val >> 1) - 1; - checkInt(Loc, Offset, 10, Type); - write16le(Loc, (read16le(Loc) & 0xFC00) | (Offset & 0x3FF)); + int16_t offset = ((int16_t)val >> 1) - 1; + checkInt(loc, offset, 10, type); + write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF)); break; } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } TargetInfo *elf::getMSP430TargetInfo() { - static MSP430 Target; - return &Target; + static MSP430 target; + return ⌖ } diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp index 23b0c1dd8a2d..24b3957acd99 100644 --- a/ELF/Arch/Mips.cpp +++ b/ELF/Arch/Mips.cpp @@ -1,9 +1,8 @@ //===- MIPS.cpp -----------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -29,47 +28,47 @@ template <class ELFT> class MIPS final : public TargetInfo { public: MIPS(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - bool usesOnlyLowPageBits(RelType Type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + RelType getDynRel(RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + bool usesOnlyLowPageBits(RelType type) const override; }; } // namespace template <class ELFT> MIPS<ELFT>::MIPS() { - GotPltHeaderEntriesNum = 2; - DefaultMaxPageSize = 65536; - GotEntrySize = sizeof(typename ELFT::uint); - GotPltEntrySize = sizeof(typename ELFT::uint); - GotBaseSymInGotPlt = false; - PltEntrySize = 16; - PltHeaderSize = 32; - CopyRel = R_MIPS_COPY; - NoneRel = R_MIPS_NONE; - PltRel = R_MIPS_JUMP_SLOT; - NeedsThunks = true; + gotPltHeaderEntriesNum = 2; + defaultMaxPageSize = 65536; + gotBaseSymInGotPlt = false; + pltEntrySize = 16; + pltHeaderSize = 32; + copyRel = R_MIPS_COPY; + noneRel = R_MIPS_NONE; + pltRel = R_MIPS_JUMP_SLOT; + needsThunks = true; // Set `sigrie 1` as a trap instruction. - write32(TrapInstr.data(), 0x04170001); + write32(trapInstr.data(), 0x04170001); if (ELFT::Is64Bits) { - RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; - TlsGotRel = R_MIPS_TLS_TPREL64; - TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; - TlsOffsetRel = R_MIPS_TLS_DTPREL64; + relativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; + symbolicRel = R_MIPS_64; + tlsGotRel = R_MIPS_TLS_TPREL64; + tlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; + tlsOffsetRel = R_MIPS_TLS_DTPREL64; } else { - RelativeRel = R_MIPS_REL32; - TlsGotRel = R_MIPS_TLS_TPREL32; - TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; - TlsOffsetRel = R_MIPS_TLS_DTPREL32; + relativeRel = R_MIPS_REL32; + symbolicRel = R_MIPS_32; + tlsGotRel = R_MIPS_TLS_TPREL32; + tlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; + tlsOffsetRel = R_MIPS_TLS_DTPREL32; } } @@ -78,13 +77,13 @@ template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const { } template <class ELFT> -RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { +RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { // See comment in the calculateMipsRelChain. - if (ELFT::Is64Bits || Config->MipsN32Abi) - Type &= 0xff; + if (ELFT::Is64Bits || config->mipsN32Abi) + type &= 0xff; - switch (Type) { + switch (type) { case R_MIPS_JALR: case R_MICROMIPS_JALR: return R_HINT; @@ -108,9 +107,9 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, // offset between start of function and 'gp' value which by default // equal to the start of .got section. In that case we consider these // relocations as relative. - if (&S == ElfSym::MipsGpDisp) + if (&s == ElfSym::mipsGpDisp) return R_MIPS_GOT_GP_PC; - if (&S == ElfSym::MipsLocalGp) + if (&s == ElfSym::mipsLocalGp) return R_MIPS_GOT_GP; LLVM_FALLTHROUGH; case R_MIPS_32: @@ -147,7 +146,7 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, return R_PC; case R_MIPS_GOT16: case R_MICROMIPS_GOT16: - if (S.isLocal()) + if (s.isLocal()) return R_MIPS_GOT_LOCAL_PAGE; LLVM_FALLTHROUGH; case R_MIPS_CALL16: @@ -176,209 +175,213 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, case R_MIPS_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const { - if (Type == R_MIPS_32 || Type == R_MIPS_64) - return RelativeRel; +template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType type) const { + if (type == symbolicRel) + return type; return R_MIPS_NONE; } template <class ELFT> -void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &) const { - uint64_t VA = In.Plt->getVA(); +void MIPS<ELFT>::writeGotPlt(uint8_t *buf, const Symbol &) const { + uint64_t va = in.plt->getVA(); if (isMicroMips()) - VA |= 1; - write32<ELFT::TargetEndianness>(Buf, VA); + va |= 1; + write32<ELFT::TargetEndianness>(buf, va); } -template <endianness E> static uint32_t readShuffle(const uint8_t *Loc) { +template <endianness E> static uint32_t readShuffle(const uint8_t *loc) { // The major opcode of a microMIPS instruction needs to appear // in the first 16-bit word (lowest address) for efficient hardware // decode so that it knows if the instruction is 16-bit or 32-bit // as early as possible. To do so, little-endian binaries keep 16-bit // words in a big-endian order. That is why we have to swap these // words to get a correct value. - uint32_t V = read32<E>(Loc); + uint32_t v = read32<E>(loc); if (E == support::little) - return (V << 16) | (V >> 16); - return V; + return (v << 16) | (v >> 16); + return v; } template <endianness E> -static void writeValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { - uint32_t Instr = read32<E>(Loc); - uint32_t Mask = 0xffffffff >> (32 - BitsSize); - uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); - write32<E>(Loc, Data); +static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + uint32_t instr = read32<E>(loc); + uint32_t mask = 0xffffffff >> (32 - bitsSize); + uint32_t data = (instr & ~mask) | ((v >> shift) & mask); + write32<E>(loc, data); } template <endianness E> -static void writeShuffleValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { +static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { // See comments in readShuffle for purpose of this code. - uint16_t *Words = (uint16_t *)Loc; + uint16_t *words = (uint16_t *)loc; if (E == support::little) - std::swap(Words[0], Words[1]); + std::swap(words[0], words[1]); - writeValue<E>(Loc, V, BitsSize, Shift); + writeValue<E>(loc, v, bitsSize, shift); if (E == support::little) - std::swap(Words[0], Words[1]); + std::swap(words[0], words[1]); } template <endianness E> -static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { - uint16_t Instr = read16<E>(Loc); - uint16_t Mask = 0xffff >> (16 - BitsSize); - uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); - write16<E>(Loc, Data); +static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + uint16_t instr = read16<E>(loc); + uint16_t mask = 0xffff >> (16 - bitsSize); + uint16_t data = (instr & ~mask) | ((v >> shift) & mask); + write16<E>(loc, data); } -template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const { - const endianness E = ELFT::TargetEndianness; +template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const { + const endianness e = ELFT::TargetEndianness; if (isMicroMips()) { - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); // Overwrite trap instructions written by Writer::writeTrapInstr. - memset(Buf, 0, PltHeaderSize); - - write16<E>(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . - write16<E>(Buf + 4, 0xff23); // lw $25, 0($3) - write16<E>(Buf + 8, 0x0535); // subu16 $2, $2, $3 - write16<E>(Buf + 10, 0x2525); // srl16 $2, $2, 2 - write16<E>(Buf + 12, 0x3302); // addiu $24, $2, -2 - write16<E>(Buf + 14, 0xfffe); - write16<E>(Buf + 16, 0x0dff); // move $15, $31 + memset(buf, 0, pltHeaderSize); + + write16<e>(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . + write16<e>(buf + 4, 0xff23); // lw $25, 0($3) + write16<e>(buf + 8, 0x0535); // subu16 $2, $2, $3 + write16<e>(buf + 10, 0x2525); // srl16 $2, $2, 2 + write16<e>(buf + 12, 0x3302); // addiu $24, $2, -2 + write16<e>(buf + 14, 0xfffe); + write16<e>(buf + 16, 0x0dff); // move $15, $31 if (isMipsR6()) { - write16<E>(Buf + 18, 0x0f83); // move $28, $3 - write16<E>(Buf + 20, 0x472b); // jalrc $25 - write16<E>(Buf + 22, 0x0c00); // nop - relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt); + write16<e>(buf + 18, 0x0f83); // move $28, $3 + write16<e>(buf + 20, 0x472b); // jalrc $25 + write16<e>(buf + 22, 0x0c00); // nop + relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt); } else { - write16<E>(Buf + 18, 0x45f9); // jalrc $25 - write16<E>(Buf + 20, 0x0f83); // move $28, $3 - write16<E>(Buf + 22, 0x0c00); // nop - relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt); + write16<e>(buf + 18, 0x45f9); // jalrc $25 + write16<e>(buf + 20, 0x0f83); // move $28, $3 + write16<e>(buf + 22, 0x0c00); // nop + relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt); } return; } - if (Config->MipsN32Abi) { - write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) - write32<E>(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) - write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) - write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14 - write32<E>(Buf + 16, 0x03e07825); // move $15, $31 - write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2 + if (config->mipsN32Abi) { + write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32<e>(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) + write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32<e>(buf + 16, 0x03e07825); // move $15, $31 + write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2 } else if (ELFT::Is64Bits) { - write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) - write32<E>(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) - write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) - write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14 - write32<E>(Buf + 16, 0x03e07825); // move $15, $31 - write32<E>(Buf + 20, 0x0018c0c2); // srl $24, $24, 3 + write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32<e>(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) + write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32<e>(buf + 16, 0x03e07825); // move $15, $31 + write32<e>(buf + 20, 0x0018c0c2); // srl $24, $24, 3 } else { - write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) - write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) - write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) - write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28 - write32<E>(Buf + 16, 0x03e07825); // move $15, $31 - write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2 + write32<e>(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32<e>(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32<e>(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32<e>(buf + 12, 0x031cc023); // subu $24, $24, $28 + write32<e>(buf + 16, 0x03e07825); // move $15, $31 + write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2 } - uint32_t JalrInst = Config->ZHazardplt ? 0x0320fc09 : 0x0320f809; - write32<E>(Buf + 24, JalrInst); // jalr.hb $25 or jalr $25 - write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2 + uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809; + write32<e>(buf + 24, jalrInst); // jalr.hb $25 or jalr $25 + write32<e>(buf + 28, 0x2718fffe); // subu $24, $24, 2 - uint64_t GotPlt = In.GotPlt->getVA(); - writeValue<E>(Buf, GotPlt + 0x8000, 16, 16); - writeValue<E>(Buf + 4, GotPlt, 16, 0); - writeValue<E>(Buf + 8, GotPlt, 16, 0); + uint64_t gotPlt = in.gotPlt->getVA(); + writeValue<e>(buf, gotPlt + 0x8000, 16, 16); + writeValue<e>(buf + 4, gotPlt, 16, 0); + writeValue<e>(buf + 8, gotPlt, 16, 0); } template <class ELFT> -void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const endianness E = ELFT::TargetEndianness; +void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const endianness e = ELFT::TargetEndianness; if (isMicroMips()) { // Overwrite trap instructions written by Writer::writeTrapInstr. - memset(Buf, 0, PltEntrySize); + memset(buf, 0, pltEntrySize); if (isMipsR6()) { - write16<E>(Buf, 0x7840); // addiupc $2, (GOTPLT) - . - write16<E>(Buf + 4, 0xff22); // lw $25, 0($2) - write16<E>(Buf + 8, 0x0f02); // move $24, $2 - write16<E>(Buf + 10, 0x4723); // jrc $25 / jr16 $25 - relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr); + write16<e>(buf, 0x7840); // addiupc $2, (GOTPLT) - . + write16<e>(buf + 4, 0xff22); // lw $25, 0($2) + write16<e>(buf + 8, 0x0f02); // move $24, $2 + write16<e>(buf + 10, 0x4723); // jrc $25 / jr16 $25 + relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr); } else { - write16<E>(Buf, 0x7900); // addiupc $2, (GOTPLT) - . - write16<E>(Buf + 4, 0xff22); // lw $25, 0($2) - write16<E>(Buf + 8, 0x4599); // jrc $25 / jr16 $25 - write16<E>(Buf + 10, 0x0f02); // move $24, $2 - relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr); + write16<e>(buf, 0x7900); // addiupc $2, (GOTPLT) - . + write16<e>(buf + 4, 0xff22); // lw $25, 0($2) + write16<e>(buf + 8, 0x4599); // jrc $25 / jr16 $25 + write16<e>(buf + 10, 0x0f02); // move $24, $2 + relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr); } return; } - uint32_t JrInst = isMipsR6() ? (Config->ZHazardplt ? 0x03200409 : 0x03200009) - : (Config->ZHazardplt ? 0x03200408 : 0x03200008); - - write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) - write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15) - write32<E>(Buf + 8, JrInst); // jr $25 / jr.hb $25 - write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) - writeValue<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16); - writeValue<E>(Buf + 4, GotPltEntryAddr, 16, 0); - writeValue<E>(Buf + 12, GotPltEntryAddr, 16, 0); + uint32_t loadInst = ELFT::Is64Bits ? 0xddf90000 : 0x8df90000; + uint32_t jrInst = isMipsR6() ? (config->zHazardplt ? 0x03200409 : 0x03200009) + : (config->zHazardplt ? 0x03200408 : 0x03200008); + uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000; + + write32<e>(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) + write32<e>(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15) + write32<e>(buf + 8, jrInst); // jr $25 / jr.hb $25 + write32<e>(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry) + writeValue<e>(buf, gotPltEntryAddr + 0x8000, 16, 16); + writeValue<e>(buf + 4, gotPltEntryAddr, 16, 0); + writeValue<e>(buf + 12, gotPltEntryAddr, 16, 0); } template <class ELFT> -bool MIPS<ELFT>::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { +bool MIPS<ELFT>::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { // Any MIPS PIC code function is invoked with its address in register $t9. // So if we have a branch instruction from non-PIC code to the PIC one // we cannot make the jump directly and need to create a small stubs // to save the target function address. // See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - if (Type != R_MIPS_26 && Type != R_MICROMIPS_26_S1 && - Type != R_MICROMIPS_PC26_S1) + if (type != R_MIPS_26 && type != R_MIPS_PC26_S2 && + type != R_MICROMIPS_26_S1 && type != R_MICROMIPS_PC26_S1) return false; - auto *F = dyn_cast_or_null<ELFFileBase<ELFT>>(File); - if (!F) + auto *f = dyn_cast_or_null<ObjFile<ELFT>>(file); + if (!f) return false; // If current file has PIC code, LA25 stub is not required. - if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC) + if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC) return false; - auto *D = dyn_cast<Defined>(&S); + auto *d = dyn_cast<Defined>(&s); // LA25 is required if target file has PIC code // or target symbol is a PIC symbol. - return D && isMipsPIC<ELFT>(D); + return d && isMipsPIC<ELFT>(d); } template <class ELFT> -int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - const endianness E = ELFT::TargetEndianness; - switch (Type) { +int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const { + const endianness e = ELFT::TargetEndianness; + switch (type) { case R_MIPS_32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - return SignExtend64<32>(read32<E>(Buf)); + return SignExtend64<32>(read32<e>(buf)); case R_MIPS_26: // FIXME (simon): If the relocation target symbol is not a PLT entry // we should use another expression for calculation: // ((A << 2) | (P & 0xf0000000)) >> 2 - return SignExtend64<28>(read32<E>(Buf) << 2); + return SignExtend64<28>(read32<e>(buf) << 2); case R_MIPS_GOT16: case R_MIPS_HI16: case R_MIPS_PCHI16: - return SignExtend64<16>(read32<E>(Buf)) << 16; + return SignExtend64<16>(read32<e>(buf)) << 16; case R_MIPS_GPREL16: case R_MIPS_LO16: case R_MIPS_PCLO16: @@ -386,54 +389,54 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, RelType Type) const { case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: - return SignExtend64<16>(read32<E>(Buf)); + return SignExtend64<16>(read32<e>(buf)); case R_MICROMIPS_GOT16: case R_MICROMIPS_HI16: - return SignExtend64<16>(readShuffle<E>(Buf)) << 16; + return SignExtend64<16>(readShuffle<e>(buf)) << 16; case R_MICROMIPS_GPREL16: case R_MICROMIPS_LO16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: - return SignExtend64<16>(readShuffle<E>(Buf)); + return SignExtend64<16>(readShuffle<e>(buf)); case R_MICROMIPS_GPREL7_S2: - return SignExtend64<9>(readShuffle<E>(Buf) << 2); + return SignExtend64<9>(readShuffle<e>(buf) << 2); case R_MIPS_PC16: - return SignExtend64<18>(read32<E>(Buf) << 2); + return SignExtend64<18>(read32<e>(buf) << 2); case R_MIPS_PC19_S2: - return SignExtend64<21>(read32<E>(Buf) << 2); + return SignExtend64<21>(read32<e>(buf) << 2); case R_MIPS_PC21_S2: - return SignExtend64<23>(read32<E>(Buf) << 2); + return SignExtend64<23>(read32<e>(buf) << 2); case R_MIPS_PC26_S2: - return SignExtend64<28>(read32<E>(Buf) << 2); + return SignExtend64<28>(read32<e>(buf) << 2); case R_MIPS_PC32: - return SignExtend64<32>(read32<E>(Buf)); + return SignExtend64<32>(read32<e>(buf)); case R_MICROMIPS_26_S1: - return SignExtend64<27>(readShuffle<E>(Buf) << 1); + return SignExtend64<27>(readShuffle<e>(buf) << 1); case R_MICROMIPS_PC7_S1: - return SignExtend64<8>(read16<E>(Buf) << 1); + return SignExtend64<8>(read16<e>(buf) << 1); case R_MICROMIPS_PC10_S1: - return SignExtend64<11>(read16<E>(Buf) << 1); + return SignExtend64<11>(read16<e>(buf) << 1); case R_MICROMIPS_PC16_S1: - return SignExtend64<17>(readShuffle<E>(Buf) << 1); + return SignExtend64<17>(readShuffle<e>(buf) << 1); case R_MICROMIPS_PC18_S3: - return SignExtend64<21>(readShuffle<E>(Buf) << 3); + return SignExtend64<21>(readShuffle<e>(buf) << 3); case R_MICROMIPS_PC19_S2: - return SignExtend64<21>(readShuffle<E>(Buf) << 2); + return SignExtend64<21>(readShuffle<e>(buf) << 2); case R_MICROMIPS_PC21_S1: - return SignExtend64<22>(readShuffle<E>(Buf) << 1); + return SignExtend64<22>(readShuffle<e>(buf) << 1); case R_MICROMIPS_PC23_S2: - return SignExtend64<25>(readShuffle<E>(Buf) << 2); + return SignExtend64<25>(readShuffle<e>(buf) << 2); case R_MICROMIPS_PC26_S1: - return SignExtend64<27>(readShuffle<E>(Buf) << 1); + return SignExtend64<27>(readShuffle<e>(buf) << 1); default: return 0; } } static std::pair<uint32_t, uint64_t> -calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) { +calculateMipsRelChain(uint8_t *loc, RelType type, uint64_t val) { // MIPS N64 ABI packs multiple relocations into the single relocation // record. In general, all up to three relocations can have arbitrary // types. In fact, Clang and GCC uses only a few combinations. For now, @@ -446,72 +449,134 @@ calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) { // relocations used to modify result of the first one: extend it to // 64-bit, extract high or low part etc. For details, see part 2.9 Relocation // at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf - RelType Type2 = (Type >> 8) & 0xff; - RelType Type3 = (Type >> 16) & 0xff; - if (Type2 == R_MIPS_NONE && Type3 == R_MIPS_NONE) - return std::make_pair(Type, Val); - if (Type2 == R_MIPS_64 && Type3 == R_MIPS_NONE) - return std::make_pair(Type2, Val); - if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16)) - return std::make_pair(Type3, -Val); - error(getErrorLocation(Loc) + "unsupported relocations combination " + - Twine(Type)); - return std::make_pair(Type & 0xff, Val); + RelType type2 = (type >> 8) & 0xff; + RelType type3 = (type >> 16) & 0xff; + if (type2 == R_MIPS_NONE && type3 == R_MIPS_NONE) + return std::make_pair(type, val); + if (type2 == R_MIPS_64 && type3 == R_MIPS_NONE) + return std::make_pair(type2, val); + if (type2 == R_MIPS_SUB && (type3 == R_MIPS_HI16 || type3 == R_MIPS_LO16)) + return std::make_pair(type3, -val); + error(getErrorLocation(loc) + "unsupported relocations combination " + + Twine(type)); + return std::make_pair(type & 0xff, val); +} + +static bool isBranchReloc(RelType type) { + return type == R_MIPS_26 || type == R_MIPS_PC26_S2 || + type == R_MIPS_PC21_S2 || type == R_MIPS_PC16; +} + +static bool isMicroBranchReloc(RelType type) { + return type == R_MICROMIPS_26_S1 || type == R_MICROMIPS_PC16_S1 || + type == R_MICROMIPS_PC10_S1 || type == R_MICROMIPS_PC7_S1; } template <class ELFT> -void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - const endianness E = ELFT::TargetEndianness; +static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) { + // Here we need to detect jump/branch from regular MIPS code + // to a microMIPS target and vice versa. In that cases jump + // instructions need to be replaced by their "cross-mode" + // equivalents. + const endianness e = ELFT::TargetEndianness; + bool isMicroTgt = val & 0x1; + bool isCrossJump = (isMicroTgt && isBranchReloc(type)) || + (!isMicroTgt && isMicroBranchReloc(type)); + if (!isCrossJump) + return val; + + switch (type) { + case R_MIPS_26: { + uint32_t inst = read32<e>(loc) >> 26; + if (inst == 0x3 || inst == 0x1d) { // JAL or JALX + writeValue<e>(loc, 0x1d << 26, 32, 0); + return val; + } + break; + } + case R_MICROMIPS_26_S1: { + uint32_t inst = readShuffle<e>(loc) >> 26; + if (inst == 0x3d || inst == 0x3c) { // JAL32 or JALX32 + val >>= 1; + writeShuffleValue<e>(loc, 0x3c << 26, 32, 0); + return val; + } + break; + } + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + // FIXME (simon): Support valid branch relocations. + break; + default: + llvm_unreachable("unexpected jump/branch relocation"); + } + + error(getErrorLocation(loc) + + "unsupported jump/branch instruction between ISA modes referenced by " + + toString(type) + " relocation"); + return val; +} + +template <class ELFT> +void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + const endianness e = ELFT::TargetEndianness; + + if (ELFT::Is64Bits || config->mipsN32Abi) + std::tie(type, val) = calculateMipsRelChain(loc, type, val); - if (ELFT::Is64Bits || Config->MipsN32Abi) - std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val); + // Detect cross-mode jump/branch and fix instruction. + val = fixupCrossModeJump<ELFT>(loc, type, val); // Thread pointer and DRP offsets from the start of TLS data area. // https://www.linux-mips.org/wiki/NPTL - if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 || - Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64 || - Type == R_MICROMIPS_TLS_DTPREL_HI16 || - Type == R_MICROMIPS_TLS_DTPREL_LO16) { - Val -= 0x8000; - } else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 || - Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64 || - Type == R_MICROMIPS_TLS_TPREL_HI16 || - Type == R_MICROMIPS_TLS_TPREL_LO16) { - Val -= 0x7000; + if (type == R_MIPS_TLS_DTPREL_HI16 || type == R_MIPS_TLS_DTPREL_LO16 || + type == R_MIPS_TLS_DTPREL32 || type == R_MIPS_TLS_DTPREL64 || + type == R_MICROMIPS_TLS_DTPREL_HI16 || + type == R_MICROMIPS_TLS_DTPREL_LO16) { + val -= 0x8000; + } else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 || + type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 || + type == R_MICROMIPS_TLS_TPREL_HI16 || + type == R_MICROMIPS_TLS_TPREL_LO16) { + val -= 0x7000; } - switch (Type) { + switch (type) { case R_MIPS_32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - write32<E>(Loc, Val); + write32<e>(loc, val); break; case R_MIPS_64: case R_MIPS_TLS_DTPREL64: case R_MIPS_TLS_TPREL64: - write64<E>(Loc, Val); + write64<e>(loc, val); break; case R_MIPS_26: - writeValue<E>(Loc, Val, 26, 2); + writeValue<e>(loc, val, 26, 2); break; case R_MIPS_GOT16: // The R_MIPS_GOT16 relocation's value in "relocatable" linking mode // is updated addend (not a GOT index). In that case write high 16 bits // to store a correct addend value. - if (Config->Relocatable) { - writeValue<E>(Loc, Val + 0x8000, 16, 16); + if (config->relocatable) { + writeValue<e>(loc, val + 0x8000, 16, 16); } else { - checkInt(Loc, Val, 16, Type); - writeValue<E>(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeValue<e>(loc, val, 16, 0); } break; case R_MICROMIPS_GOT16: - if (Config->Relocatable) { - writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16); + if (config->relocatable) { + writeShuffleValue<e>(loc, val + 0x8000, 16, 16); } else { - checkInt(Loc, Val, 16, Type); - writeShuffleValue<E>(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeShuffleValue<e>(loc, val, 16, 0); } break; case R_MIPS_CALL16: @@ -521,7 +586,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_TLS_GD: case R_MIPS_TLS_GOTTPREL: case R_MIPS_TLS_LDM: - checkInt(Loc, Val, 16, Type); + checkInt(loc, val, 16, type); LLVM_FALLTHROUGH; case R_MIPS_CALL_LO16: case R_MIPS_GOT_LO16: @@ -530,13 +595,13 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_LO16: - writeValue<E>(Loc, Val, 16, 0); + writeValue<e>(loc, val, 16, 0); break; case R_MICROMIPS_GPREL16: case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: - checkInt(Loc, Val, 16, Type); - writeShuffleValue<E>(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeShuffleValue<e>(loc, val, 16, 0); break; case R_MICROMIPS_CALL16: case R_MICROMIPS_CALL_LO16: @@ -544,11 +609,11 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_GOTTPREL: case R_MICROMIPS_TLS_TPREL_LO16: - writeShuffleValue<E>(Loc, Val, 16, 0); + writeShuffleValue<e>(loc, val, 16, 0); break; case R_MICROMIPS_GPREL7_S2: - checkInt(Loc, Val, 7, Type); - writeShuffleValue<E>(Loc, Val, 7, 2); + checkInt(loc, val, 7, type); + writeShuffleValue<e>(loc, val, 7, 2); break; case R_MIPS_CALL_HI16: case R_MIPS_GOT_HI16: @@ -556,113 +621,113 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_PCHI16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_TPREL_HI16: - writeValue<E>(Loc, Val + 0x8000, 16, 16); + writeValue<e>(loc, val + 0x8000, 16, 16); break; case R_MICROMIPS_CALL_HI16: case R_MICROMIPS_GOT_HI16: case R_MICROMIPS_HI16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_TPREL_HI16: - writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16); + writeShuffleValue<e>(loc, val + 0x8000, 16, 16); break; case R_MIPS_HIGHER: - writeValue<E>(Loc, Val + 0x80008000, 16, 32); + writeValue<e>(loc, val + 0x80008000, 16, 32); break; case R_MIPS_HIGHEST: - writeValue<E>(Loc, Val + 0x800080008000, 16, 48); + writeValue<e>(loc, val + 0x800080008000, 16, 48); break; case R_MIPS_JALR: case R_MICROMIPS_JALR: // Ignore this optimization relocation for now break; case R_MIPS_PC16: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 18, Type); - writeValue<E>(Loc, Val, 16, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 18, type); + writeValue<e>(loc, val, 16, 2); break; case R_MIPS_PC19_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 21, Type); - writeValue<E>(Loc, Val, 19, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 21, type); + writeValue<e>(loc, val, 19, 2); break; case R_MIPS_PC21_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 23, Type); - writeValue<E>(Loc, Val, 21, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 23, type); + writeValue<e>(loc, val, 21, 2); break; case R_MIPS_PC26_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 28, Type); - writeValue<E>(Loc, Val, 26, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 28, type); + writeValue<e>(loc, val, 26, 2); break; case R_MIPS_PC32: - writeValue<E>(Loc, Val, 32, 0); + writeValue<e>(loc, val, 32, 0); break; case R_MICROMIPS_26_S1: case R_MICROMIPS_PC26_S1: - checkInt(Loc, Val, 27, Type); - writeShuffleValue<E>(Loc, Val, 26, 1); + checkInt(loc, val, 27, type); + writeShuffleValue<e>(loc, val, 26, 1); break; case R_MICROMIPS_PC7_S1: - checkInt(Loc, Val, 8, Type); - writeMicroRelocation16<E>(Loc, Val, 7, 1); + checkInt(loc, val, 8, type); + writeMicroRelocation16<e>(loc, val, 7, 1); break; case R_MICROMIPS_PC10_S1: - checkInt(Loc, Val, 11, Type); - writeMicroRelocation16<E>(Loc, Val, 10, 1); + checkInt(loc, val, 11, type); + writeMicroRelocation16<e>(loc, val, 10, 1); break; case R_MICROMIPS_PC16_S1: - checkInt(Loc, Val, 17, Type); - writeShuffleValue<E>(Loc, Val, 16, 1); + checkInt(loc, val, 17, type); + writeShuffleValue<e>(loc, val, 16, 1); break; case R_MICROMIPS_PC18_S3: - checkInt(Loc, Val, 21, Type); - writeShuffleValue<E>(Loc, Val, 18, 3); + checkInt(loc, val, 21, type); + writeShuffleValue<e>(loc, val, 18, 3); break; case R_MICROMIPS_PC19_S2: - checkInt(Loc, Val, 21, Type); - writeShuffleValue<E>(Loc, Val, 19, 2); + checkInt(loc, val, 21, type); + writeShuffleValue<e>(loc, val, 19, 2); break; case R_MICROMIPS_PC21_S1: - checkInt(Loc, Val, 22, Type); - writeShuffleValue<E>(Loc, Val, 21, 1); + checkInt(loc, val, 22, type); + writeShuffleValue<e>(loc, val, 21, 1); break; case R_MICROMIPS_PC23_S2: - checkInt(Loc, Val, 25, Type); - writeShuffleValue<E>(Loc, Val, 23, 2); + checkInt(loc, val, 25, type); + writeShuffleValue<e>(loc, val, 23, 2); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType Type) const { - return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST || - Type == R_MICROMIPS_LO16; +template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType type) const { + return type == R_MIPS_LO16 || type == R_MIPS_GOT_OFST || + type == R_MICROMIPS_LO16; } // Return true if the symbol is a PIC function. -template <class ELFT> bool elf::isMipsPIC(const Defined *Sym) { - if (!Sym->isFunc()) +template <class ELFT> bool elf::isMipsPIC(const Defined *sym) { + if (!sym->isFunc()) return false; - if (Sym->StOther & STO_MIPS_PIC) + if (sym->stOther & STO_MIPS_PIC) return true; - if (!Sym->Section) + if (!sym->section) return false; - ObjFile<ELFT> *File = - cast<InputSectionBase>(Sym->Section)->template getFile<ELFT>(); - if (!File) + ObjFile<ELFT> *file = + cast<InputSectionBase>(sym->section)->template getFile<ELFT>(); + if (!file) return false; - return File->getObj().getHeader()->e_flags & EF_MIPS_PIC; + return file->getObj().getHeader()->e_flags & EF_MIPS_PIC; } template <class ELFT> TargetInfo *elf::getMipsTargetInfo() { - static MIPS<ELFT> Target; - return &Target; + static MIPS<ELFT> target; + return ⌖ } template TargetInfo *elf::getMipsTargetInfo<ELF32LE>(); diff --git a/ELF/Arch/MipsArchTree.cpp b/ELF/Arch/MipsArchTree.cpp index 98ceac3075e0..f64d03756457 100644 --- a/ELF/Arch/MipsArchTree.cpp +++ b/ELF/Arch/MipsArchTree.cpp @@ -1,9 +1,8 @@ //===- MipsArchTree.cpp --------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===---------------------------------------------------------------------===// // @@ -29,18 +28,18 @@ using namespace lld::elf; namespace { struct ArchTreeEdge { - uint32_t Child; - uint32_t Parent; + uint32_t child; + uint32_t parent; }; struct FileFlags { - InputFile *File; - uint32_t Flags; + InputFile *file; + uint32_t flags; }; } // namespace -static StringRef getAbiName(uint32_t Flags) { - switch (Flags) { +static StringRef getAbiName(uint32_t flags) { + switch (flags) { case 0: return "n64"; case EF_MIPS_ABI2: @@ -58,76 +57,76 @@ static StringRef getAbiName(uint32_t Flags) { } } -static StringRef getNanName(bool IsNan2008) { - return IsNan2008 ? "2008" : "legacy"; +static StringRef getNanName(bool isNan2008) { + return isNan2008 ? "2008" : "legacy"; } -static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; } +static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; } -static void checkFlags(ArrayRef<FileFlags> Files) { - assert(!Files.empty() && "expected non-empty file list"); +static void checkFlags(ArrayRef<FileFlags> files) { + assert(!files.empty() && "expected non-empty file list"); - uint32_t ABI = Files[0].Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); - bool Nan = Files[0].Flags & EF_MIPS_NAN2008; - bool Fp = Files[0].Flags & EF_MIPS_FP64; + uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + bool nan = files[0].flags & EF_MIPS_NAN2008; + bool fp = files[0].flags & EF_MIPS_FP64; - for (const FileFlags &F : Files) { - if (Config->Is64 && F.Flags & EF_MIPS_MICROMIPS) - error(toString(F.File) + ": microMIPS 64-bit is not supported"); + for (const FileFlags &f : files) { + if (config->is64 && f.flags & EF_MIPS_MICROMIPS) + error(toString(f.file) + ": microMIPS 64-bit is not supported"); - uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); - if (ABI != ABI2) - error(toString(F.File) + ": ABI '" + getAbiName(ABI2) + - "' is incompatible with target ABI '" + getAbiName(ABI) + "'"); + uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + if (abi != abi2) + error(toString(f.file) + ": ABI '" + getAbiName(abi2) + + "' is incompatible with target ABI '" + getAbiName(abi) + "'"); - bool Nan2 = F.Flags & EF_MIPS_NAN2008; - if (Nan != Nan2) - error(toString(F.File) + ": -mnan=" + getNanName(Nan2) + - " is incompatible with target -mnan=" + getNanName(Nan)); + bool nan2 = f.flags & EF_MIPS_NAN2008; + if (nan != nan2) + error(toString(f.file) + ": -mnan=" + getNanName(nan2) + + " is incompatible with target -mnan=" + getNanName(nan)); - bool Fp2 = F.Flags & EF_MIPS_FP64; - if (Fp != Fp2) - error(toString(F.File) + ": -mfp" + getFpName(Fp2) + - " is incompatible with target -mfp" + getFpName(Fp)); + bool fp2 = f.flags & EF_MIPS_FP64; + if (fp != fp2) + error(toString(f.file) + ": -mfp" + getFpName(fp2) + + " is incompatible with target -mfp" + getFpName(fp)); } } -static uint32_t getMiscFlags(ArrayRef<FileFlags> Files) { - uint32_t Ret = 0; - for (const FileFlags &F : Files) - Ret |= F.Flags & +static uint32_t getMiscFlags(ArrayRef<FileFlags> files) { + uint32_t ret = 0; + for (const FileFlags &f : files) + ret |= f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER | EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE); - return Ret; + return ret; } -static uint32_t getPicFlags(ArrayRef<FileFlags> Files) { +static uint32_t getPicFlags(ArrayRef<FileFlags> files) { // Check PIC/non-PIC compatibility. - bool IsPic = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - for (const FileFlags &F : Files.slice(1)) { - bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - if (IsPic && !IsPic2) - warn(toString(F.File) + + bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) { + bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + if (isPic && !isPic2) + warn(toString(f.file) + ": linking non-abicalls code with abicalls code " + - toString(Files[0].File)); - if (!IsPic && IsPic2) - warn(toString(F.File) + + toString(files[0].file)); + if (!isPic && isPic2) + warn(toString(f.file) + ": linking abicalls code with non-abicalls code " + - toString(Files[0].File)); + toString(files[0].file)); } // Compute the result PIC/non-PIC flag. - uint32_t Ret = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - for (const FileFlags &F : Files.slice(1)) - Ret &= F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) + ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); // PIC code is inherently CPIC and may not set CPIC flag explicitly. - if (Ret & EF_MIPS_PIC) - Ret |= EF_MIPS_CPIC; - return Ret; + if (ret & EF_MIPS_PIC) + ret |= EF_MIPS_CPIC; + return ret; } -static ArchTreeEdge ArchTree[] = { +static ArchTreeEdge archTree[] = { // MIPS32R6 and MIPS64R6 are not compatible with other extensions // MIPS64R2 extensions. {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2}, @@ -167,25 +166,25 @@ static ArchTreeEdge ArchTree[] = { {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, }; -static bool isArchMatched(uint32_t New, uint32_t Res) { - if (New == Res) +static bool isArchMatched(uint32_t New, uint32_t res) { + if (New == res) return true; - if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, Res)) + if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) return true; - if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, Res)) + if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res)) return true; - for (const auto &Edge : ArchTree) { - if (Res == Edge.Child) { - Res = Edge.Parent; - if (Res == New) + for (const auto &edge : archTree) { + if (res == edge.child) { + res = edge.parent; + if (res == New) return true; } } return false; } -static StringRef getMachName(uint32_t Flags) { - switch (Flags & EF_MIPS_MACH) { +static StringRef getMachName(uint32_t flags) { + switch (flags & EF_MIPS_MACH) { case EF_MIPS_MACH_NONE: return ""; case EF_MIPS_MACH_3900: @@ -229,8 +228,8 @@ static StringRef getMachName(uint32_t Flags) { } } -static StringRef getArchName(uint32_t Flags) { - switch (Flags & EF_MIPS_ARCH) { +static StringRef getArchName(uint32_t flags) { + switch (flags & EF_MIPS_ARCH) { case EF_MIPS_ARCH_1: return "mips1"; case EF_MIPS_ARCH_2: @@ -258,12 +257,12 @@ static StringRef getArchName(uint32_t Flags) { } } -static std::string getFullArchName(uint32_t Flags) { - StringRef Arch = getArchName(Flags); - StringRef Mach = getMachName(Flags); - if (Mach.empty()) - return Arch.str(); - return (Arch + " (" + Mach + ")").str(); +static std::string getFullArchName(uint32_t flags) { + StringRef arch = getArchName(flags); + StringRef mach = getMachName(flags); + if (mach.empty()) + return arch.str(); + return (arch + " (" + mach + ")").str(); } // There are (arguably too) many MIPS ISAs out there. Their relationships @@ -275,55 +274,55 @@ static std::string getFullArchName(uint32_t Flags) { // Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32 // are incompatible because nor mips3 is a parent for misp32, nor mips32 // is a parent for mips3. -static uint32_t getArchFlags(ArrayRef<FileFlags> Files) { - uint32_t Ret = Files[0].Flags & (EF_MIPS_ARCH | EF_MIPS_MACH); +static uint32_t getArchFlags(ArrayRef<FileFlags> files) { + uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH); - for (const FileFlags &F : Files.slice(1)) { - uint32_t New = F.Flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + for (const FileFlags &f : files.slice(1)) { + uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); // Check ISA compatibility. - if (isArchMatched(New, Ret)) + if (isArchMatched(New, ret)) continue; - if (!isArchMatched(Ret, New)) { - error("incompatible target ISA:\n>>> " + toString(Files[0].File) + ": " + - getFullArchName(Ret) + "\n>>> " + toString(F.File) + ": " + + if (!isArchMatched(ret, New)) { + error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " + + getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " + getFullArchName(New)); return 0; } - Ret = New; + ret = New; } - return Ret; + return ret; } template <class ELFT> uint32_t elf::calcMipsEFlags() { - std::vector<FileFlags> V; - for (InputFile *F : ObjectFiles) - V.push_back({F, cast<ObjFile<ELFT>>(F)->getObj().getHeader()->e_flags}); - if (V.empty()) + std::vector<FileFlags> v; + for (InputFile *f : objectFiles) + v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags}); + if (v.empty()) return 0; - checkFlags(V); - return getMiscFlags(V) | getPicFlags(V) | getArchFlags(V); + checkFlags(v); + return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v); } -static int compareMipsFpAbi(uint8_t FpA, uint8_t FpB) { - if (FpA == FpB) +static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) { + if (fpA == fpB) return 0; - if (FpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) return 1; - if (FpB == Mips::Val_GNU_MIPS_ABI_FP_64A && - FpA == Mips::Val_GNU_MIPS_ABI_FP_64) + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A && + fpA == Mips::Val_GNU_MIPS_ABI_FP_64) return 1; - if (FpB != Mips::Val_GNU_MIPS_ABI_FP_XX) + if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX) return -1; - if (FpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || - FpA == Mips::Val_GNU_MIPS_ABI_FP_64 || - FpA == Mips::Val_GNU_MIPS_ABI_FP_64A) + if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64 || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64A) return 1; return -1; } -static StringRef getMipsFpAbiName(uint8_t FpAbi) { - switch (FpAbi) { +static StringRef getMipsFpAbiName(uint8_t fpAbi) { + switch (fpAbi) { case Mips::Val_GNU_MIPS_ABI_FP_ANY: return "any"; case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE: @@ -345,43 +344,43 @@ static StringRef getMipsFpAbiName(uint8_t FpAbi) { } } -uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, - StringRef FileName) { - if (compareMipsFpAbi(NewFlag, OldFlag) >= 0) - return NewFlag; - if (compareMipsFpAbi(OldFlag, NewFlag) < 0) - error(FileName + ": floating point ABI '" + getMipsFpAbiName(NewFlag) + +uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, + StringRef fileName) { + if (compareMipsFpAbi(newFlag, oldFlag) >= 0) + return newFlag; + if (compareMipsFpAbi(oldFlag, newFlag) < 0) + error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) + "' is incompatible with target floating point ABI '" + - getMipsFpAbiName(OldFlag) + "'"); - return OldFlag; + getMipsFpAbiName(oldFlag) + "'"); + return oldFlag; } -template <class ELFT> static bool isN32Abi(const InputFile *F) { - if (auto *EF = dyn_cast<ELFFileBase<ELFT>>(F)) - return EF->getObj().getHeader()->e_flags & EF_MIPS_ABI2; +template <class ELFT> static bool isN32Abi(const InputFile *f) { + if (auto *ef = dyn_cast<ELFFileBase>(f)) + return ef->template getObj<ELFT>().getHeader()->e_flags & EF_MIPS_ABI2; return false; } -bool elf::isMipsN32Abi(const InputFile *F) { - switch (Config->EKind) { +bool elf::isMipsN32Abi(const InputFile *f) { + switch (config->ekind) { case ELF32LEKind: - return isN32Abi<ELF32LE>(F); + return isN32Abi<ELF32LE>(f); case ELF32BEKind: - return isN32Abi<ELF32BE>(F); + return isN32Abi<ELF32BE>(f); case ELF64LEKind: - return isN32Abi<ELF64LE>(F); + return isN32Abi<ELF64LE>(f); case ELF64BEKind: - return isN32Abi<ELF64BE>(F); + return isN32Abi<ELF64BE>(f); default: llvm_unreachable("unknown Config->EKind"); } } -bool elf::isMicroMips() { return Config->EFlags & EF_MIPS_MICROMIPS; } +bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } bool elf::isMipsR6() { - uint32_t Arch = Config->EFlags & EF_MIPS_ARCH; - return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6; + uint32_t arch = config->eflags & EF_MIPS_ARCH; + return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6; } template uint32_t elf::calcMipsEFlags<ELF32LE>(); diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp index 767378067341..46c5891e4f8a 100644 --- a/ELF/Arch/PPC.cpp +++ b/ELF/Arch/PPC.cpp @@ -1,13 +1,14 @@ //===- PPC.cpp ------------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +#include "OutputSections.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" @@ -22,60 +23,410 @@ namespace { class PPC final : public TargetInfo { public: PPC(); - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotHeader(uint8_t *buf) const override; + void writePltHeader(uint8_t *buf) const override { + llvm_unreachable("should call writePPC32GlinkSection() instead"); + } + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override { + llvm_unreachable("should call writePPC32GlinkSection() instead"); + } + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + int getTlsGdRelaxSkip(RelType type) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace +static uint16_t lo(uint32_t v) { return v; } +static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; } + +static uint32_t readFromHalf16(const uint8_t *loc) { + return read32(config->isLE ? loc : loc - 2); +} + +static void writeFromHalf16(uint8_t *loc, uint32_t insn) { + write32(config->isLE ? loc : loc - 2, insn); +} + +void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { + // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an + // absolute address from a specific .plt slot (usually called .got.plt on + // other targets) and jumps there. + // + // a) With immediate binding (BIND_NOW), the .plt entry is resolved at load + // time. The .glink section is not used. + // b) With lazy binding, the .plt entry points to a `b PLTresolve` + // instruction in .glink, filled in by PPC::writeGotPlt(). + + // Write N `b PLTresolve` first. + for (size_t i = 0; i != numEntries; ++i) + write32(buf + 4 * i, 0x48000000 | 4 * (numEntries - i)); + buf += 4 * numEntries; + + // Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve() + // computes the PLT index (by computing the distance from the landing b to + // itself) and calls _dl_runtime_resolve() (in glibc). + uint32_t got = in.got->getVA(); + uint32_t glink = in.plt->getVA(); // VA of .glink + const uint8_t *end = buf + 64; + if (config->isPic) { + uint32_t afterBcl = in.plt->getSize() - target->pltHeaderSize + 12; + uint32_t gotBcl = got + 4 - (glink + afterBcl); + write32(buf + 0, 0x3d6b0000 | ha(afterBcl)); // addis r11,r11,1f-glink@ha + write32(buf + 4, 0x7c0802a6); // mflr r0 + write32(buf + 8, 0x429f0005); // bcl 20,30,.+4 + write32(buf + 12, 0x396b0000 | lo(afterBcl)); // 1: addi r11,r11,1b-.glink@l + write32(buf + 16, 0x7d8802a6); // mflr r12 + write32(buf + 20, 0x7c0803a6); // mtlr r0 + write32(buf + 24, 0x7d6c5850); // sub r11,r11,r12 + write32(buf + 28, 0x3d8c0000 | ha(gotBcl)); // addis 12,12,GOT+4-1b@ha + if (ha(gotBcl) == ha(gotBcl + 4)) { + write32(buf + 32, 0x800c0000 | lo(gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12) + write32(buf + 36, + 0x818c0000 | lo(gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12) + } else { + write32(buf + 32, 0x840c0000 | lo(gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12) + write32(buf + 36, 0x818c0000 | 4); // lwz r12,r12,4(r12) + } + write32(buf + 40, 0x7c0903a6); // mtctr 0 + write32(buf + 44, 0x7c0b5a14); // add r0,11,11 + write32(buf + 48, 0x7d605a14); // add r11,0,11 + write32(buf + 52, 0x4e800420); // bctr + buf += 56; + } else { + write32(buf + 0, 0x3d800000 | ha(got + 4)); // lis r12,GOT+4@ha + write32(buf + 4, 0x3d6b0000 | ha(-glink)); // addis r11,r11,-Glink@ha + if (ha(got + 4) == ha(got + 8)) + write32(buf + 8, 0x800c0000 | lo(got + 4)); // lwz r0,GOT+4@l(r12) + else + write32(buf + 8, 0x840c0000 | lo(got + 4)); // lwzu r0,GOT+4@l(r12) + write32(buf + 12, 0x396b0000 | lo(-glink)); // addi r11,r11,-Glink@l + write32(buf + 16, 0x7c0903a6); // mtctr r0 + write32(buf + 20, 0x7c0b5a14); // add r0,r11,r11 + if (ha(got + 4) == ha(got + 8)) + write32(buf + 24, 0x818c0000 | lo(got + 8)); // lwz r12,GOT+8@ha(r12) + else + write32(buf + 24, 0x818c0000 | 4); // lwz r12,4(r12) + write32(buf + 28, 0x7d605a14); // add r11,r0,r11 + write32(buf + 32, 0x4e800420); // bctr + buf += 36; + } + + // Pad with nop. They should not be executed. + for (; buf < end; buf += 4) + write32(buf, 0x60000000); +} + PPC::PPC() { - NoneRel = R_PPC_NONE; - GotBaseSymOff = 0x8000; - GotBaseSymInGotPlt = false; + gotRel = R_PPC_GLOB_DAT; + noneRel = R_PPC_NONE; + pltRel = R_PPC_JMP_SLOT; + relativeRel = R_PPC_RELATIVE; + iRelativeRel = R_PPC_IRELATIVE; + symbolicRel = R_PPC_ADDR32; + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 3; + gotPltHeaderEntriesNum = 0; + pltHeaderSize = 64; // size of PLTresolve in .glink + pltEntrySize = 4; + + needsThunks = true; + + tlsModuleIndexRel = R_PPC_DTPMOD32; + tlsOffsetRel = R_PPC_DTPREL32; + tlsGotRel = R_PPC_TPREL32; + + defaultMaxPageSize = 65536; + defaultImageBase = 0x10000000; + + write32(trapInstr.data(), 0x7fe00008); +} + +void PPC::writeGotHeader(uint8_t *buf) const { + // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC + // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1], + // link_map in _GLOBAL_OFFSET_TABLE_[2]. + write32(buf, mainPart->dynamic->getVA()); +} + +void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const { + // Address of the symbol resolver stub in .glink . + write32(buf, in.plt->getVA() + 4 * s.pltIndex); +} + +bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + if (type != R_PPC_REL24 && type != R_PPC_PLTREL24) + return false; + if (s.isInPlt()) + return true; + if (s.isUndefWeak()) + return false; + return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA())); } -RelExpr PPC::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; } + +bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + uint64_t offset = dst - src; + if (type == R_PPC_REL24 || type == R_PPC_PLTREL24) + return isInt<26>(offset); + llvm_unreachable("unsupported relocation type used in branch"); +} + +RelExpr PPC::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + case R_PPC_DTPREL32: + return R_DTPREL; case R_PPC_REL14: - case R_PPC_REL24: case R_PPC_REL32: + case R_PPC_LOCAL24PC: + case R_PPC_REL16_LO: + case R_PPC_REL16_HI: + case R_PPC_REL16_HA: return R_PC; - case R_PPC_PLTREL24: + case R_PPC_GOT16: + return R_GOT_OFF; + case R_PPC_REL24: return R_PLT_PC; + case R_PPC_PLTREL24: + return R_PPC32_PLTREL; + case R_PPC_GOT_TLSGD16: + return R_TLSGD_GOT; + case R_PPC_GOT_TLSLD16: + return R_TLSLD_GOT; + case R_PPC_GOT_TPREL16: + return R_GOT_OFF; + case R_PPC_TLS: + return R_TLSIE_HINT; + case R_PPC_TLSGD: + return R_TLSDESC_CALL; + case R_PPC_TLSLD: + return R_TLSLD_HINT; + case R_PPC_TPREL16: + case R_PPC_TPREL16_HA: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + return R_TLS; default: return R_ABS; } } -void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +RelType PPC::getDynRel(RelType type) const { + if (type == R_PPC_ADDR32) + return type; + return R_PPC_NONE; +} + +static std::pair<RelType, uint64_t> fromDTPREL(RelType type, uint64_t val) { + uint64_t dtpBiasedVal = val - 0x8000; + switch (type) { + case R_PPC_DTPREL16: + return {R_PPC64_ADDR16, dtpBiasedVal}; + case R_PPC_DTPREL16_HA: + return {R_PPC_ADDR16_HA, dtpBiasedVal}; + case R_PPC_DTPREL16_HI: + return {R_PPC_ADDR16_HI, dtpBiasedVal}; + case R_PPC_DTPREL16_LO: + return {R_PPC_ADDR16_LO, dtpBiasedVal}; + case R_PPC_DTPREL32: + return {R_PPC_ADDR32, dtpBiasedVal}; + default: + return {type, val}; + } +} + +void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + RelType newType; + std::tie(newType, val) = fromDTPREL(type, val); + switch (newType) { + case R_PPC_ADDR16: + checkIntUInt(loc, val, 16, type); + write16(loc, val); + break; + case R_PPC_GOT16: + case R_PPC_GOT_TLSGD16: + case R_PPC_GOT_TLSLD16: + case R_PPC_GOT_TPREL16: + case R_PPC_TPREL16: + checkInt(loc, val, 16, type); + write16(loc, val); + break; case R_PPC_ADDR16_HA: - write16be(Loc, (Val + 0x8000) >> 16); + case R_PPC_DTPREL16_HA: + case R_PPC_GOT_TLSGD16_HA: + case R_PPC_GOT_TLSLD16_HA: + case R_PPC_GOT_TPREL16_HA: + case R_PPC_REL16_HA: + case R_PPC_TPREL16_HA: + write16(loc, ha(val)); break; case R_PPC_ADDR16_HI: - write16be(Loc, Val >> 16); + case R_PPC_DTPREL16_HI: + case R_PPC_GOT_TLSGD16_HI: + case R_PPC_GOT_TLSLD16_HI: + case R_PPC_GOT_TPREL16_HI: + case R_PPC_REL16_HI: + case R_PPC_TPREL16_HI: + write16(loc, val >> 16); break; case R_PPC_ADDR16_LO: - write16be(Loc, Val); + case R_PPC_DTPREL16_LO: + case R_PPC_GOT_TLSGD16_LO: + case R_PPC_GOT_TLSLD16_LO: + case R_PPC_GOT_TPREL16_LO: + case R_PPC_REL16_LO: + case R_PPC_TPREL16_LO: + write16(loc, val); break; case R_PPC_ADDR32: case R_PPC_REL32: - write32be(Loc, Val); + write32(loc, val); break; - case R_PPC_REL14: - write32be(Loc, read32be(Loc) | (Val & 0xFFFC)); + case R_PPC_REL14: { + uint32_t mask = 0x0000FFFC; + checkInt(loc, val, 16, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; - case R_PPC_PLTREL24: + } case R_PPC_REL24: - write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC)); + case R_PPC_LOCAL24PC: + case R_PPC_PLTREL24: { + uint32_t mask = 0x03FFFFFC; + checkInt(loc, val, 26, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; + } + default: + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + } +} + +RelExpr PPC::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + if (expr == R_RELAX_TLS_LD_TO_LE) + return R_RELAX_TLS_LD_TO_LE_ABS; + return expr; +} + +int PPC::getTlsGdRelaxSkip(RelType type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation + // R_PPC_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (type == R_PPC_TLSGD || type == R_PPC_TLSLD) + return 2; + return 1; +} + +void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSGD16: { + // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA) + uint32_t insn = readFromHalf16(loc); + writeFromHalf16(loc, 0x80000000 | (insn & 0x03ff0000)); + relocateOne(loc, R_PPC_GOT_TPREL16, val); + break; + } + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2 + write32(loc, 0x7c631214); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + +void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSGD16: + // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha + writeFromHalf16(loc, 0x3c620000 | ha(val)); + break; + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l + write32(loc, 0x38630000 | lo(val)); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSLD16: + // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0 + writeFromHalf16(loc, 0x3c620000); + break; + case R_PPC_TLSLD: + // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel + // = r3+x-0x7000, so add 4096 to r3. + // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096 + write32(loc, 0x38631000); + break; + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + relocateOne(loc, type, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); + } +} + +void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TPREL16: { + // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha + uint32_t rt = readFromHalf16(loc) & 0x03e00000; + writeFromHalf16(loc, 0x3c020000 | rt | ha(val)); + break; + } + case R_PPC_TLS: { + uint32_t insn = read32(loc); + if (insn >> 26 != 31) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l + uint32_t dFormOp = getPPCDFormOp((read32(loc) & 0x000007fe) >> 1); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + write32(loc, (dFormOp << 26) | (insn & 0x03ff0000) | lo(val)); + break; + } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unsupported relocation for TLS IE to LE relaxation"); } } TargetInfo *elf::getPPCTargetInfo() { - static PPC Target; - return &Target; + static PPC target; + return ⌖ } diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp index 8a320c9a4e9e..70d284cfad71 100644 --- a/ELF/Arch/PPC64.cpp +++ b/ELF/Arch/PPC64.cpp @@ -1,9 +1,8 @@ //===- PPC64.cpp ----------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -20,8 +19,8 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; -static uint64_t PPC64TocOffset = 0x8000; -static uint64_t DynamicThreadPointerOffset = 0x8000; +static uint64_t ppc64TocOffset = 0x8000; +static uint64_t dynamicThreadPointerOffset = 0x8000; // The instruction encoding of bits 21-30 from the ISA for the Xform and Dform // instructions that can be used as part of the initial exec TLS sequence. @@ -65,16 +64,16 @@ uint64_t elf::getPPC64TocBase() { // TOC starts where the first of these sections starts. We always create a // .got when we see a relocation that uses it, so for us the start is always // the .got. - uint64_t TocVA = In.Got->getVA(); + uint64_t tocVA = in.got->getVA(); // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 // thus permitting a full 64 Kbytes segment. Note that the glibc startup // code (crt1.o) assumes that you can get from the TOC base to the // start of the .toc section with only a single (signed) 16-bit relocation. - return TocVA + PPC64TocOffset; + return tocVA + ppc64TocOffset; } -unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) { +unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { // The offset is encoded into the 3 most significant bits of the st_other // field, with some special values described in section 3.4.1 of the ABI: // 0 --> Zero offset between the GEP and LEP, and the function does NOT use @@ -86,43 +85,134 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) { // 2 --> 2^2 = 4 bytes --> 1 instruction. // 6 --> 2^6 = 64 bytes --> 16 instructions. // 7 --> Reserved. - uint8_t GepToLep = (StOther >> 5) & 7; - if (GepToLep < 2) + uint8_t gepToLep = (stOther >> 5) & 7; + if (gepToLep < 2) return 0; // The value encoded in the st_other bits is the // log-base-2(offset). - if (GepToLep < 7) - return 1 << GepToLep; + if (gepToLep < 7) + return 1 << gepToLep; error("reserved value of 7 in the 3 most-significant-bits of st_other"); return 0; } +bool elf::isPPC64SmallCodeModelTocReloc(RelType type) { + // The only small code model relocations that access the .toc section. + return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; +} + +// Find the R_PPC64_ADDR64 in .rela.toc with matching offset. +template <typename ELFT> +static std::pair<Defined *, int64_t> +getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { + if (tocSec->numRelocations == 0) + return {}; + + // .rela.toc contains exclusively R_PPC64_ADDR64 relocations sorted by + // r_offset: 0, 8, 16, etc. For a given Offset, Offset / 8 gives us the + // relocation index in most cases. + // + // In rare cases a TOC entry may store a constant that doesn't need an + // R_PPC64_ADDR64, the corresponding r_offset is therefore missing. Offset / 8 + // points to a relocation with larger r_offset. Do a linear probe then. + // Constants are extremely uncommon in .toc and the extra number of array + // accesses can be seen as a small constant. + ArrayRef<typename ELFT::Rela> relas = tocSec->template relas<ELFT>(); + uint64_t index = std::min<uint64_t>(offset / 8, relas.size() - 1); + for (;;) { + if (relas[index].r_offset == offset) { + Symbol &sym = tocSec->getFile<ELFT>()->getRelocTargetSym(relas[index]); + return {dyn_cast<Defined>(&sym), getAddend<ELFT>(relas[index])}; + } + if (relas[index].r_offset < offset || index == 0) + break; + --index; + } + return {}; +} + +// When accessing a symbol defined in another translation unit, compilers +// reserve a .toc entry, allocate a local label and generate toc-indirect +// instuctions: +// +// addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA +// ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry +// ld/lwa 3, 0(3) # load the value from the address +// +// .section .toc,"aw",@progbits +// .LC0: .tc var[TC],var +// +// If var is defined, non-preemptable and addressable with a 32-bit signed +// offset from the toc base, the address of var can be computed by adding an +// offset to the toc base, saving a load. +// +// addis 3,2,var@toc@ha # this may be relaxed to a nop, +// addi 3,3,var@toc@l # then this becomes addi 3,2,var@toc +// ld/lwa 3, 0(3) # load the value from the address +// +// Returns true if the relaxation is performed. +bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, + uint8_t *bufLoc) { + assert(config->tocOptimize); + if (rel.addend < 0) + return false; + + // If the symbol is not the .toc section, this isn't a toc-indirection. + Defined *defSym = dyn_cast<Defined>(rel.sym); + if (!defSym || !defSym->isSection() || defSym->section->name != ".toc") + return false; + + Defined *d; + int64_t addend; + auto *tocISB = cast<InputSectionBase>(defSym->section); + std::tie(d, addend) = + config->isLE ? getRelaTocSymAndAddend<ELF64LE>(tocISB, rel.addend) + : getRelaTocSymAndAddend<ELF64BE>(tocISB, rel.addend); + + // Only non-preemptable defined symbols can be relaxed. + if (!d || d->isPreemptible) + return false; + + // Two instructions can materialize a 32-bit signed offset from the toc base. + uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase(); + if (!isInt<32>(tocRelative)) + return false; + + // Add PPC64TocOffset that will be subtracted by relocateOne(). + target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset); + return true; +} + namespace { class PPC64 final : public TargetInfo { public: PPC64(); + int getTlsGdRelaxSkip(RelType type) const override; uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void writeGotHeader(uint8_t *Buf) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - - bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void writeGotHeader(uint8_t *buf) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; }; } // namespace @@ -130,19 +220,19 @@ public: // #higher(value), #highera(value), #highest(value), and #highesta(value) // macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi // document. -static uint16_t lo(uint64_t V) { return V; } -static uint16_t hi(uint64_t V) { return V >> 16; } -static uint16_t ha(uint64_t V) { return (V + 0x8000) >> 16; } -static uint16_t higher(uint64_t V) { return V >> 32; } -static uint16_t highera(uint64_t V) { return (V + 0x8000) >> 32; } -static uint16_t highest(uint64_t V) { return V >> 48; } -static uint16_t highesta(uint64_t V) { return (V + 0x8000) >> 48; } +static uint16_t lo(uint64_t v) { return v; } +static uint16_t hi(uint64_t v) { return v >> 16; } +static uint16_t ha(uint64_t v) { return (v + 0x8000) >> 16; } +static uint16_t higher(uint64_t v) { return v >> 32; } +static uint16_t highera(uint64_t v) { return (v + 0x8000) >> 32; } +static uint16_t highest(uint64_t v) { return v >> 48; } +static uint16_t highesta(uint64_t v) { return (v + 0x8000) >> 48; } // Extracts the 'PO' field of an instruction encoding. -static uint8_t getPrimaryOpCode(uint32_t Encoding) { return (Encoding >> 26); } +static uint8_t getPrimaryOpCode(uint32_t encoding) { return (encoding >> 26); } -static bool isDQFormInstruction(uint32_t Encoding) { - switch (getPrimaryOpCode(Encoding)) { +static bool isDQFormInstruction(uint32_t encoding) { + switch (getPrimaryOpCode(encoding)) { default: return false; case 56: @@ -152,12 +242,12 @@ static bool isDQFormInstruction(uint32_t Encoding) { // There are both DS and DQ instruction forms with this primary opcode. // Namely `lxv` and `stxv` are the DQ-forms that use it. // The DS 'XO' bits being set to 01 is restricted to DQ form. - return (Encoding & 3) == 0x1; + return (encoding & 3) == 0x1; } } -static bool isInstructionUpdateForm(uint32_t Encoding) { - switch (getPrimaryOpCode(Encoding)) { +static bool isInstructionUpdateForm(uint32_t encoding) { + switch (getPrimaryOpCode(encoding)) { default: return false; case LBZU: @@ -176,7 +266,7 @@ static bool isInstructionUpdateForm(uint32_t Encoding) { // between LD/LDU/LWA case LD: case STD: - return (Encoding & 3) == 1; + return (encoding & 3) == 1; } } @@ -185,40 +275,38 @@ static bool isInstructionUpdateForm(uint32_t Encoding) { // pointer is pointing into the middle of the word we want to extract, and on // little-endian it is pointing to the start of the word. These 2 helpers are to // simplify reading and writing in that context. -static void writeInstrFromHalf16(uint8_t *Loc, uint32_t Instr) { - write32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0), Instr); +static void writeFromHalf16(uint8_t *loc, uint32_t insn) { + write32(config->isLE ? loc : loc - 2, insn); } -static uint32_t readInstrFromHalf16(const uint8_t *Loc) { - return read32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0)); +static uint32_t readFromHalf16(const uint8_t *loc) { + return read32(config->isLE ? loc : loc - 2); } PPC64::PPC64() { - GotRel = R_PPC64_GLOB_DAT; - NoneRel = R_PPC64_NONE; - PltRel = R_PPC64_JMP_SLOT; - RelativeRel = R_PPC64_RELATIVE; - IRelativeRel = R_PPC64_IRELATIVE; - GotEntrySize = 8; - PltEntrySize = 4; - GotPltEntrySize = 8; - GotBaseSymInGotPlt = false; - GotBaseSymOff = 0x8000; - GotHeaderEntriesNum = 1; - GotPltHeaderEntriesNum = 2; - PltHeaderSize = 60; - NeedsThunks = true; - - TlsModuleIndexRel = R_PPC64_DTPMOD64; - TlsOffsetRel = R_PPC64_DTPREL64; - - TlsGotRel = R_PPC64_TPREL64; - - NeedsMoreStackNonSplit = false; + gotRel = R_PPC64_GLOB_DAT; + noneRel = R_PPC64_NONE; + pltRel = R_PPC64_JMP_SLOT; + relativeRel = R_PPC64_RELATIVE; + iRelativeRel = R_PPC64_IRELATIVE; + symbolicRel = R_PPC64_ADDR64; + pltEntrySize = 4; + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + gotPltHeaderEntriesNum = 2; + pltHeaderSize = 60; + needsThunks = true; + + tlsModuleIndexRel = R_PPC64_DTPMOD64; + tlsOffsetRel = R_PPC64_DTPREL64; + + tlsGotRel = R_PPC64_TPREL64; + + needsMoreStackNonSplit = false; // We need 64K pages (at least under glibc/Linux, the loader won't // set different permissions on a finer granularity than that). - DefaultMaxPageSize = 65536; + defaultMaxPageSize = 65536; // The PPC64 ELF ABI v1 spec, says: // @@ -228,31 +316,66 @@ PPC64::PPC64() { // // And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers // use 0x10000000 as the starting address. - DefaultImageBase = 0x10000000; + defaultImageBase = 0x10000000; - write32(TrapInstr.data(), 0x7fe00008); + write32(trapInstr.data(), 0x7fe00008); +} + +int PPC64::getTlsGdRelaxSkip(RelType type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC64_TLSGD / R_PPC64_TLSLD: marker relocation + // R_PPC64_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (type == R_PPC64_TLSGD || type == R_PPC64_TLSLD) + return 2; + return 1; } -static uint32_t getEFlags(InputFile *File) { - if (Config->EKind == ELF64BEKind) - return cast<ObjFile<ELF64BE>>(File)->getObj().getHeader()->e_flags; - return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags; +static uint32_t getEFlags(InputFile *file) { + if (config->ekind == ELF64BEKind) + return cast<ObjFile<ELF64BE>>(file)->getObj().getHeader()->e_flags; + return cast<ObjFile<ELF64LE>>(file)->getObj().getHeader()->e_flags; } // This file implements v2 ABI. This function makes sure that all // object files have v2 or an unspecified version as an ABI version. uint32_t PPC64::calcEFlags() const { - for (InputFile *F : ObjectFiles) { - uint32_t Flag = getEFlags(F); - if (Flag == 1) - error(toString(F) + ": ABI version 1 is not supported"); - else if (Flag > 2) - error(toString(F) + ": unrecognized e_flags: " + Twine(Flag)); + for (InputFile *f : objectFiles) { + uint32_t flag = getEFlags(f); + if (flag == 1) + error(toString(f) + ": ABI version 1 is not supported"); + else if (flag > 2) + error(toString(f) + ": unrecognized e_flags: " + Twine(flag)); } return 2; } -void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC64_TOC16_HA: + // Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop". + relocateOne(loc, type, val); + break; + case R_PPC64_TOC16_LO_DS: { + // Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or + // "addi reg, 2, var@toc". + uint32_t insn = readFromHalf16(loc); + if (getPrimaryOpCode(insn) != LD) + error("expected a 'ld' for got-indirect to toc-relative relaxing"); + writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000); + relocateOne(loc, R_PPC64_TOC16_LO, val); + break; + } + default: + llvm_unreachable("unexpected relocation type"); + } +} + +void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol @@ -268,30 +391,30 @@ void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, x@tprel@l - switch (Type) { + switch (type) { case R_PPC64_GOT_TLSGD16_HA: - writeInstrFromHalf16(Loc, 0x60000000); // nop + writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: - writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13 - relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13 + relocateOne(loc, R_PPC64_TPREL16_HA, val); break; case R_PPC64_TLSGD: - write32(Loc, 0x60000000); // nop - write32(Loc + 4, 0x38630000); // addi r3, r3 + write32(loc, 0x60000000); // nop + write32(loc + 4, 0x38630000); // addi r3, r3 // Since we are relocating a half16 type relocation and Loc + 4 points to // the start of an instruction we need to advance the buffer by an extra // 2 bytes on BE. - relocateOne(Loc + 4 + (Config->EKind == ELF64BEKind ? 2 : 0), - R_PPC64_TPREL16_LO, Val); + relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), + R_PPC64_TPREL16_LO, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement. // The local dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol @@ -307,16 +430,16 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, 4096 - switch (Type) { + switch (type) { case R_PPC64_GOT_TLSLD16_HA: - writeInstrFromHalf16(Loc, 0x60000000); // nop + writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSLD16_LO: - writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13, 0 + writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13, 0 break; case R_PPC64_TLSLD: - write32(Loc, 0x60000000); // nop - write32(Loc + 4, 0x38631000); // addi r3, r3, 4096 + write32(loc, 0x60000000); // nop + write32(loc + 4, 0x38631000); // addi r3, r3, 4096 break; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_HA: @@ -324,19 +447,15 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: - case R_PPC64_GOT_DTPREL16_HA: - case R_PPC64_GOT_DTPREL16_LO_DS: - case R_PPC64_GOT_DTPREL16_DS: - case R_PPC64_GOT_DTPREL16_HI: - relocateOne(Loc, Type, Val); + relocateOne(loc, type, val); break; default: llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); } } -static unsigned getDFormOp(unsigned SecondaryOp) { - switch (SecondaryOp) { +unsigned elf::getPPCDFormOp(unsigned secondaryOp) { + switch (secondaryOp) { case LBZX: return LBZ; case LHZX: @@ -356,12 +475,11 @@ static unsigned getDFormOp(unsigned SecondaryOp) { case ADD: return ADDI; default: - error("unrecognized instruction for IE to LE R_PPC64_TLS"); return 0; } } -void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { // The initial exec code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x @@ -381,26 +499,28 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // instruction, if we are accessing memory it will use any of the X-form // indexed load or store instructions. - unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0; - switch (Type) { + unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0; + switch (type) { case R_PPC64_GOT_TPREL16_HA: - write32(Loc - Offset, 0x60000000); // nop + write32(loc - offset, 0x60000000); // nop break; case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_DS: { - uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10 - write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13 - relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10 + write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13 + relocateOne(loc, R_PPC64_TPREL16_HA, val); break; } case R_PPC64_TLS: { - uint32_t PrimaryOp = getPrimaryOpCode(read32(Loc)); - if (PrimaryOp != 31) + uint32_t primaryOp = getPrimaryOpCode(read32(loc)); + if (primaryOp != 31) error("unrecognized instruction for IE to LE R_PPC64_TLS"); - uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30 - uint32_t DFormOp = getDFormOp(SecondaryOp); - write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF))); - relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val); + uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 + uint32_t dFormOp = getPPCDFormOp(secondaryOp); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); + relocateOne(loc + offset, R_PPC64_TPREL16_LO, val); break; } default: @@ -409,9 +529,9 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { } } -RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_PPC64_GOT16: case R_PPC64_GOT16_DS: case R_PPC64_GOT16_HA: @@ -421,16 +541,17 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, return R_GOT_OFF; case R_PPC64_TOC16: case R_PPC64_TOC16_DS: - case R_PPC64_TOC16_HA: case R_PPC64_TOC16_HI: case R_PPC64_TOC16_LO: - case R_PPC64_TOC16_LO_DS: return R_GOTREL; + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: + return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL; case R_PPC64_TOC: - return R_PPC_TOC; + return R_PPC64_TOCBASE; case R_PPC64_REL14: case R_PPC64_REL24: - return R_PPC_CALL_PLT; + return R_PPC64_CALL_PLT; case R_PPC64_REL16_LO: case R_PPC64_REL16_HA: case R_PPC64_REL32: @@ -478,7 +599,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: case R_PPC64_DTPREL64: - return R_ABS; + return R_DTPREL; case R_PPC64_TLSGD: return R_TLSDESC_CALL; case R_PPC64_TLSLD: @@ -490,115 +611,121 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, } } -void PPC64::writeGotHeader(uint8_t *Buf) const { - write64(Buf, getPPC64TocBase()); +RelType PPC64::getDynRel(RelType type) const { + if (type == R_PPC64_ADDR64 || type == R_PPC64_TOC) + return R_PPC64_ADDR64; + return R_PPC64_NONE; } -void PPC64::writePltHeader(uint8_t *Buf) const { +void PPC64::writeGotHeader(uint8_t *buf) const { + write64(buf, getPPC64TocBase()); +} + +void PPC64::writePltHeader(uint8_t *buf) const { // The generic resolver stub goes first. - write32(Buf + 0, 0x7c0802a6); // mflr r0 - write32(Buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> - write32(Buf + 8, 0x7d6802a6); // mflr r11 - write32(Buf + 12, 0x7c0803a6); // mtlr r0 - write32(Buf + 16, 0x7d8b6050); // subf r12, r11, r12 - write32(Buf + 20, 0x380cffcc); // subi r0,r12,52 - write32(Buf + 24, 0x7800f082); // srdi r0,r0,62,2 - write32(Buf + 28, 0xe98b002c); // ld r12,44(r11) - write32(Buf + 32, 0x7d6c5a14); // add r11,r12,r11 - write32(Buf + 36, 0xe98b0000); // ld r12,0(r11) - write32(Buf + 40, 0xe96b0008); // ld r11,8(r11) - write32(Buf + 44, 0x7d8903a6); // mtctr r12 - write32(Buf + 48, 0x4e800420); // bctr + write32(buf + 0, 0x7c0802a6); // mflr r0 + write32(buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> + write32(buf + 8, 0x7d6802a6); // mflr r11 + write32(buf + 12, 0x7c0803a6); // mtlr r0 + write32(buf + 16, 0x7d8b6050); // subf r12, r11, r12 + write32(buf + 20, 0x380cffcc); // subi r0,r12,52 + write32(buf + 24, 0x7800f082); // srdi r0,r0,62,2 + write32(buf + 28, 0xe98b002c); // ld r12,44(r11) + write32(buf + 32, 0x7d6c5a14); // add r11,r12,r11 + write32(buf + 36, 0xe98b0000); // ld r12,0(r11) + write32(buf + 40, 0xe96b0008); // ld r11,8(r11) + write32(buf + 44, 0x7d8903a6); // mtctr r12 + write32(buf + 48, 0x4e800420); // bctr // The 'bcl' instruction will set the link register to the address of the // following instruction ('mflr r11'). Here we store the offset from that // instruction to the first entry in the GotPlt section. - int64_t GotPltOffset = In.GotPlt->getVA() - (In.Plt->getVA() + 8); - write64(Buf + 52, GotPltOffset); + int64_t gotPltOffset = in.gotPlt->getVA() - (in.plt->getVA() + 8); + write64(buf + 52, gotPltOffset); } -void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - int32_t Offset = PltHeaderSize + Index * PltEntrySize; +void PPC64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + int32_t offset = pltHeaderSize + index * pltEntrySize; // bl __glink_PLTresolve - write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc)); + write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc)); } -static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) { +static std::pair<RelType, uint64_t> toAddr16Rel(RelType type, uint64_t val) { // Relocations relative to the toc-base need to be adjusted by the Toc offset. - uint64_t TocBiasedVal = Val - PPC64TocOffset; + uint64_t tocBiasedVal = val - ppc64TocOffset; // Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset. - uint64_t DTPBiasedVal = Val - DynamicThreadPointerOffset; + uint64_t dtpBiasedVal = val - dynamicThreadPointerOffset; - switch (Type) { + switch (type) { // TOC biased relocation. case R_PPC64_GOT16: case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSLD16: case R_PPC64_TOC16: - return {R_PPC64_ADDR16, TocBiasedVal}; + return {R_PPC64_ADDR16, tocBiasedVal}; case R_PPC64_GOT16_DS: case R_PPC64_TOC16_DS: case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_DTPREL16_DS: - return {R_PPC64_ADDR16_DS, TocBiasedVal}; + return {R_PPC64_ADDR16_DS, tocBiasedVal}; case R_PPC64_GOT16_HA: case R_PPC64_GOT_TLSGD16_HA: case R_PPC64_GOT_TLSLD16_HA: case R_PPC64_GOT_TPREL16_HA: case R_PPC64_GOT_DTPREL16_HA: case R_PPC64_TOC16_HA: - return {R_PPC64_ADDR16_HA, TocBiasedVal}; + return {R_PPC64_ADDR16_HA, tocBiasedVal}; case R_PPC64_GOT16_HI: case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TPREL16_HI: case R_PPC64_GOT_DTPREL16_HI: case R_PPC64_TOC16_HI: - return {R_PPC64_ADDR16_HI, TocBiasedVal}; + return {R_PPC64_ADDR16_HI, tocBiasedVal}; case R_PPC64_GOT16_LO: case R_PPC64_GOT_TLSGD16_LO: case R_PPC64_GOT_TLSLD16_LO: case R_PPC64_TOC16_LO: - return {R_PPC64_ADDR16_LO, TocBiasedVal}; + return {R_PPC64_ADDR16_LO, tocBiasedVal}; case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_LO_DS: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_LO_DS: - return {R_PPC64_ADDR16_LO_DS, TocBiasedVal}; + return {R_PPC64_ADDR16_LO_DS, tocBiasedVal}; // Dynamic Thread pointer biased relocation types. case R_PPC64_DTPREL16: - return {R_PPC64_ADDR16, DTPBiasedVal}; + return {R_PPC64_ADDR16, dtpBiasedVal}; case R_PPC64_DTPREL16_DS: - return {R_PPC64_ADDR16_DS, DTPBiasedVal}; + return {R_PPC64_ADDR16_DS, dtpBiasedVal}; case R_PPC64_DTPREL16_HA: - return {R_PPC64_ADDR16_HA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HA, dtpBiasedVal}; case R_PPC64_DTPREL16_HI: - return {R_PPC64_ADDR16_HI, DTPBiasedVal}; + return {R_PPC64_ADDR16_HI, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHER: - return {R_PPC64_ADDR16_HIGHER, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHER, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHERA: - return {R_PPC64_ADDR16_HIGHERA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHERA, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHEST: - return {R_PPC64_ADDR16_HIGHEST, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHEST, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHESTA: - return {R_PPC64_ADDR16_HIGHESTA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHESTA, dtpBiasedVal}; case R_PPC64_DTPREL16_LO: - return {R_PPC64_ADDR16_LO, DTPBiasedVal}; + return {R_PPC64_ADDR16_LO, dtpBiasedVal}; case R_PPC64_DTPREL16_LO_DS: - return {R_PPC64_ADDR16_LO_DS, DTPBiasedVal}; + return {R_PPC64_ADDR16_LO_DS, dtpBiasedVal}; case R_PPC64_DTPREL64: - return {R_PPC64_ADDR64, DTPBiasedVal}; + return {R_PPC64_ADDR64, dtpBiasedVal}; default: - return {Type, Val}; + return {type, val}; } } -static bool isTocOptType(RelType Type) { - switch (Type) { +static bool isTocOptType(RelType type) { + switch (type) { case R_PPC64_GOT16_HA: case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_HA: @@ -610,66 +737,69 @@ static bool isTocOptType(RelType Type) { } } -void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { // We need to save the original relocation type to use in diagnostics, and // use the original type to determine if we should toc-optimize the // instructions being relocated. - RelType OriginalType = Type; - bool ShouldTocOptimize = isTocOptType(Type); + RelType originalType = type; + bool shouldTocOptimize = isTocOptType(type); // For dynamic thread pointer relative, toc-relative, and got-indirect // relocations, proceed in terms of the corresponding ADDR16 relocation type. - std::tie(Type, Val) = toAddr16Rel(Type, Val); + std::tie(type, val) = toAddr16Rel(type, val); - switch (Type) { + switch (type) { case R_PPC64_ADDR14: { - checkAlignment(Loc, Val, 4, Type); + checkAlignment(loc, val, 4, type); // Preserve the AA/LK bits in the branch instruction - uint8_t AALK = Loc[3]; - write16(Loc + 2, (AALK & 3) | (Val & 0xfffc)); + uint8_t aalk = loc[3]; + write16(loc + 2, (aalk & 3) | (val & 0xfffc)); break; } case R_PPC64_ADDR16: - case R_PPC64_TPREL16: - checkInt(Loc, Val, 16, OriginalType); - write16(Loc, Val); + checkIntUInt(loc, val, 16, originalType); + write16(loc, val); + break; + case R_PPC64_ADDR32: + checkIntUInt(loc, val, 32, originalType); + write32(loc, val); break; case R_PPC64_ADDR16_DS: case R_PPC64_TPREL16_DS: { - checkInt(Loc, Val, 16, OriginalType); + checkInt(loc, val, 16, originalType); // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. - uint16_t Mask = isDQFormInstruction(readInstrFromHalf16(Loc)) ? 0xF : 0x3; - checkAlignment(Loc, lo(Val), Mask + 1, OriginalType); - write16(Loc, (read16(Loc) & Mask) | lo(Val)); + uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3; + checkAlignment(loc, lo(val), mask + 1, originalType); + write16(loc, (read16(loc) & mask) | lo(val)); } break; case R_PPC64_ADDR16_HA: case R_PPC64_REL16_HA: case R_PPC64_TPREL16_HA: - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) - writeInstrFromHalf16(Loc, 0x60000000); + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) + writeFromHalf16(loc, 0x60000000); else - write16(Loc, ha(Val)); + write16(loc, ha(val)); break; case R_PPC64_ADDR16_HI: case R_PPC64_REL16_HI: case R_PPC64_TPREL16_HI: - write16(Loc, hi(Val)); + write16(loc, hi(val)); break; case R_PPC64_ADDR16_HIGHER: case R_PPC64_TPREL16_HIGHER: - write16(Loc, higher(Val)); + write16(loc, higher(val)); break; case R_PPC64_ADDR16_HIGHERA: case R_PPC64_TPREL16_HIGHERA: - write16(Loc, highera(Val)); + write16(loc, highera(val)); break; case R_PPC64_ADDR16_HIGHEST: case R_PPC64_TPREL16_HIGHEST: - write16(Loc, highest(Val)); + write16(loc, highest(val)); break; case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_TPREL16_HIGHESTA: - write16(Loc, highesta(Val)); + write16(loc, highesta(val)); break; case R_PPC64_ADDR16_LO: case R_PPC64_REL16_LO: @@ -677,104 +807,119 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { // When the high-adjusted part of a toc relocation evalutes to 0, it is // changed into a nop. The lo part then needs to be updated to use the // toc-pointer register r2, as the base register. - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) { - uint32_t Instr = readInstrFromHalf16(Loc); - if (isInstructionUpdateForm(Instr)) - error(getErrorLocation(Loc) + + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { + uint32_t insn = readFromHalf16(loc); + if (isInstructionUpdateForm(insn)) + error(getErrorLocation(loc) + "can't toc-optimize an update instruction: 0x" + - utohexstr(Instr)); - Instr = (Instr & 0xFFE00000) | 0x00020000; - writeInstrFromHalf16(Loc, Instr); + utohexstr(insn)); + writeFromHalf16(loc, (insn & 0xffe00000) | 0x00020000 | lo(val)); + } else { + write16(loc, lo(val)); } - write16(Loc, lo(Val)); break; case R_PPC64_ADDR16_LO_DS: case R_PPC64_TPREL16_LO_DS: { // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. - uint32_t Inst = readInstrFromHalf16(Loc); - uint16_t Mask = isDQFormInstruction(Inst) ? 0xF : 0x3; - checkAlignment(Loc, lo(Val), Mask + 1, OriginalType); - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) { + uint32_t insn = readFromHalf16(loc); + uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3; + checkAlignment(loc, lo(val), mask + 1, originalType); + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { // When the high-adjusted part of a toc relocation evalutes to 0, it is // changed into a nop. The lo part then needs to be updated to use the toc // pointer register r2, as the base register. - if (isInstructionUpdateForm(Inst)) - error(getErrorLocation(Loc) + + if (isInstructionUpdateForm(insn)) + error(getErrorLocation(loc) + "Can't toc-optimize an update instruction: 0x" + - Twine::utohexstr(Inst)); - Inst = (Inst & 0xFFE0000F) | 0x00020000; - writeInstrFromHalf16(Loc, Inst); + Twine::utohexstr(insn)); + insn &= 0xffe00000 | mask; + writeFromHalf16(loc, insn | 0x00020000 | lo(val)); + } else { + write16(loc, (read16(loc) & mask) | lo(val)); } - write16(Loc, (read16(Loc) & Mask) | lo(Val)); } break; - case R_PPC64_ADDR32: + case R_PPC64_TPREL16: + checkInt(loc, val, 16, originalType); + write16(loc, val); + break; case R_PPC64_REL32: - checkInt(Loc, Val, 32, Type); - write32(Loc, Val); + checkInt(loc, val, 32, type); + write32(loc, val); break; case R_PPC64_ADDR64: case R_PPC64_REL64: case R_PPC64_TOC: - write64(Loc, Val); + write64(loc, val); break; case R_PPC64_REL14: { - uint32_t Mask = 0x0000FFFC; - checkInt(Loc, Val, 16, Type); - checkAlignment(Loc, Val, 4, Type); - write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); + uint32_t mask = 0x0000FFFC; + checkInt(loc, val, 16, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_REL24: { - uint32_t Mask = 0x03FFFFFC; - checkInt(Loc, Val, 26, Type); - checkAlignment(Loc, Val, 4, Type); - write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); + uint32_t mask = 0x03FFFFFC; + checkInt(loc, val, 26, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_DTPREL64: - write64(Loc, Val - DynamicThreadPointerOffset); + write64(loc, val - dynamicThreadPointerOffset); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { - if (Type != R_PPC64_REL14 && Type != R_PPC64_REL24) +bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + if (type != R_PPC64_REL14 && type != R_PPC64_REL24) return false; // If a function is in the Plt it needs to be called with a call-stub. - if (S.isInPlt()) + if (s.isInPlt()) return true; // If a symbol is a weak undefined and we are compiling an executable // it doesn't need a range-extending thunk since it can't be called. - if (S.isUndefWeak() && !Config->Shared) + if (s.isUndefWeak() && !config->shared) return false; // If the offset exceeds the range of the branch type then it will need // a range-extending thunk. - return !inBranchRange(Type, BranchAddr, S.getVA()); + // See the comment in getRelocTargetVA() about R_PPC64_CALL. + return !inBranchRange(type, branchAddr, + s.getVA() + + getPPC64GlobalEntryToLocalEntryOffset(s.stOther)); } -bool PPC64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - int64_t Offset = Dst - Src; - if (Type == R_PPC64_REL14) - return isInt<16>(Offset); - if (Type == R_PPC64_REL24) - return isInt<26>(Offset); +uint32_t PPC64::getThunkSectionSpacing() const { + // See comment in Arch/ARM.cpp for a more detailed explanation of + // getThunkSectionSpacing(). For PPC64 we pick the constant here based on + // R_PPC64_REL24, which is used by unconditional branch instructions. + // 0x2000000 = (1 << 24-1) * 4 + return 0x2000000; +} + +bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + int64_t offset = dst - src; + if (type == R_PPC64_REL14) + return isInt<16>(offset); + if (type == R_PPC64_REL24) + return isInt<26>(offset); llvm_unreachable("unsupported relocation type used in branch"); } -RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const { - if (Expr == R_RELAX_TLS_GD_TO_IE) +RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) return R_RELAX_TLS_GD_TO_IE_GOT_OFF; - if (Expr == R_RELAX_TLS_LD_TO_LE) + if (expr == R_RELAX_TLS_LD_TO_LE) return R_RELAX_TLS_LD_TO_LE_ABS; - return Expr; + return expr; } // Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement. @@ -794,24 +939,25 @@ RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data, // thread pointer. // Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is // used as the relaxation hint for both steps 2 and 3. -void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_PPC64_GOT_TLSGD16_HA: // This is relaxed from addis rT, r2, sym@got@tlsgd@ha to // addis rT, r2, sym@got@tprel@ha. - relocateOne(Loc, R_PPC64_GOT_TPREL16_HA, Val); + relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val); return; + case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: { // Relax from addi r3, rA, sym@got@tlsgd@l to // ld r3, sym@got@tprel@l(rA) - uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16)); - writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister); - relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val); + uint32_t ra = (readFromHalf16(loc) & (0x1f << 16)); + writeFromHalf16(loc, 0xe8600000 | ra); + relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val); return; } case R_PPC64_TLSGD: - write32(Loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop - write32(Loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + write32(loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop + write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 return; default: llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); @@ -846,86 +992,86 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { // pair by split-stack-size-adjust. // addis r12, r1, ha(-stack-frame size - split-stack-adjust-size) // addi r12, r12, l(-stack-frame size - split-stack-adjust-size) -bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const { +bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const { // If the caller has a global entry point adjust the buffer past it. The start // of the split-stack prologue will be at the local entry point. - Loc += getPPC64GlobalEntryToLocalEntryOffset(StOther); + loc += getPPC64GlobalEntryToLocalEntryOffset(stOther); // At the very least we expect to see a load of some split-stack data from the // tcb, and 2 instructions that calculate the ending stack address this // function will require. If there is not enough room for at least 3 // instructions it can't be a split-stack prologue. - if (Loc + 12 >= End) + if (loc + 12 >= end) return false; // First instruction must be `ld r0, -0x7000-64(r13)` - if (read32(Loc) != 0xe80d8fc0) + if (read32(loc) != 0xe80d8fc0) return false; - int16_t HiImm = 0; - int16_t LoImm = 0; + int16_t hiImm = 0; + int16_t loImm = 0; // First instruction can be either an addis if the frame size is larger then // 32K, or an addi if the size is less then 32K. - int32_t FirstInstr = read32(Loc + 4); - if (getPrimaryOpCode(FirstInstr) == 15) { - HiImm = FirstInstr & 0xFFFF; - } else if (getPrimaryOpCode(FirstInstr) == 14) { - LoImm = FirstInstr & 0xFFFF; + int32_t firstInstr = read32(loc + 4); + if (getPrimaryOpCode(firstInstr) == 15) { + hiImm = firstInstr & 0xFFFF; + } else if (getPrimaryOpCode(firstInstr) == 14) { + loImm = firstInstr & 0xFFFF; } else { return false; } // Second instruction is either an addi or a nop. If the first instruction was // an addi then LoImm is set and the second instruction must be a nop. - uint32_t SecondInstr = read32(Loc + 8); - if (!LoImm && getPrimaryOpCode(SecondInstr) == 14) { - LoImm = SecondInstr & 0xFFFF; - } else if (SecondInstr != 0x60000000) { + uint32_t secondInstr = read32(loc + 8); + if (!loImm && getPrimaryOpCode(secondInstr) == 14) { + loImm = secondInstr & 0xFFFF; + } else if (secondInstr != 0x60000000) { return false; } // The register operands of the first instruction should be the stack-pointer // (r1) as the input (RA) and r12 as the output (RT). If the second // instruction is not a nop, then it should use r12 as both input and output. - auto CheckRegOperands = [](uint32_t Instr, uint8_t ExpectedRT, - uint8_t ExpectedRA) { - return ((Instr & 0x3E00000) >> 21 == ExpectedRT) && - ((Instr & 0x1F0000) >> 16 == ExpectedRA); + auto checkRegOperands = [](uint32_t instr, uint8_t expectedRT, + uint8_t expectedRA) { + return ((instr & 0x3E00000) >> 21 == expectedRT) && + ((instr & 0x1F0000) >> 16 == expectedRA); }; - if (!CheckRegOperands(FirstInstr, 12, 1)) + if (!checkRegOperands(firstInstr, 12, 1)) return false; - if (SecondInstr != 0x60000000 && !CheckRegOperands(SecondInstr, 12, 12)) + if (secondInstr != 0x60000000 && !checkRegOperands(secondInstr, 12, 12)) return false; - int32_t StackFrameSize = (HiImm * 65536) + LoImm; + int32_t stackFrameSize = (hiImm * 65536) + loImm; // Check that the adjusted size doesn't overflow what we can represent with 2 // instructions. - if (StackFrameSize < Config->SplitStackAdjustSize + INT32_MIN) { - error(getErrorLocation(Loc) + "split-stack prologue adjustment overflows"); + if (stackFrameSize < config->splitStackAdjustSize + INT32_MIN) { + error(getErrorLocation(loc) + "split-stack prologue adjustment overflows"); return false; } - int32_t AdjustedStackFrameSize = - StackFrameSize - Config->SplitStackAdjustSize; + int32_t adjustedStackFrameSize = + stackFrameSize - config->splitStackAdjustSize; - LoImm = AdjustedStackFrameSize & 0xFFFF; - HiImm = (AdjustedStackFrameSize + 0x8000) >> 16; - if (HiImm) { - write32(Loc + 4, 0x3D810000 | (uint16_t)HiImm); + loImm = adjustedStackFrameSize & 0xFFFF; + hiImm = (adjustedStackFrameSize + 0x8000) >> 16; + if (hiImm) { + write32(loc + 4, 0x3D810000 | (uint16_t)hiImm); // If the low immediate is zero the second instruction will be a nop. - SecondInstr = LoImm ? 0x398C0000 | (uint16_t)LoImm : 0x60000000; - write32(Loc + 8, SecondInstr); + secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : 0x60000000; + write32(loc + 8, secondInstr); } else { // addi r12, r1, imm - write32(Loc + 4, (0x39810000) | (uint16_t)LoImm); - write32(Loc + 8, 0x60000000); + write32(loc + 4, (0x39810000) | (uint16_t)loImm); + write32(loc + 8, 0x60000000); } return true; } TargetInfo *elf::getPPC64TargetInfo() { - static PPC64 Target; - return &Target; + static PPC64 target; + return ⌖ } diff --git a/ELF/Arch/RISCV.cpp b/ELF/Arch/RISCV.cpp index 461e8d35c3e6..6f16ade57177 100644 --- a/ELF/Arch/RISCV.cpp +++ b/ELF/Arch/RISCV.cpp @@ -1,13 +1,13 @@ //===- RISCV.cpp ----------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "SyntheticSections.h" #include "Target.h" using namespace llvm; @@ -23,59 +23,207 @@ class RISCV final : public TargetInfo { public: RISCV(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void writeGotHeader(uint8_t *buf) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + RelType getDynRel(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // end anonymous namespace -RISCV::RISCV() { NoneRel = R_RISCV_NONE; } +const uint64_t dtpOffset = 0x800; + +enum Op { + ADDI = 0x13, + AUIPC = 0x17, + JALR = 0x67, + LD = 0x3003, + LW = 0x2003, + SRLI = 0x5013, + SUB = 0x40000033, +}; + +enum Reg { + X_RA = 1, + X_T0 = 5, + X_T1 = 6, + X_T2 = 7, + X_T3 = 28, +}; + +static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } +static uint32_t lo12(uint32_t val) { return val & 4095; } + +static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) { + return op | (rd << 7) | (rs1 << 15) | (imm << 20); +} +static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) { + return op | (rd << 7) | (rs1 << 15) | (rs2 << 20); +} +static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) { + return op | (rd << 7) | (imm << 12); +} + +RISCV::RISCV() { + copyRel = R_RISCV_COPY; + noneRel = R_RISCV_NONE; + pltRel = R_RISCV_JUMP_SLOT; + relativeRel = R_RISCV_RELATIVE; + if (config->is64) { + symbolicRel = R_RISCV_64; + tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64; + tlsOffsetRel = R_RISCV_TLS_DTPREL64; + tlsGotRel = R_RISCV_TLS_TPREL64; + } else { + symbolicRel = R_RISCV_32; + tlsModuleIndexRel = R_RISCV_TLS_DTPMOD32; + tlsOffsetRel = R_RISCV_TLS_DTPREL32; + tlsGotRel = R_RISCV_TLS_TPREL32; + } + gotRel = symbolicRel; + + // .got[0] = _DYNAMIC + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + + // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map + gotPltHeaderEntriesNum = 2; -static uint32_t getEFlags(InputFile *F) { - if (Config->Is64) - return cast<ObjFile<ELF64LE>>(F)->getObj().getHeader()->e_flags; - return cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags; + pltEntrySize = 16; + pltHeaderSize = 32; +} + +static uint32_t getEFlags(InputFile *f) { + if (config->is64) + return cast<ObjFile<ELF64LE>>(f)->getObj().getHeader()->e_flags; + return cast<ObjFile<ELF32LE>>(f)->getObj().getHeader()->e_flags; } uint32_t RISCV::calcEFlags() const { - assert(!ObjectFiles.empty()); + assert(!objectFiles.empty()); - uint32_t Target = getEFlags(ObjectFiles.front()); + uint32_t target = getEFlags(objectFiles.front()); - for (InputFile *F : ObjectFiles) { - uint32_t EFlags = getEFlags(F); - if (EFlags & EF_RISCV_RVC) - Target |= EF_RISCV_RVC; + for (InputFile *f : objectFiles) { + uint32_t eflags = getEFlags(f); + if (eflags & EF_RISCV_RVC) + target |= EF_RISCV_RVC; - if ((EFlags & EF_RISCV_FLOAT_ABI) != (Target & EF_RISCV_FLOAT_ABI)) - error(toString(F) + + if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI)) + error(toString(f) + ": cannot link object files with different floating-point ABI"); - if ((EFlags & EF_RISCV_RVE) != (Target & EF_RISCV_RVE)) - error(toString(F) + + if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE)) + error(toString(f) + ": cannot link object files with different EF_RISCV_RVE"); } - return Target; + return target; +} + +void RISCV::writeGotHeader(uint8_t *buf) const { + if (config->is64) + write64le(buf, mainPart->dynamic->getVA()); + else + write32le(buf, mainPart->dynamic->getVA()); +} + +void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const { + if (config->is64) + write64le(buf, in.plt->getVA()); + else + write32le(buf, in.plt->getVA()); +} + +void RISCV::writePltHeader(uint8_t *buf) const { + // 1: auipc t2, %pcrel_hi(.got.plt) + // sub t1, t1, t3 + // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve + // addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0] + // addi t0, t2, %pcrel_lo(1b) + // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0] + // l[wd] t0, Wordsize(t0); t0 = link_map + // jr t3 + uint32_t offset = in.gotPlt->getVA() - in.plt->getVA(); + uint32_t load = config->is64 ? LD : LW; + write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset))); + write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3)); + write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset))); + write32le(buf + 12, itype(ADDI, X_T1, X_T1, -target->pltHeaderSize - 12)); + write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset))); + write32le(buf + 20, itype(SRLI, X_T1, X_T1, config->is64 ? 1 : 2)); + write32le(buf + 24, itype(load, X_T0, X_T0, config->wordsize)); + write32le(buf + 28, itype(JALR, 0, X_T3, 0)); } -RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +void RISCV::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // 1: auipc t3, %pcrel_hi(f@.got.plt) + // l[wd] t3, %pcrel_lo(1b)(t3) + // jalr t1, t3 + // nop + uint32_t offset = gotPltEntryAddr - pltEntryAddr; + write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset))); + write32le(buf + 4, itype(config->is64 ? LD : LW, X_T3, X_T3, lo12(offset))); + write32le(buf + 8, itype(JALR, X_T1, X_T3, 0)); + write32le(buf + 12, itype(ADDI, 0, 0, 0)); +} + +RelType RISCV::getDynRel(RelType type) const { + return type == target->symbolicRel ? type + : static_cast<RelType>(R_RISCV_NONE); +} + +RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_RISCV_ADD8: + case R_RISCV_ADD16: + case R_RISCV_ADD32: + case R_RISCV_ADD64: + case R_RISCV_SET6: + case R_RISCV_SET8: + case R_RISCV_SET16: + case R_RISCV_SET32: + case R_RISCV_SUB6: + case R_RISCV_SUB8: + case R_RISCV_SUB16: + case R_RISCV_SUB32: + case R_RISCV_SUB64: + return R_RISCV_ADD; case R_RISCV_JAL: case R_RISCV_BRANCH: - case R_RISCV_CALL: case R_RISCV_PCREL_HI20: case R_RISCV_RVC_BRANCH: case R_RISCV_RVC_JUMP: case R_RISCV_32_PCREL: return R_PC; + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + return R_PLT_PC; + case R_RISCV_GOT_HI20: + return R_GOT_PC; case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: return R_RISCV_PC_INDIRECT; + case R_RISCV_TLS_GD_HI20: + return R_TLSGD_PC; + case R_RISCV_TLS_GOT_HI20: + config->hasStaticTlsModel = true; + return R_GOT_PC; + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_LO12_I: + case R_RISCV_TPREL_LO12_S: + return R_TLS; case R_RISCV_RELAX: case R_RISCV_ALIGN: + case R_RISCV_TPREL_ADD: return R_HINT; default: return R_ABS; @@ -83,175 +231,190 @@ RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S, } // Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63. -static uint32_t extractBits(uint64_t V, uint32_t Begin, uint32_t End) { - return (V & ((1ULL << (Begin + 1)) - 1)) >> End; +static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { + return (v & ((1ULL << (begin + 1)) - 1)) >> end; } -void RISCV::relocateOne(uint8_t *Loc, const RelType Type, - const uint64_t Val) const { - switch (Type) { +void RISCV::relocateOne(uint8_t *loc, const RelType type, + const uint64_t val) const { + const unsigned bits = config->wordsize * 8; + + switch (type) { case R_RISCV_32: - write32le(Loc, Val); + write32le(loc, val); return; case R_RISCV_64: - write64le(Loc, Val); + write64le(loc, val); return; case R_RISCV_RVC_BRANCH: { - checkInt(Loc, static_cast<int64_t>(Val) >> 1, 8, Type); - checkAlignment(Loc, Val, 2, Type); - uint16_t Insn = read16le(Loc) & 0xE383; - uint16_t Imm8 = extractBits(Val, 8, 8) << 12; - uint16_t Imm4_3 = extractBits(Val, 4, 3) << 10; - uint16_t Imm7_6 = extractBits(Val, 7, 6) << 5; - uint16_t Imm2_1 = extractBits(Val, 2, 1) << 3; - uint16_t Imm5 = extractBits(Val, 5, 5) << 2; - Insn |= Imm8 | Imm4_3 | Imm7_6 | Imm2_1 | Imm5; - - write16le(Loc, Insn); + checkInt(loc, static_cast<int64_t>(val) >> 1, 8, type); + checkAlignment(loc, val, 2, type); + uint16_t insn = read16le(loc) & 0xE383; + uint16_t imm8 = extractBits(val, 8, 8) << 12; + uint16_t imm4_3 = extractBits(val, 4, 3) << 10; + uint16_t imm7_6 = extractBits(val, 7, 6) << 5; + uint16_t imm2_1 = extractBits(val, 2, 1) << 3; + uint16_t imm5 = extractBits(val, 5, 5) << 2; + insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5; + + write16le(loc, insn); return; } case R_RISCV_RVC_JUMP: { - checkInt(Loc, static_cast<int64_t>(Val) >> 1, 11, Type); - checkAlignment(Loc, Val, 2, Type); - uint16_t Insn = read16le(Loc) & 0xE003; - uint16_t Imm11 = extractBits(Val, 11, 11) << 12; - uint16_t Imm4 = extractBits(Val, 4, 4) << 11; - uint16_t Imm9_8 = extractBits(Val, 9, 8) << 9; - uint16_t Imm10 = extractBits(Val, 10, 10) << 8; - uint16_t Imm6 = extractBits(Val, 6, 6) << 7; - uint16_t Imm7 = extractBits(Val, 7, 7) << 6; - uint16_t Imm3_1 = extractBits(Val, 3, 1) << 3; - uint16_t Imm5 = extractBits(Val, 5, 5) << 2; - Insn |= Imm11 | Imm4 | Imm9_8 | Imm10 | Imm6 | Imm7 | Imm3_1 | Imm5; - - write16le(Loc, Insn); + checkInt(loc, static_cast<int64_t>(val) >> 1, 11, type); + checkAlignment(loc, val, 2, type); + uint16_t insn = read16le(loc) & 0xE003; + uint16_t imm11 = extractBits(val, 11, 11) << 12; + uint16_t imm4 = extractBits(val, 4, 4) << 11; + uint16_t imm9_8 = extractBits(val, 9, 8) << 9; + uint16_t imm10 = extractBits(val, 10, 10) << 8; + uint16_t imm6 = extractBits(val, 6, 6) << 7; + uint16_t imm7 = extractBits(val, 7, 7) << 6; + uint16_t imm3_1 = extractBits(val, 3, 1) << 3; + uint16_t imm5 = extractBits(val, 5, 5) << 2; + insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5; + + write16le(loc, insn); return; } case R_RISCV_RVC_LUI: { - int32_t Imm = ((Val + 0x800) >> 12); - checkUInt(Loc, Imm, 6, Type); - if (Imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` - write16le(Loc, (read16le(Loc) & 0x0F83) | 0x4000); + int64_t imm = SignExtend64(val + 0x800, bits) >> 12; + checkInt(loc, imm, 6, type); + if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` + write16le(loc, (read16le(loc) & 0x0F83) | 0x4000); } else { - uint16_t Imm17 = extractBits(Val + 0x800, 17, 17) << 12; - uint16_t Imm16_12 = extractBits(Val + 0x800, 16, 12) << 2; - write16le(Loc, (read16le(Loc) & 0xEF83) | Imm17 | Imm16_12); + uint16_t imm17 = extractBits(val + 0x800, 17, 17) << 12; + uint16_t imm16_12 = extractBits(val + 0x800, 16, 12) << 2; + write16le(loc, (read16le(loc) & 0xEF83) | imm17 | imm16_12); } return; } case R_RISCV_JAL: { - checkInt(Loc, static_cast<int64_t>(Val) >> 1, 20, Type); - checkAlignment(Loc, Val, 2, Type); + checkInt(loc, static_cast<int64_t>(val) >> 1, 20, type); + checkAlignment(loc, val, 2, type); - uint32_t Insn = read32le(Loc) & 0xFFF; - uint32_t Imm20 = extractBits(Val, 20, 20) << 31; - uint32_t Imm10_1 = extractBits(Val, 10, 1) << 21; - uint32_t Imm11 = extractBits(Val, 11, 11) << 20; - uint32_t Imm19_12 = extractBits(Val, 19, 12) << 12; - Insn |= Imm20 | Imm10_1 | Imm11 | Imm19_12; + uint32_t insn = read32le(loc) & 0xFFF; + uint32_t imm20 = extractBits(val, 20, 20) << 31; + uint32_t imm10_1 = extractBits(val, 10, 1) << 21; + uint32_t imm11 = extractBits(val, 11, 11) << 20; + uint32_t imm19_12 = extractBits(val, 19, 12) << 12; + insn |= imm20 | imm10_1 | imm11 | imm19_12; - write32le(Loc, Insn); + write32le(loc, insn); return; } case R_RISCV_BRANCH: { - checkInt(Loc, static_cast<int64_t>(Val) >> 1, 12, Type); - checkAlignment(Loc, Val, 2, Type); + checkInt(loc, static_cast<int64_t>(val) >> 1, 12, type); + checkAlignment(loc, val, 2, type); - uint32_t Insn = read32le(Loc) & 0x1FFF07F; - uint32_t Imm12 = extractBits(Val, 12, 12) << 31; - uint32_t Imm10_5 = extractBits(Val, 10, 5) << 25; - uint32_t Imm4_1 = extractBits(Val, 4, 1) << 8; - uint32_t Imm11 = extractBits(Val, 11, 11) << 7; - Insn |= Imm12 | Imm10_5 | Imm4_1 | Imm11; + uint32_t insn = read32le(loc) & 0x1FFF07F; + uint32_t imm12 = extractBits(val, 12, 12) << 31; + uint32_t imm10_5 = extractBits(val, 10, 5) << 25; + uint32_t imm4_1 = extractBits(val, 4, 1) << 8; + uint32_t imm11 = extractBits(val, 11, 11) << 7; + insn |= imm12 | imm10_5 | imm4_1 | imm11; - write32le(Loc, Insn); + write32le(loc, insn); return; } // auipc + jalr pair - case R_RISCV_CALL: { - checkInt(Loc, Val, 32, Type); - if (isInt<32>(Val)) { - relocateOne(Loc, R_RISCV_PCREL_HI20, Val); - relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val); + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: { + int64_t hi = SignExtend64(val + 0x800, bits) >> 12; + checkInt(loc, hi, 20, type); + if (isInt<20>(hi)) { + relocateOne(loc, R_RISCV_PCREL_HI20, val); + relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val); } return; } + case R_RISCV_GOT_HI20: case R_RISCV_PCREL_HI20: + case R_RISCV_TLS_GD_HI20: + case R_RISCV_TLS_GOT_HI20: + case R_RISCV_TPREL_HI20: case R_RISCV_HI20: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - write32le(Loc, (read32le(Loc) & 0xFFF) | (Hi & 0xFFFFF000)); + uint64_t hi = val + 0x800; + checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type); + write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000)); return; } case R_RISCV_PCREL_LO12_I: + case R_RISCV_TPREL_LO12_I: case R_RISCV_LO12_I: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - uint32_t Lo = Val - (Hi & 0xFFFFF000); - write32le(Loc, (read32le(Loc) & 0xFFFFF) | ((Lo & 0xFFF) << 20)); + uint64_t hi = (val + 0x800) >> 12; + uint64_t lo = val - (hi << 12); + write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20)); return; } case R_RISCV_PCREL_LO12_S: + case R_RISCV_TPREL_LO12_S: case R_RISCV_LO12_S: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - uint32_t Lo = Val - (Hi & 0xFFFFF000); - uint32_t Imm11_5 = extractBits(Lo, 11, 5) << 25; - uint32_t Imm4_0 = extractBits(Lo, 4, 0) << 7; - write32le(Loc, (read32le(Loc) & 0x1FFF07F) | Imm11_5 | Imm4_0); + uint64_t hi = (val + 0x800) >> 12; + uint64_t lo = val - (hi << 12); + uint32_t imm11_5 = extractBits(lo, 11, 5) << 25; + uint32_t imm4_0 = extractBits(lo, 4, 0) << 7; + write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0); return; } case R_RISCV_ADD8: - *Loc += Val; + *loc += val; return; case R_RISCV_ADD16: - write16le(Loc, read16le(Loc) + Val); + write16le(loc, read16le(loc) + val); return; case R_RISCV_ADD32: - write32le(Loc, read32le(Loc) + Val); + write32le(loc, read32le(loc) + val); return; case R_RISCV_ADD64: - write64le(Loc, read64le(Loc) + Val); + write64le(loc, read64le(loc) + val); return; case R_RISCV_SUB6: - *Loc = (*Loc & 0xc0) | (((*Loc & 0x3f) - Val) & 0x3f); + *loc = (*loc & 0xc0) | (((*loc & 0x3f) - val) & 0x3f); return; case R_RISCV_SUB8: - *Loc -= Val; + *loc -= val; return; case R_RISCV_SUB16: - write16le(Loc, read16le(Loc) - Val); + write16le(loc, read16le(loc) - val); return; case R_RISCV_SUB32: - write32le(Loc, read32le(Loc) - Val); + write32le(loc, read32le(loc) - val); return; case R_RISCV_SUB64: - write64le(Loc, read64le(Loc) - Val); + write64le(loc, read64le(loc) - val); return; case R_RISCV_SET6: - *Loc = (*Loc & 0xc0) | (Val & 0x3f); + *loc = (*loc & 0xc0) | (val & 0x3f); return; case R_RISCV_SET8: - *Loc = Val; + *loc = val; return; case R_RISCV_SET16: - write16le(Loc, Val); + write16le(loc, val); return; case R_RISCV_SET32: case R_RISCV_32_PCREL: - write32le(Loc, Val); + write32le(loc, val); return; + case R_RISCV_TLS_DTPREL32: + write32le(loc, val - dtpOffset); + break; + case R_RISCV_TLS_DTPREL64: + write64le(loc, val - dtpOffset); + break; + case R_RISCV_ALIGN: case R_RISCV_RELAX: return; // Ignored (for now) @@ -267,13 +430,13 @@ void RISCV::relocateOne(uint8_t *Loc, const RelType Type, case R_RISCV_GPREL_I: case R_RISCV_GPREL_S: default: - error(getErrorLocation(Loc) + - "unimplemented relocation: " + toString(Type)); + error(getErrorLocation(loc) + + "unimplemented relocation: " + toString(type)); return; } } TargetInfo *elf::getRISCVTargetInfo() { - static RISCV Target; - return &Target; + static RISCV target; + return ⌖ } diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp index 831aa2028e7f..5299206dd919 100644 --- a/ELF/Arch/SPARCV9.cpp +++ b/ELF/Arch/SPARCV9.cpp @@ -1,9 +1,8 @@ //===- SPARCV9.cpp --------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -24,32 +23,32 @@ namespace { class SPARCV9 final : public TargetInfo { public: SPARCV9(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace SPARCV9::SPARCV9() { - CopyRel = R_SPARC_COPY; - GotRel = R_SPARC_GLOB_DAT; - NoneRel = R_SPARC_NONE; - PltRel = R_SPARC_JMP_SLOT; - RelativeRel = R_SPARC_RELATIVE; - GotEntrySize = 8; - PltEntrySize = 32; - PltHeaderSize = 4 * PltEntrySize; + copyRel = R_SPARC_COPY; + gotRel = R_SPARC_GLOB_DAT; + noneRel = R_SPARC_NONE; + pltRel = R_SPARC_JMP_SLOT; + relativeRel = R_SPARC_RELATIVE; + symbolicRel = R_SPARC_64; + pltEntrySize = 32; + pltHeaderSize = 4 * pltEntrySize; - PageSize = 8192; - DefaultMaxPageSize = 0x100000; - DefaultImageBase = 0x100000; + defaultCommonPageSize = 8192; + defaultMaxPageSize = 0x100000; + defaultImageBase = 0x100000; } -RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_SPARC_32: case R_SPARC_UA32: case R_SPARC_64: @@ -69,64 +68,65 @@ RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S, case R_SPARC_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_SPARC_32: case R_SPARC_UA32: // V-word32 - checkUInt(Loc, Val, 32, Type); - write32be(Loc, Val); + checkUInt(loc, val, 32, type); + write32be(loc, val); break; case R_SPARC_DISP32: // V-disp32 - checkInt(Loc, Val, 32, Type); - write32be(Loc, Val); + checkInt(loc, val, 32, type); + write32be(loc, val); break; case R_SPARC_WDISP30: case R_SPARC_WPLT30: // V-disp30 - checkInt(Loc, Val, 32, Type); - write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff)); + checkInt(loc, val, 32, type); + write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff)); break; case R_SPARC_22: // V-imm22 - checkUInt(Loc, Val, 22, Type); - write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff)); + checkUInt(loc, val, 22, type); + write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); break; case R_SPARC_GOT22: case R_SPARC_PC22: // T-imm22 - write32be(Loc, (read32be(Loc) & ~0x003fffff) | ((Val >> 10) & 0x003fffff)); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); break; case R_SPARC_WDISP19: // V-disp19 - checkInt(Loc, Val, 21, Type); - write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff)); + checkInt(loc, val, 21, type); + write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); break; case R_SPARC_GOT10: case R_SPARC_PC10: // T-simm10 - write32be(Loc, (read32be(Loc) & ~0x000003ff) | (Val & 0x000003ff)); + write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff)); break; case R_SPARC_64: case R_SPARC_UA64: - case R_SPARC_GLOB_DAT: // V-xword64 - write64be(Loc, Val); + write64be(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t PltData[] = { +void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t pltData[] = { 0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1 0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1 0x01, 0x00, 0x00, 0x00, // nop @@ -136,14 +136,14 @@ void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00 // nop }; - memcpy(Buf, PltData, sizeof(PltData)); + memcpy(buf, pltData, sizeof(pltData)); - uint64_t Off = getPltEntryOffset(Index); - relocateOne(Buf, R_SPARC_22, Off); - relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize)); + uint64_t off = pltHeaderSize + pltEntrySize * index; + relocateOne(buf, R_SPARC_22, off); + relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); } TargetInfo *elf::getSPARCV9TargetInfo() { - static SPARCV9 Target; - return &Target; + static SPARCV9 target; + return ⌖ } diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp index e910375d2fc7..e1dd231e8e8d 100644 --- a/ELF/Arch/X86.cpp +++ b/ELF/Arch/X86.cpp @@ -1,9 +1,8 @@ //===- X86.cpp ------------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -24,63 +23,73 @@ namespace { class X86 : public TargetInfo { public: X86(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; - void writeGotPltHeader(uint8_t *Buf) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + int getTlsGdRelaxSkip(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void writeGotPltHeader(uint8_t *buf) const override; + RelType getDynRel(RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace X86::X86() { - CopyRel = R_386_COPY; - GotRel = R_386_GLOB_DAT; - NoneRel = R_386_NONE; - PltRel = R_386_JUMP_SLOT; - IRelativeRel = R_386_IRELATIVE; - RelativeRel = R_386_RELATIVE; - TlsGotRel = R_386_TLS_TPOFF; - TlsModuleIndexRel = R_386_TLS_DTPMOD32; - TlsOffsetRel = R_386_TLS_DTPOFF32; - GotEntrySize = 4; - GotPltEntrySize = 4; - PltEntrySize = 16; - PltHeaderSize = 16; - TlsGdRelaxSkip = 2; - TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 + copyRel = R_386_COPY; + gotRel = R_386_GLOB_DAT; + noneRel = R_386_NONE; + pltRel = R_386_JUMP_SLOT; + iRelativeRel = R_386_IRELATIVE; + relativeRel = R_386_RELATIVE; + symbolicRel = R_386_32; + tlsGotRel = R_386_TLS_TPOFF; + tlsModuleIndexRel = R_386_TLS_DTPMOD32; + tlsOffsetRel = R_386_TLS_DTPOFF32; + pltEntrySize = 16; + pltHeaderSize = 16; + trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the non-PAE large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. - DefaultImageBase = 0x400000; + defaultImageBase = 0x400000; } -static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; } +int X86::getTlsGdRelaxSkip(RelType type) const { + return 2; +} -RelExpr X86::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr X86::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + // There are 4 different TLS variable models with varying degrees of + // flexibility and performance. LocalExec and InitialExec models are fast but + // less-flexible models. If they are in use, we set DF_STATIC_TLS flag in the + // dynamic section to let runtime know about that. + if (type == R_386_TLS_LE || type == R_386_TLS_LE_32 || type == R_386_TLS_IE || + type == R_386_TLS_GOTIE) + config->hasStaticTlsModel = true; + + switch (type) { case R_386_8: case R_386_16: case R_386_32: - case R_386_TLS_LDO_32: return R_ABS; + case R_386_TLS_LDO_32: + return R_DTPREL; case R_386_TLS_GD: - return R_TLSGD_GOT_FROM_END; + return R_TLSGD_GOTPLT; case R_386_TLS_LDM: - return R_TLSLD_GOT_FROM_END; + return R_TLSLD_GOTPLT; case R_386_PLT32: return R_PLT_PC; case R_386_PC8: @@ -88,7 +97,7 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, case R_386_PC32: return R_PC; case R_386_GOTPC: - return R_GOTONLY_PC_FROM_END; + return R_GOTPLTONLY_PC; case R_386_TLS_IE: return R_GOT; case R_386_GOT32: @@ -108,14 +117,14 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, // load an GOT address to a register, which is usually %ebx. // // So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or - // foo@GOT(%reg). + // foo@GOT(%ebx). // // foo@GOT is not usable in PIC. If we are creating a PIC output and if we // find such relocation, we should report an error. foo@GOT is resolved to // an *absolute* address of foo's GOT entry, because both GOT address and // foo's offset are known. In other words, it's G + A. // - // foo@GOT(%reg) needs to be resolved to a *relative* offset from a GOT to + // foo@GOT(%ebx) needs to be resolved to a *relative* offset from a GOT to // foo's GOT entry in the table, because GOT address is not known but foo's // offset in the table is known. It's G + A - GOT. // @@ -123,16 +132,16 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, // different use cases. In order to distinguish them, we have to read a // machine instruction. // - // The following code implements it. We assume that Loc[0] is the first - // byte of a displacement or an immediate field of a valid machine + // The following code implements it. We assume that Loc[0] is the first byte + // of a displacement or an immediate field of a valid machine // instruction. That means a ModRM byte is at Loc[-1]. By taking a look at - // the byte, we can determine whether the instruction is register-relative - // (i.e. it was generated for foo@GOT(%reg)) or absolute (i.e. foo@GOT). - return hasBaseReg(Loc[-1]) ? R_GOT_FROM_END : R_GOT; + // the byte, we can determine whether the instruction uses the operand as an + // absolute address (R_GOT) or a register-relative address (R_GOTPLT). + return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT; case R_386_TLS_GOTIE: - return R_GOT_FROM_END; + return R_GOTPLT; case R_386_GOTOFF: - return R_GOTREL_FROM_END; + return R_GOTPLTREL; case R_386_TLS_LE: return R_TLS; case R_386_TLS_LE_32: @@ -140,105 +149,102 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, case R_386_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const { - switch (Expr) { +RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + switch (expr) { default: - return Expr; + return expr; case R_RELAX_TLS_GD_TO_IE: - return R_RELAX_TLS_GD_TO_IE_END; + return R_RELAX_TLS_GD_TO_IE_GOTPLT; case R_RELAX_TLS_GD_TO_LE: return R_RELAX_TLS_GD_TO_LE_NEG; } } -void X86::writeGotPltHeader(uint8_t *Buf) const { - write32le(Buf, In.Dynamic->getVA()); +void X86::writeGotPltHeader(uint8_t *buf) const { + write32le(buf, mainPart->dynamic->getVA()); } -void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const { +void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const { // Entries in .got.plt initially points back to the corresponding // PLT entries with a fixed offset to skip the first instruction. - write32le(Buf, S.getPltVA() + 6); + write32le(buf, s.getPltVA() + 6); } -void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const { +void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const { // An x86 entry is the address of the ifunc resolver function. - write32le(Buf, S.getVA()); + write32le(buf, s.getVA()); } -RelType X86::getDynRel(RelType Type) const { - if (Type == R_386_TLS_LE) +RelType X86::getDynRel(RelType type) const { + if (type == R_386_TLS_LE) return R_386_TLS_TPOFF; - if (Type == R_386_TLS_LE_32) + if (type == R_386_TLS_LE_32) return R_386_TLS_TPOFF32; - return Type; + return type; } -void X86::writePltHeader(uint8_t *Buf) const { - if (Config->Pic) { - const uint8_t V[] = { - 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx) - 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx) +void X86::writePltHeader(uint8_t *buf) const { + if (config->isPic) { + const uint8_t v[] = { + 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx) + 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx) 0x90, 0x90, 0x90, 0x90 // nop }; - memcpy(Buf, V, sizeof(V)); - - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - uint32_t GotPlt = In.GotPlt->getVA() - Ebx; - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + memcpy(buf, v, sizeof(v)); return; } - const uint8_t PltData[] = { + const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4) 0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8) 0x90, 0x90, 0x90, 0x90, // nop }; - memcpy(Buf, PltData, sizeof(PltData)); - uint32_t GotPlt = In.GotPlt->getVA(); - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + memcpy(buf, pltData, sizeof(pltData)); + uint32_t gotPlt = in.gotPlt->getVA(); + write32le(buf + 2, gotPlt + 4); + write32le(buf + 8, gotPlt + 8); } -void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { - 0xff, 0x00, 0, 0, 0, 0, // jmp *foo_in_GOT or jmp *foo@GOT(%ebx) - 0x68, 0, 0, 0, 0, // pushl $reloc_offset - 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC - }; - memcpy(Buf, Inst, sizeof(Inst)); - - if (Config->Pic) { - // jmp *foo@GOT(%ebx) - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - Buf[1] = 0xa3; - write32le(Buf + 2, GotPltEntryAddr - Ebx); +void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + if (config->isPic) { + const uint8_t inst[] = { + 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx) + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC + }; + memcpy(buf, inst, sizeof(inst)); + write32le(buf + 2, gotPltEntryAddr - in.gotPlt->getVA()); } else { - // jmp *foo_in_GOT - Buf[1] = 0x25; - write32le(Buf + 2, GotPltEntryAddr); + const uint8_t inst[] = { + 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC + }; + memcpy(buf, inst, sizeof(inst)); + write32le(buf + 2, gotPltEntryAddr); } - write32le(Buf + 7, RelOff); - write32le(Buf + 12, -getPltEntryOffset(Index) - 16); + write32le(buf + 7, relOff); + write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); } -int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - switch (Type) { +int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { case R_386_8: case R_386_PC8: - return SignExtend64<8>(*Buf); + return SignExtend64<8>(*buf); case R_386_16: case R_386_PC16: - return SignExtend64<16>(read16le(Buf)); + return SignExtend64<16>(read16le(buf)); case R_386_32: case R_386_GOT32: case R_386_GOT32X: @@ -248,28 +254,28 @@ int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { case R_386_PLT32: case R_386_TLS_LDO_32: case R_386_TLS_LE: - return SignExtend64<32>(read32le(Buf)); + return SignExtend64<32>(read32le(buf)); default: return 0; } } -void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_386_8: // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are // being used for some 16-bit programs such as boot loaders, so // we want to support them. - checkIntUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; break; case R_386_PC8: - checkInt(Loc, Val, 8, Type); - *Loc = Val; + checkInt(loc, val, 8, type); + *loc = val; break; case R_386_16: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_386_PC16: // R_386_PC16 is normally used with 16 bit code. In that situation @@ -282,11 +288,10 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { // current location subtracted from it. // We just check that Val fits in 17 bits. This misses some cases, but // should have no false positives. - checkInt(Loc, Val, 17, Type); - write16le(Loc, Val); + checkInt(loc, val, 17, type); + write16le(loc, val); break; case R_386_32: - case R_386_GLOB_DAT: case R_386_GOT32: case R_386_GOT32X: case R_386_GOTOFF: @@ -305,86 +310,86 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_386_TLS_LE_32: case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: - checkInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkInt(loc, val, 32, type); + write32le(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -void X86::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt // to // movl %gs:0,%eax // subl $x@ntpoff,%eax - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax 0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); - write32le(Loc + 5, Val); + memcpy(loc - 3, inst, sizeof(inst)); + write32le(loc + 5, val); } -void X86::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt // to // movl %gs:0, %eax // addl x@gotntpoff(%ebx), %eax - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax 0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); - write32le(Loc + 5, Val); + memcpy(loc - 3, inst, sizeof(inst)); + write32le(loc + 5, val); } // In some conditions, relocations can be optimized to avoid using GOT. // This function does that for Initial Exec to Local Exec case. -void X86::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { // Ulrich's document section 6.2 says that @gotntpoff can // be used with MOVL or ADDL instructions. // @indntpoff is similar to @gotntpoff, but for use in // position dependent code. - uint8_t Reg = (Loc[-1] >> 3) & 7; + uint8_t reg = (loc[-1] >> 3) & 7; - if (Type == R_386_TLS_IE) { - if (Loc[-1] == 0xa1) { + if (type == R_386_TLS_IE) { + if (loc[-1] == 0xa1) { // "movl foo@indntpoff,%eax" -> "movl $foo,%eax" // This case is different from the generic case below because // this is a 5 byte instruction while below is 6 bytes. - Loc[-1] = 0xb8; - } else if (Loc[-2] == 0x8b) { + loc[-1] = 0xb8; + } else if (loc[-2] == 0x8b) { // "movl foo@indntpoff,%reg" -> "movl $foo,%reg" - Loc[-2] = 0xc7; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0xc7; + loc[-1] = 0xc0 | reg; } else { // "addl foo@indntpoff,%reg" -> "addl $foo,%reg" - Loc[-2] = 0x81; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0x81; + loc[-1] = 0xc0 | reg; } } else { - assert(Type == R_386_TLS_GOTIE); - if (Loc[-2] == 0x8b) { + assert(type == R_386_TLS_GOTIE); + if (loc[-2] == 0x8b) { // "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg" - Loc[-2] = 0xc7; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0xc7; + loc[-1] = 0xc0 | reg; } else { // "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg" - Loc[-2] = 0x8d; - Loc[-1] = 0x80 | (Reg << 3) | Reg; + loc[-2] = 0x8d; + loc[-1] = 0x80 | (reg << 3) | reg; } } - write32le(Loc, Val); + write32le(loc, val); } -void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { - if (Type == R_386_TLS_LDO_32) { - write32le(Loc, Val); +void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_386_TLS_LDO_32) { + write32le(loc, val); return; } @@ -395,48 +400,48 @@ void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // movl %gs:0,%eax // nop // leal 0(%esi,1),%esi - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax 0x90, // nop 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi }; - memcpy(Loc - 2, Inst, sizeof(Inst)); + memcpy(loc - 2, inst, sizeof(inst)); } namespace { class RetpolinePic : public X86 { public: RetpolinePic(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; class RetpolineNoPic : public X86 { public: RetpolineNoPic(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace RetpolinePic::RetpolinePic() { - PltHeaderSize = 48; - PltEntrySize = 32; + pltHeaderSize = 48; + pltEntrySize = 32; } -void RetpolinePic::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write32le(Buf, S.getPltVA() + 17); +void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write32le(buf, s.getPltVA() + 17); } -void RetpolinePic::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { - 0xff, 0xb3, 0, 0, 0, 0, // 0: pushl GOTPLT+4(%ebx) +void RetpolinePic::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { + 0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx) 0x50, // 6: pushl %eax - 0x8b, 0x83, 0, 0, 0, 0, // 7: mov GOTPLT+8(%ebx), %eax + 0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next 0xf3, 0x90, // 12: loop: pause 0x0f, 0xae, 0xe8, // 14: lfence @@ -450,18 +455,13 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const { 0xc3, // 2e: ret 0xcc, // 2f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); - - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - uint32_t GotPlt = In.GotPlt->getVA() - Ebx; - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 9, GotPlt + 8); + memcpy(buf, insn, sizeof(insn)); } -void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x50, // pushl %eax 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax 0xe8, 0, 0, 0, 0, // call plt+0x20 @@ -470,28 +470,28 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, 0xe9, 0, 0, 0, 0, // jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); - - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - unsigned Off = getPltEntryOffset(Index); - write32le(Buf + 3, GotPltEntryAddr - Ebx); - write32le(Buf + 8, -Off - 12 + 32); - write32le(Buf + 13, -Off - 17 + 18); - write32le(Buf + 18, RelOff); - write32le(Buf + 23, -Off - 27); + memcpy(buf, insn, sizeof(insn)); + + uint32_t ebx = in.gotPlt->getVA(); + unsigned off = pltHeaderSize + pltEntrySize * index; + write32le(buf + 3, gotPltEntryAddr - ebx); + write32le(buf + 8, -off - 12 + 32); + write32le(buf + 13, -off - 17 + 18); + write32le(buf + 18, relOff); + write32le(buf + 23, -off - 27); } RetpolineNoPic::RetpolineNoPic() { - PltHeaderSize = 48; - PltEntrySize = 32; + pltHeaderSize = 48; + pltEntrySize = 32; } -void RetpolineNoPic::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write32le(Buf, S.getPltVA() + 16); +void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write32le(buf, s.getPltVA() + 16); } -void RetpolineNoPic::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void RetpolineNoPic::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4 0x50, // 6: pushl %eax 0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax @@ -509,17 +509,17 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const { 0xc3, // 2e: ret 0xcc, // 2f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint32_t GotPlt = In.GotPlt->getVA(); - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + uint32_t gotPlt = in.gotPlt->getVA(); + write32le(buf + 2, gotPlt + 4); + write32le(buf + 8, gotPlt + 8); } -void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x50, // 0: pushl %eax 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax 0xe8, 0, 0, 0, 0, // 6: call plt+0x20 @@ -529,26 +529,26 @@ void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding 0xcc, // 1f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); - - unsigned Off = getPltEntryOffset(Index); - write32le(Buf + 2, GotPltEntryAddr); - write32le(Buf + 7, -Off - 11 + 32); - write32le(Buf + 12, -Off - 16 + 17); - write32le(Buf + 17, RelOff); - write32le(Buf + 22, -Off - 26); + memcpy(buf, insn, sizeof(insn)); + + unsigned off = pltHeaderSize + pltEntrySize * index; + write32le(buf + 2, gotPltEntryAddr); + write32le(buf + 7, -off - 11 + 32); + write32le(buf + 12, -off - 16 + 17); + write32le(buf + 17, relOff); + write32le(buf + 22, -off - 26); } TargetInfo *elf::getX86TargetInfo() { - if (Config->ZRetpolineplt) { - if (Config->Pic) { - static RetpolinePic T; - return &T; + if (config->zRetpolineplt) { + if (config->isPic) { + static RetpolinePic t; + return &t; } - static RetpolineNoPic T; - return &T; + static RetpolineNoPic t; + return &t; } - static X86 T; - return &T; + static X86 t; + return &t; } diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp index 06314155dcc9..de67aa5c33dc 100644 --- a/ELF/Arch/X86_64.cpp +++ b/ELF/Arch/X86_64.cpp @@ -1,9 +1,8 @@ //===- X86_64.cpp ---------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -23,71 +22,74 @@ using namespace lld; using namespace lld::elf; namespace { -template <class ELFT> class X86_64 : public TargetInfo { +class X86_64 : public TargetInfo { public: X86_64(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPltHeader(uint8_t *Buf) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxGot(uint8_t *Loc, uint64_t Val) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const override; - -private: - void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, - uint8_t ModRm) const; + int getTlsGdRelaxSkip(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotPltHeader(uint8_t *buf) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; }; } // namespace -template <class ELFT> X86_64<ELFT>::X86_64() { - CopyRel = R_X86_64_COPY; - GotRel = R_X86_64_GLOB_DAT; - NoneRel = R_X86_64_NONE; - PltRel = R_X86_64_JUMP_SLOT; - RelativeRel = R_X86_64_RELATIVE; - IRelativeRel = R_X86_64_IRELATIVE; - TlsGotRel = R_X86_64_TPOFF64; - TlsModuleIndexRel = R_X86_64_DTPMOD64; - TlsOffsetRel = R_X86_64_DTPOFF64; - GotEntrySize = 8; - GotPltEntrySize = 8; - PltEntrySize = 16; - PltHeaderSize = 16; - TlsGdRelaxSkip = 2; - TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 +X86_64::X86_64() { + copyRel = R_X86_64_COPY; + gotRel = R_X86_64_GLOB_DAT; + noneRel = R_X86_64_NONE; + pltRel = R_X86_64_JUMP_SLOT; + relativeRel = R_X86_64_RELATIVE; + iRelativeRel = R_X86_64_IRELATIVE; + symbolicRel = R_X86_64_64; + tlsDescRel = R_X86_64_TLSDESC; + tlsGotRel = R_X86_64_TPOFF64; + tlsModuleIndexRel = R_X86_64_DTPMOD64; + tlsOffsetRel = R_X86_64_DTPOFF64; + pltEntrySize = 16; + pltHeaderSize = 16; + trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. - DefaultImageBase = 0x200000; + defaultImageBase = 0x200000; } -template <class ELFT> -RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; } + +RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + if (type == R_X86_64_GOTTPOFF) + config->hasStaticTlsModel = true; + + switch (type) { case R_X86_64_8: case R_X86_64_16: case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: + return R_ABS; case R_X86_64_DTPOFF32: case R_X86_64_DTPOFF64: - return R_ABS; + return R_DTPREL; case R_X86_64_TPOFF32: return R_TLS; + case R_X86_64_TLSDESC_CALL: + return R_TLSDESC_CALL; case R_X86_64_TLSLD: return R_TLSLD_PC; case R_X86_64_TLSGD: @@ -97,218 +99,280 @@ RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S, return R_SIZE; case R_X86_64_PLT32: return R_PLT_PC; + case R_X86_64_PC8: + case R_X86_64_PC16: case R_X86_64_PC32: case R_X86_64_PC64: return R_PC; case R_X86_64_GOT32: case R_X86_64_GOT64: - return R_GOT_FROM_END; + return R_GOTPLT; + case R_X86_64_GOTPC32_TLSDESC: + return R_TLSDESC_PC; case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTTPOFF: return R_GOT_PC; case R_X86_64_GOTOFF64: - return R_GOTREL_FROM_END; + return R_GOTPLTREL; case R_X86_64_GOTPC32: case R_X86_64_GOTPC64: - return R_GOTONLY_PC_FROM_END; + return R_GOTPLTONLY_PC; case R_X86_64_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const { +void X86_64::writeGotPltHeader(uint8_t *buf) const { // The first entry holds the value of _DYNAMIC. It is not clear why that is // required, but it is documented in the psabi and the glibc dynamic linker // seems to use it (note that this is relevant for linking ld.so, not any // other program). - write64le(Buf, In.Dynamic->getVA()); + write64le(buf, mainPart->dynamic->getVA()); } -template <class ELFT> -void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const { +void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const { // See comments in X86::writeGotPlt. - write64le(Buf, S.getPltVA() + 6); + write64le(buf, s.getPltVA() + 6); } -template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void X86_64::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip) 0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip) 0x0f, 0x1f, 0x40, 0x00, // nop }; - memcpy(Buf, PltData, sizeof(PltData)); - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8 - write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16 + memcpy(buf, pltData, sizeof(pltData)); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + write32le(buf + 2, gotPlt - plt + 2); // GOTPLT+8 + write32le(buf + 8, gotPlt - plt + 4); // GOTPLT+16 } -template <class ELFT> -void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) 0x68, 0, 0, 0, 0, // pushq <relocation index> 0xe9, 0, 0, 0, 0, // jmpq plt[0] }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6); - write32le(Buf + 7, Index); - write32le(Buf + 12, -getPltEntryOffset(Index) - 16); + write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6); + write32le(buf + 7, index); + write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); } -template <class ELFT> RelType X86_64<ELFT>::getDynRel(RelType Type) const { - if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 || - Type == R_X86_64_SIZE64) - return Type; +RelType X86_64::getDynRel(RelType type) const { + if (type == R_X86_64_64 || type == R_X86_64_PC64 || type == R_X86_64_SIZE32 || + type == R_X86_64_SIZE64) + return type; return R_X86_64_NONE; } -template <class ELFT> -void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // .byte 0x66 - // leaq x@tlsgd(%rip), %rdi - // .word 0x6666 - // rex64 - // call __tls_get_addr@plt - // to - // mov %fs:0x0,%rax - // lea x@tpoff,%rax - const uint8_t Inst[] = { - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax - }; - memcpy(Loc - 4, Inst, sizeof(Inst)); - - // The original code used a pc relative relocation and so we have to - // compensate for the -4 in had in the addend. - write32le(Loc + 8, Val + 4); +void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax + }; + memcpy(loc - 4, inst, sizeof(inst)); + + // The original code used a pc relative relocation and so we have to + // compensate for the -4 in had in the addend. + write32le(loc + 8, val + 4); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq $x@tpoff(%rip),%rax + loc[-2] = 0xc7; + loc[-1] = 0xc0; + write32le(loc, val + 4); + // xchg ax,ax + loc[4] = 0x66; + loc[5] = 0x90; + } } -template <class ELFT> -void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // .byte 0x66 - // leaq x@tlsgd(%rip), %rdi - // .word 0x6666 - // rex64 - // call __tls_get_addr@plt - // to - // mov %fs:0x0,%rax - // addq x@tpoff,%rax - const uint8_t Inst[] = { - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@tpoff,%rax - }; - memcpy(Loc - 4, Inst, sizeof(Inst)); - - // Both code sequences are PC relatives, but since we are moving the constant - // forward by 8 bytes we have to subtract the value by 8. - write32le(Loc + 8, Val - 8); +void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax + }; + memcpy(loc - 4, inst, sizeof(inst)); + + // Both code sequences are PC relatives, but since we are moving the + // constant forward by 8 bytes we have to subtract the value by 8. + write32le(loc + 8, val - 8); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq x@gottpoff(%rip),%rax + loc[-2] = 0x8b; + write32le(loc, val); + // xchg ax,ax + loc[4] = 0x66; + loc[5] = 0x90; + } } // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to // R_X86_64_TPOFF32 so that it does not use GOT. -template <class ELFT> -void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - uint8_t *Inst = Loc - 3; - uint8_t Reg = Loc[-1] >> 3; - uint8_t *RegSlot = Loc - 1; +void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + uint8_t *inst = loc - 3; + uint8_t reg = loc[-1] >> 3; + uint8_t *regSlot = loc - 1; // Note that ADD with RSP or R12 is converted to ADD instead of LEA // because LEA with these registers needs 4 bytes to encode and thus // wouldn't fit the space. - if (memcmp(Inst, "\x48\x03\x25", 3) == 0) { + if (memcmp(inst, "\x48\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp" - memcpy(Inst, "\x48\x81\xc4", 3); - } else if (memcmp(Inst, "\x4c\x03\x25", 3) == 0) { + memcpy(inst, "\x48\x81\xc4", 3); + } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12" - memcpy(Inst, "\x49\x81\xc4", 3); - } else if (memcmp(Inst, "\x4c\x03", 2) == 0) { + memcpy(inst, "\x49\x81\xc4", 3); + } else if (memcmp(inst, "\x4c\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]" - memcpy(Inst, "\x4d\x8d", 2); - *RegSlot = 0x80 | (Reg << 3) | Reg; - } else if (memcmp(Inst, "\x48\x03", 2) == 0) { + memcpy(inst, "\x4d\x8d", 2); + *regSlot = 0x80 | (reg << 3) | reg; + } else if (memcmp(inst, "\x48\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg" - memcpy(Inst, "\x48\x8d", 2); - *RegSlot = 0x80 | (Reg << 3) | Reg; - } else if (memcmp(Inst, "\x4c\x8b", 2) == 0) { + memcpy(inst, "\x48\x8d", 2); + *regSlot = 0x80 | (reg << 3) | reg; + } else if (memcmp(inst, "\x4c\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]" - memcpy(Inst, "\x49\xc7", 2); - *RegSlot = 0xc0 | Reg; - } else if (memcmp(Inst, "\x48\x8b", 2) == 0) { + memcpy(inst, "\x49\xc7", 2); + *regSlot = 0xc0 | reg; + } else if (memcmp(inst, "\x48\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg" - memcpy(Inst, "\x48\xc7", 2); - *RegSlot = 0xc0 | Reg; + memcpy(inst, "\x48\xc7", 2); + *regSlot = 0xc0 | reg; } else { - error(getErrorLocation(Loc - 3) + + error(getErrorLocation(loc - 3) + "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only"); } // The original code used a PC relative relocation. // Need to compensate for the -4 it had in the addend. - write32le(Loc, Val + 4); + write32le(loc, val + 4); } -template <class ELFT> -void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // leaq bar@tlsld(%rip), %rdi - // callq __tls_get_addr@PLT - // leaq bar@dtpoff(%rax), %rcx - // to - // .word 0x6666 - // .byte 0x66 - // mov %fs:0,%rax - // leaq bar@tpoff(%rax), %rcx - if (Type == R_X86_64_DTPOFF64) { - write64le(Loc, Val); +void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_DTPOFF64) { + write64le(loc, val); return; } - if (Type == R_X86_64_DTPOFF32) { - write32le(Loc, Val); + if (type == R_X86_64_DTPOFF32) { + write32le(loc, val); return; } - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x66, 0x66, // .word 0x6666 0x66, // .byte 0x66 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); + + if (loc[4] == 0xe8) { + // Convert + // leaq bar@tlsld(%rip), %rdi # 48 8d 3d <Loc> + // callq __tls_get_addr@PLT # e8 <disp32> + // leaq bar@dtpoff(%rax), %rcx + // to + // .word 0x6666 + // .byte 0x66 + // mov %fs:0,%rax + // leaq bar@tpoff(%rax), %rcx + memcpy(loc - 3, inst, sizeof(inst)); + return; + } + + if (loc[4] == 0xff && loc[5] == 0x15) { + // Convert + // leaq x@tlsld(%rip),%rdi # 48 8d 3d <Loc> + // call *__tls_get_addr@GOTPCREL(%rip) # ff 15 <disp32> + // to + // .long 0x66666666 + // movq %fs:0,%rax + // See "Table 11.9: LD -> LE Code Transition (LP64)" in + // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf + loc[-3] = 0x66; + memcpy(loc - 2, inst, sizeof(inst)); + return; + } + + error(getErrorLocation(loc - 3) + + "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD"); } -template <class ELFT> -void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_X86_64_8: - checkUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; + break; + case R_X86_64_PC8: + checkInt(loc, val, 8, type); + *loc = val; break; case R_X86_64_16: - checkUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); + break; + case R_X86_64_PC16: + checkInt(loc, val, 16, type); + write16le(loc, val); break; case R_X86_64_32: - checkUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkUInt(loc, val, 32, type); + write32le(loc, val); break; case R_X86_64_32S: case R_X86_64_TPOFF32: case R_X86_64_GOT32: case R_X86_64_GOTPC32: + case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: @@ -319,49 +383,47 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: - checkInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkInt(loc, val, 32, type); + write32le(loc, val); break; case R_X86_64_64: case R_X86_64_DTPOFF64: - case R_X86_64_GLOB_DAT: case R_X86_64_PC64: case R_X86_64_SIZE64: case R_X86_64_GOT64: case R_X86_64_GOTOFF64: case R_X86_64_GOTPC64: - write64le(Loc, Val); + write64le(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -template <class ELFT> -RelExpr X86_64<ELFT>::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr RelExpr) const { - if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX) - return RelExpr; - const uint8_t Op = Data[-2]; - const uint8_t ModRm = Data[-1]; +RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr relExpr) const { + if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) + return relExpr; + const uint8_t op = data[-2]; + const uint8_t modRm = data[-1]; // FIXME: When PIC is disabled and foo is defined locally in the // lower 32 bit address space, memory operand in mov can be converted into // immediate operand. Otherwise, mov must be changed to lea. We support only // latter relaxation at this moment. - if (Op == 0x8b) + if (op == 0x8b) return R_RELAX_GOT_PC; // Relax call and jmp. - if (Op == 0xff && (ModRm == 0x15 || ModRm == 0x25)) + if (op == 0xff && (modRm == 0x15 || modRm == 0x25)) return R_RELAX_GOT_PC; // Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor. // If PIC then no relaxation is available. // We also don't relax test/binop instructions without REX byte, // they are 32bit operations and not common to have. - assert(Type == R_X86_64_REX_GOTPCRELX); - return Config->Pic ? RelExpr : R_RELAX_GOT_PC_NOPIC; + assert(type == R_X86_64_REX_GOTPCRELX); + return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC; } // A subset of relaxations can only be applied for no-PIC. This method @@ -369,12 +431,11 @@ RelExpr X86_64<ELFT>::adjustRelaxExpr(RelType Type, const uint8_t *Data, // "Intel 64 and IA-32 Architectures Software Developer's Manual V2" // (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/ // 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) -template <class ELFT> -void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, - uint8_t ModRm) const { - const uint8_t Rex = Loc[-3]; +static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, + uint8_t modRm) { + const uint8_t rex = loc[-3]; // Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg". - if (Op == 0x85) { + if (op == 0x85) { // See "TEST-Logical Compare" (4-428 Vol. 2B), // TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension). @@ -391,11 +452,11 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, // 0x38 == 00 111 000 binary. // We transfer reg2 to reg1 here as operand. // See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3). - Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3; // ModR/M byte. + loc[-1] = 0xc0 | (modRm & 0x38) >> 3; // ModR/M byte. // Change opcode from TEST r/m64, r64 to TEST r/m64, imm32 // See "TEST-Logical Compare" (4-428 Vol. 2B). - Loc[-2] = 0xf7; + loc[-2] = 0xf7; // Move R bit to the B bit in REX byte. // REX byte is encoded as 0100WRXB, where @@ -408,8 +469,8 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, // REX.B This 1-bit value is an extension to the MODRM.rm field or the // SIB.base field. // See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A). - Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; - write32le(Loc, Val); + loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; + write32le(loc, val); return; } @@ -419,7 +480,7 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, // Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg". // Logic is close to one for test instruction above, but we also // write opcode extension here, see below for details. - Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3 | (Op & 0x3c); // ModR/M byte. + loc[-1] = 0xc0 | (modRm & 0x38) >> 3 | (op & 0x3c); // ModR/M byte. // Primary opcode is 0x81, opcode extension is one of: // 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB, @@ -428,69 +489,67 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, // See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15), // "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for // descriptions about each operation. - Loc[-2] = 0x81; - Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; - write32le(Loc, Val); + loc[-2] = 0x81; + loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; + write32le(loc, val); } -template <class ELFT> -void X86_64<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const { - const uint8_t Op = Loc[-2]; - const uint8_t ModRm = Loc[-1]; +void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { + const uint8_t op = loc[-2]; + const uint8_t modRm = loc[-1]; // Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". - if (Op == 0x8b) { - Loc[-2] = 0x8d; - write32le(Loc, Val); + if (op == 0x8b) { + loc[-2] = 0x8d; + write32le(loc, val); return; } - if (Op != 0xff) { + if (op != 0xff) { // We are relaxing a rip relative to an absolute, so compensate // for the old -4 addend. - assert(!Config->Pic); - relaxGotNoPic(Loc, Val + 4, Op, ModRm); + assert(!config->isPic); + relaxGotNoPic(loc, val + 4, op, modRm); return; } // Convert call/jmp instructions. - if (ModRm == 0x15) { + if (modRm == 0x15) { // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo". // Instead we convert to "addr32 call foo" where addr32 is an instruction // prefix. That makes result expression to be a single instruction. - Loc[-2] = 0x67; // addr32 prefix - Loc[-1] = 0xe8; // call - write32le(Loc, Val); + loc[-2] = 0x67; // addr32 prefix + loc[-1] = 0xe8; // call + write32le(loc, val); return; } // Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop". // jmp doesn't return, so it is fine to use nop here, it is just a stub. - assert(ModRm == 0x25); - Loc[-2] = 0xe9; // jmp - Loc[3] = 0x90; // nop - write32le(Loc - 1, Val + 1); + assert(modRm == 0x25); + loc[-2] = 0xe9; // jmp + loc[3] = 0x90; // nop + write32le(loc - 1, val + 1); } -// This anonymous namespace works around a warning bug in -// old versions of gcc. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 -namespace { - // A split-stack prologue starts by checking the amount of stack remaining // in one of two ways: // A) Comparing of the stack pointer to a field in the tcb. // B) Or a load of a stack pointer offset with an lea to r10 or r11. -template <> -bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc, - uint8_t *End, - uint8_t StOther) const { - if (Loc + 8 >= End) +bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const { + if (!config->is64) { + error("Target doesn't support split stacks."); + return false; + } + + if (loc + 8 >= end) return false; // Replace "cmp %fs:0x70,%rsp" and subsequent branch // with "stc, nopl 0x0(%rax,%rax,1)" - if (memcmp(Loc, "\x64\x48\x3b\x24\x25", 5) == 0) { - memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); + if (memcmp(loc, "\x64\x48\x3b\x24\x25", 5) == 0) { + memcpy(loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); return true; } @@ -498,25 +557,16 @@ bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc, // be r10 or r11. The lea instruction feeds a subsequent compare which checks // if there is X available stack space. Making X larger effectively reserves // that much additional space. The stack grows downward so subtract the value. - if (memcmp(Loc, "\x4c\x8d\x94\x24", 4) == 0 || - memcmp(Loc, "\x4c\x8d\x9c\x24", 4) == 0) { + if (memcmp(loc, "\x4c\x8d\x94\x24", 4) == 0 || + memcmp(loc, "\x4c\x8d\x9c\x24", 4) == 0) { // The offset bytes are encoded four bytes after the start of the // instruction. - write32le(Loc + 4, read32le(Loc + 4) - 0x4000); + write32le(loc + 4, read32le(loc + 4) - 0x4000); return true; } return false; } -template <> -bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc, - uint8_t *End, - uint8_t StOther) const { - llvm_unreachable("Target doesn't support split stacks."); -} - -} // namespace - // These nonstandard PLT entries are to migtigate Spectre v2 security // vulnerability. In order to mitigate Spectre v2, we want to avoid indirect // branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT @@ -527,37 +577,36 @@ bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc, // is specified, all dynamic symbols are resolved at load-time. Thus, when // that option is given, we can omit code for symbol lazy resolution. namespace { -template <class ELFT> class Retpoline : public X86_64<ELFT> { +class Retpoline : public X86_64 { public: Retpoline(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; -template <class ELFT> class RetpolineZNow : public X86_64<ELFT> { +class RetpolineZNow : public X86_64 { public: RetpolineZNow(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override {} - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override {} + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace -template <class ELFT> Retpoline<ELFT>::Retpoline() { - TargetInfo::PltHeaderSize = 48; - TargetInfo::PltEntrySize = 32; +Retpoline::Retpoline() { + pltHeaderSize = 48; + pltEntrySize = 32; } -template <class ELFT> -void Retpoline<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write64le(Buf, S.getPltVA() + 17); +void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write64le(buf, s.getPltVA() + 17); } -template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void Retpoline::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip) 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next @@ -570,19 +619,18 @@ template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding 0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - write32le(Buf + 2, GotPlt - Plt - 6 + 8); - write32le(Buf + 9, GotPlt - Plt - 13 + 16); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + write32le(buf + 2, gotPlt - plt - 6 + 8); + write32le(buf + 9, gotPlt - plt - 13 + 16); } -template <class ELFT> -void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12 @@ -590,25 +638,24 @@ void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, 0xe9, 0, 0, 0, 0, // 16: jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint64_t Off = getPltEntryOffset(Index); + uint64_t off = pltHeaderSize + pltEntrySize * index; - write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); - write32le(Buf + 8, -Off - 12 + 32); - write32le(Buf + 13, -Off - 17 + 18); - write32le(Buf + 18, Index); - write32le(Buf + 23, -Off - 27); + write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); + write32le(buf + 8, -off - 12 + 32); + write32le(buf + 13, -off - 17 + 18); + write32le(buf + 18, index); + write32le(buf + 23, -off - 27); } -template <class ELFT> RetpolineZNow<ELFT>::RetpolineZNow() { - TargetInfo::PltHeaderSize = 32; - TargetInfo::PltEntrySize = 16; +RetpolineZNow::RetpolineZNow() { + pltHeaderSize = 32; + pltEntrySize = 16; } -template <class ELFT> -void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void RetpolineZNow::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next 0xf3, 0x90, // 5: loop: pause 0x0f, 0xae, 0xe8, // 7: lfence @@ -620,37 +667,35 @@ void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding 0xcc, // 1f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); } -template <class ELFT> -void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11 0xe9, 0, 0, 0, 0, // jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); - write32le(Buf + 8, -getPltEntryOffset(Index) - 12); + write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); + write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12); } -template <class ELFT> static TargetInfo *getTargetInfo() { - if (Config->ZRetpolineplt) { - if (Config->ZNow) { - static RetpolineZNow<ELFT> T; - return &T; +static TargetInfo *getTargetInfo() { + if (config->zRetpolineplt) { + if (config->zNow) { + static RetpolineZNow t; + return &t; } - static Retpoline<ELFT> T; - return &T; + static Retpoline t; + return &t; } - static X86_64<ELFT> T; - return &T; + static X86_64 t; + return &t; } -TargetInfo *elf::getX32TargetInfo() { return getTargetInfo<ELF32LE>(); } -TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo<ELF64LE>(); } +TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } diff --git a/ELF/Bits.h b/ELF/Bits.h deleted file mode 100644 index 13d40322265e..000000000000 --- a/ELF/Bits.h +++ /dev/null @@ -1,35 +0,0 @@ -//===- Bits.h ---------------------------------------------------*- C++ -*-===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_ELF_BITS_H -#define LLD_ELF_BITS_H - -#include "Config.h" -#include "llvm/Support/Endian.h" - -namespace lld { -namespace elf { - -inline uint64_t readUint(uint8_t *Buf) { - if (Config->Is64) - return llvm::support::endian::read64(Buf, Config->Endianness); - return llvm::support::endian::read32(Buf, Config->Endianness); -} - -inline void writeUint(uint8_t *Buf, uint64_t Val) { - if (Config->Is64) - llvm::support::endian::write64(Buf, Val, Config->Endianness); - else - llvm::support::endian::write32(Buf, Val, Config->Endianness); -} - -} // namespace elf -} // namespace lld - -#endif diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt index a1c23b0d49ac..70578746483d 100644 --- a/ELF/CMakeLists.txt +++ b/ELF/CMakeLists.txt @@ -27,7 +27,6 @@ add_lld_library(lldELF Driver.cpp DriverUtils.cpp EhFrame.cpp - Filesystem.cpp ICF.cpp InputFiles.cpp InputSection.cpp diff --git a/ELF/CallGraphSort.cpp b/ELF/CallGraphSort.cpp index 2a7d78664b8e..9aaadd481833 100644 --- a/ELF/CallGraphSort.cpp +++ b/ELF/CallGraphSort.cpp @@ -1,9 +1,8 @@ //===- CallGraphSort.cpp --------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// @@ -52,24 +51,24 @@ using namespace lld::elf; namespace { struct Edge { - int From; - uint64_t Weight; + int from; + uint64_t weight; }; struct Cluster { - Cluster(int Sec, size_t S) : Sections{Sec}, Size(S) {} + Cluster(int sec, size_t s) : sections{sec}, size(s) {} double getDensity() const { - if (Size == 0) + if (size == 0) return 0; - return double(Weight) / double(Size); + return double(weight) / double(size); } - std::vector<int> Sections; - size_t Size = 0; - uint64_t Weight = 0; - uint64_t InitialWeight = 0; - Edge BestPred = {-1, 0}; + std::vector<int> sections; + size_t size = 0; + uint64_t weight = 0; + uint64_t initialWeight = 0; + Edge bestPred = {-1, 0}; }; class CallGraphSort { @@ -79,8 +78,8 @@ public: DenseMap<const InputSectionBase *, int> run(); private: - std::vector<Cluster> Clusters; - std::vector<const InputSectionBase *> Sections; + std::vector<Cluster> clusters; + std::vector<const InputSectionBase *> sections; void groupClusters(); }; @@ -93,30 +92,30 @@ constexpr int MAX_DENSITY_DEGRADATION = 8; constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024; } // end anonymous namespace -typedef std::pair<const InputSectionBase *, const InputSectionBase *> - SectionPair; +using SectionPair = + std::pair<const InputSectionBase *, const InputSectionBase *>; // Take the edge list in Config->CallGraphProfile, resolve symbol names to // Symbols, and generate a graph between InputSections with the provided // weights. CallGraphSort::CallGraphSort() { - MapVector<SectionPair, uint64_t> &Profile = Config->CallGraphProfile; - DenseMap<const InputSectionBase *, int> SecToCluster; - - auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int { - auto Res = SecToCluster.insert(std::make_pair(IS, Clusters.size())); - if (Res.second) { - Sections.push_back(IS); - Clusters.emplace_back(Clusters.size(), IS->getSize()); + MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile; + DenseMap<const InputSectionBase *, int> secToCluster; + + auto getOrCreateNode = [&](const InputSectionBase *isec) -> int { + auto res = secToCluster.insert(std::make_pair(isec, clusters.size())); + if (res.second) { + sections.push_back(isec); + clusters.emplace_back(clusters.size(), isec->getSize()); } - return Res.first->second; + return res.first->second; }; // Create the graph. - for (std::pair<SectionPair, uint64_t> &C : Profile) { - const auto *FromSB = cast<InputSectionBase>(C.first.first->Repl); - const auto *ToSB = cast<InputSectionBase>(C.first.second->Repl); - uint64_t Weight = C.second; + for (std::pair<SectionPair, uint64_t> &c : profile) { + const auto *fromSB = cast<InputSectionBase>(c.first.first->repl); + const auto *toSB = cast<InputSectionBase>(c.first.second->repl); + uint64_t weight = c.second; // Ignore edges between input sections belonging to different output // sections. This is done because otherwise we would end up with clusters @@ -124,110 +123,130 @@ CallGraphSort::CallGraphSort() { // output. This messes with the cluster size and density calculations. We // would also end up moving input sections in other output sections without // moving them closer to what calls them. - if (FromSB->getOutputSection() != ToSB->getOutputSection()) + if (fromSB->getOutputSection() != toSB->getOutputSection()) continue; - int From = GetOrCreateNode(FromSB); - int To = GetOrCreateNode(ToSB); + int from = getOrCreateNode(fromSB); + int to = getOrCreateNode(toSB); - Clusters[To].Weight += Weight; + clusters[to].weight += weight; - if (From == To) + if (from == to) continue; // Remember the best edge. - Cluster &ToC = Clusters[To]; - if (ToC.BestPred.From == -1 || ToC.BestPred.Weight < Weight) { - ToC.BestPred.From = From; - ToC.BestPred.Weight = Weight; + Cluster &toC = clusters[to]; + if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) { + toC.bestPred.from = from; + toC.bestPred.weight = weight; } } - for (Cluster &C : Clusters) - C.InitialWeight = C.Weight; + for (Cluster &c : clusters) + c.initialWeight = c.weight; } // It's bad to merge clusters which would degrade the density too much. -static bool isNewDensityBad(Cluster &A, Cluster &B) { - double NewDensity = double(A.Weight + B.Weight) / double(A.Size + B.Size); - return NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION; +static bool isNewDensityBad(Cluster &a, Cluster &b) { + double newDensity = double(a.weight + b.weight) / double(a.size + b.size); + return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION; } -static void mergeClusters(Cluster &Into, Cluster &From) { - Into.Sections.insert(Into.Sections.end(), From.Sections.begin(), - From.Sections.end()); - Into.Size += From.Size; - Into.Weight += From.Weight; - From.Sections.clear(); - From.Size = 0; - From.Weight = 0; +static void mergeClusters(Cluster &into, Cluster &from) { + into.sections.insert(into.sections.end(), from.sections.begin(), + from.sections.end()); + into.size += from.size; + into.weight += from.weight; + from.sections.clear(); + from.size = 0; + from.weight = 0; } // Group InputSections into clusters using the Call-Chain Clustering heuristic // then sort the clusters by density. void CallGraphSort::groupClusters() { - std::vector<int> SortedSecs(Clusters.size()); - std::vector<Cluster *> SecToCluster(Clusters.size()); + std::vector<int> sortedSecs(clusters.size()); + std::vector<Cluster *> secToCluster(clusters.size()); - for (size_t I = 0; I < Clusters.size(); ++I) { - SortedSecs[I] = I; - SecToCluster[I] = &Clusters[I]; + for (size_t i = 0; i < clusters.size(); ++i) { + sortedSecs[i] = i; + secToCluster[i] = &clusters[i]; } - std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) { - return Clusters[B].getDensity() < Clusters[A].getDensity(); + llvm::stable_sort(sortedSecs, [&](int a, int b) { + return clusters[a].getDensity() > clusters[b].getDensity(); }); - for (int SI : SortedSecs) { - // Clusters[SI] is the same as SecToClusters[SI] here because it has not + for (int si : sortedSecs) { + // clusters[si] is the same as secToClusters[si] here because it has not // been merged into another cluster yet. - Cluster &C = Clusters[SI]; + Cluster &c = clusters[si]; // Don't consider merging if the edge is unlikely. - if (C.BestPred.From == -1 || C.BestPred.Weight * 10 <= C.InitialWeight) + if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight) continue; - Cluster *PredC = SecToCluster[C.BestPred.From]; - if (PredC == &C) + Cluster *predC = secToCluster[c.bestPred.from]; + if (predC == &c) continue; - if (C.Size + PredC->Size > MAX_CLUSTER_SIZE) + if (c.size + predC->size > MAX_CLUSTER_SIZE) continue; - if (isNewDensityBad(*PredC, C)) + if (isNewDensityBad(*predC, c)) continue; // NOTE: Consider using a disjoint-set to track section -> cluster mapping // if this is ever slow. - for (int SI : C.Sections) - SecToCluster[SI] = PredC; + for (int si : c.sections) + secToCluster[si] = predC; - mergeClusters(*PredC, C); + mergeClusters(*predC, c); } // Remove empty or dead nodes. Invalidates all cluster indices. - llvm::erase_if(Clusters, [](const Cluster &C) { - return C.Size == 0 || C.Sections.empty(); + llvm::erase_if(clusters, [](const Cluster &c) { + return c.size == 0 || c.sections.empty(); }); // Sort by density. - std::stable_sort(Clusters.begin(), Clusters.end(), - [](const Cluster &A, const Cluster &B) { - return A.getDensity() > B.getDensity(); - }); + llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) { + return a.getDensity() > b.getDensity(); + }); } DenseMap<const InputSectionBase *, int> CallGraphSort::run() { groupClusters(); // Generate order. - DenseMap<const InputSectionBase *, int> OrderMap; - ssize_t CurOrder = 1; + DenseMap<const InputSectionBase *, int> orderMap; + ssize_t curOrder = 1; + + for (const Cluster &c : clusters) + for (int secIndex : c.sections) + orderMap[sections[secIndex]] = curOrder++; + + if (!config->printSymbolOrder.empty()) { + std::error_code ec; + raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None); + if (ec) { + error("cannot open " + config->printSymbolOrder + ": " + ec.message()); + return orderMap; + } - for (const Cluster &C : Clusters) - for (int SecIndex : C.Sections) - OrderMap[Sections[SecIndex]] = CurOrder++; + // Print the symbols ordered by C3, in the order of increasing curOrder + // Instead of sorting all the orderMap, just repeat the loops above. + for (const Cluster &c : clusters) + for (int secIndex : c.sections) + // Search all the symbols in the file of the section + // and find out a Defined symbol with name that is within the section. + for (Symbol *sym: sections[secIndex]->file->getSymbols()) + if (!sym->isSection()) // Filter out section-type symbols here. + if (auto *d = dyn_cast<Defined>(sym)) + if (sections[secIndex] == d->section) + os << sym->getName() << "\n"; + } - return OrderMap; + return orderMap; } // Sort sections by the profile data provided by -callgraph-profile-file diff --git a/ELF/CallGraphSort.h b/ELF/CallGraphSort.h index 3f96dc88f435..5a092278a416 100644 --- a/ELF/CallGraphSort.h +++ b/ELF/CallGraphSort.h @@ -1,9 +1,8 @@ //===- CallGraphSort.h ------------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/ELF/Config.h b/ELF/Config.h index 8fb760e592eb..ff9d3dc0933c 100644 --- a/ELF/Config.h +++ b/ELF/Config.h @@ -1,9 +1,8 @@ //===- Config.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -18,6 +17,7 @@ #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Endian.h" +#include <atomic> #include <vector> namespace lld { @@ -62,18 +62,17 @@ enum class Target2Policy { Abs, Rel, GotRel }; enum class ARMVFPArgKind { Default, Base, VFP, ToolChain }; struct SymbolVersion { - llvm::StringRef Name; - bool IsExternCpp; - bool HasWildcard; + llvm::StringRef name; + bool isExternCpp; + bool hasWildcard; }; // This struct contains symbols version definition that // can be found in version script if it is used for link. struct VersionDefinition { - llvm::StringRef Name; - uint16_t Id = 0; - std::vector<SymbolVersion> Globals; - size_t NameOff = 0; // Offset in the string table + llvm::StringRef name; + uint16_t id = 0; + std::vector<SymbolVersion> globals; }; // This struct contains the global configuration for the linker. @@ -81,162 +80,177 @@ struct VersionDefinition { // and such fields have the same name as the corresponding options. // Most fields are initialized by the driver. struct Configuration { - uint8_t OSABI = 0; - llvm::CachePruningPolicy ThinLTOCachePolicy; - llvm::StringMap<uint64_t> SectionStartMap; - llvm::StringRef Chroot; - llvm::StringRef DynamicLinker; - llvm::StringRef DwoDir; - llvm::StringRef Entry; - llvm::StringRef Emulation; - llvm::StringRef Fini; - llvm::StringRef Init; - llvm::StringRef LTOAAPipeline; - llvm::StringRef LTONewPmPasses; - llvm::StringRef LTOObjPath; - llvm::StringRef LTOSampleProfile; - llvm::StringRef MapFile; - llvm::StringRef OutputFile; - llvm::StringRef OptRemarksFilename; - llvm::StringRef ProgName; - llvm::StringRef SoName; - llvm::StringRef Sysroot; - llvm::StringRef ThinLTOCacheDir; - llvm::StringRef ThinLTOIndexOnlyArg; - std::pair<llvm::StringRef, llvm::StringRef> ThinLTOObjectSuffixReplace; - std::pair<llvm::StringRef, llvm::StringRef> ThinLTOPrefixReplace; - std::string Rpath; - std::vector<VersionDefinition> VersionDefinitions; - std::vector<llvm::StringRef> AuxiliaryList; - std::vector<llvm::StringRef> FilterList; - std::vector<llvm::StringRef> SearchPaths; - std::vector<llvm::StringRef> SymbolOrderingFile; - std::vector<llvm::StringRef> Undefined; - std::vector<SymbolVersion> DynamicList; - std::vector<SymbolVersion> VersionScriptGlobals; - std::vector<SymbolVersion> VersionScriptLocals; - std::vector<uint8_t> BuildIdVector; + uint8_t osabi = 0; + uint32_t andFeatures = 0; + llvm::CachePruningPolicy thinLTOCachePolicy; + llvm::StringMap<uint64_t> sectionStartMap; + llvm::StringRef chroot; + llvm::StringRef dynamicLinker; + llvm::StringRef dwoDir; + llvm::StringRef entry; + llvm::StringRef emulation; + llvm::StringRef fini; + llvm::StringRef init; + llvm::StringRef ltoAAPipeline; + llvm::StringRef ltoCSProfileFile; + llvm::StringRef ltoNewPmPasses; + llvm::StringRef ltoObjPath; + llvm::StringRef ltoSampleProfile; + llvm::StringRef mapFile; + llvm::StringRef outputFile; + llvm::StringRef optRemarksFilename; + llvm::StringRef optRemarksPasses; + llvm::StringRef optRemarksFormat; + llvm::StringRef progName; + llvm::StringRef printSymbolOrder; + llvm::StringRef soName; + llvm::StringRef sysroot; + llvm::StringRef thinLTOCacheDir; + llvm::StringRef thinLTOIndexOnlyArg; + std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace; + std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace; + std::string rpath; + std::vector<VersionDefinition> versionDefinitions; + std::vector<llvm::StringRef> auxiliaryList; + std::vector<llvm::StringRef> filterList; + std::vector<llvm::StringRef> searchPaths; + std::vector<llvm::StringRef> symbolOrderingFile; + std::vector<llvm::StringRef> undefined; + std::vector<SymbolVersion> dynamicList; + std::vector<SymbolVersion> versionScriptGlobals; + std::vector<SymbolVersion> versionScriptLocals; + std::vector<uint8_t> buildIdVector; llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>, uint64_t> - CallGraphProfile; - bool AllowMultipleDefinition; - bool AndroidPackDynRelocs; - bool ARMHasBlx = false; - bool ARMHasMovtMovw = false; - bool ARMJ1J2BranchEncoding = false; - bool AsNeeded = false; - bool Bsymbolic; - bool BsymbolicFunctions; - bool CallGraphProfileSort; - bool CheckSections; - bool CompressDebugSections; - bool Cref; - bool DefineCommon; - bool Demangle = true; - bool DisableVerify; - bool EhFrameHdr; - bool EmitLLVM; - bool EmitRelocs; - bool EnableNewDtags; - bool ExecuteOnly; - bool ExportDynamic; - bool FixCortexA53Errata843419; - bool FormatBinary = false; - bool GcSections; - bool GdbIndex; - bool GnuHash = false; - bool GnuUnique; - bool HasDynamicList = false; - bool HasDynSymTab; - bool IgnoreDataAddressEquality; - bool IgnoreFunctionAddressEquality; - bool LTODebugPassManager; - bool LTONewPassManager; - bool MergeArmExidx; - bool MipsN32Abi = false; - bool NoinhibitExec; - bool Nostdlib; - bool OFormatBinary; - bool Omagic; - bool OptRemarksWithHotness; - bool Pie; - bool PrintGcSections; - bool PrintIcfSections; - bool Relocatable; - bool RelrPackDynRelocs; - bool SaveTemps; - bool SingleRoRx; - bool Shared; - bool Static = false; - bool SysvHash = false; - bool Target1Rel; - bool Trace; - bool ThinLTOEmitImportsFiles; - bool ThinLTOIndexOnly; - bool TocOptimize; - bool UndefinedVersion; - bool UseAndroidRelrTags = false; - bool WarnBackrefs; - bool WarnCommon; - bool WarnIfuncTextrel; - bool WarnMissingEntry; - bool WarnSymbolOrdering; - bool WriteAddends; - bool ZCombreloc; - bool ZCopyreloc; - bool ZExecstack; - bool ZGlobal; - bool ZHazardplt; - bool ZInitfirst; - bool ZInterpose; - bool ZKeepTextSectionPrefix; - bool ZNodefaultlib; - bool ZNodelete; - bool ZNodlopen; - bool ZNow; - bool ZOrigin; - bool ZRelro; - bool ZRodynamic; - bool ZText; - bool ZRetpolineplt; - bool ZWxneeded; - DiscardPolicy Discard; - ICFLevel ICF; - OrphanHandlingPolicy OrphanHandling; - SortSectionPolicy SortSection; - StripPolicy Strip; - UnresolvedPolicy UnresolvedSymbols; - Target2Policy Target2; - ARMVFPArgKind ARMVFPArgs = ARMVFPArgKind::Default; - BuildIdKind BuildId = BuildIdKind::None; - ELFKind EKind = ELFNoneKind; - uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; - uint16_t EMachine = llvm::ELF::EM_NONE; - llvm::Optional<uint64_t> ImageBase; - uint64_t MaxPageSize; - uint64_t MipsGotSize; - uint64_t ZStackSize; - unsigned LTOPartitions; - unsigned LTOO; - unsigned Optimize; - unsigned ThinLTOJobs; - int32_t SplitStackAdjustSize; + callGraphProfile; + bool allowMultipleDefinition; + bool allowShlibUndefined; + bool androidPackDynRelocs; + bool armHasBlx = false; + bool armHasMovtMovw = false; + bool armJ1J2BranchEncoding = false; + bool asNeeded = false; + bool bsymbolic; + bool bsymbolicFunctions; + bool callGraphProfileSort; + bool checkSections; + bool compressDebugSections; + bool cref; + bool defineCommon; + bool demangle = true; + bool dependentLibraries; + bool disableVerify; + bool ehFrameHdr; + bool emitLLVM; + bool emitRelocs; + bool enableNewDtags; + bool executeOnly; + bool exportDynamic; + bool fixCortexA53Errata843419; + bool forceBTI; + bool formatBinary = false; + bool requireCET; + bool gcSections; + bool gdbIndex; + bool gnuHash = false; + bool gnuUnique; + bool hasDynamicList = false; + bool hasDynSymTab; + bool ignoreDataAddressEquality; + bool ignoreFunctionAddressEquality; + bool ltoCSProfileGenerate; + bool ltoDebugPassManager; + bool ltoNewPassManager; + bool mergeArmExidx; + bool mipsN32Abi = false; + bool nmagic; + bool noinhibitExec; + bool nostdlib; + bool oFormatBinary; + bool omagic; + bool optRemarksWithHotness; + bool pacPlt; + bool picThunk; + bool pie; + bool printGcSections; + bool printIcfSections; + bool relocatable; + bool relrPackDynRelocs; + bool saveTemps; + bool singleRoRx; + bool shared; + bool isStatic = false; + bool sysvHash = false; + bool target1Rel; + bool trace; + bool thinLTOEmitImportsFiles; + bool thinLTOIndexOnly; + bool tocOptimize; + bool undefinedVersion; + bool useAndroidRelrTags = false; + bool warnBackrefs; + bool warnCommon; + bool warnIfuncTextrel; + bool warnMissingEntry; + bool warnSymbolOrdering; + bool writeAddends; + bool zCombreloc; + bool zCopyreloc; + bool zExecstack; + bool zGlobal; + bool zHazardplt; + bool zIfuncNoplt; + bool zInitfirst; + bool zInterpose; + bool zKeepTextSectionPrefix; + bool zNodefaultlib; + bool zNodelete; + bool zNodlopen; + bool zNow; + bool zOrigin; + bool zRelro; + bool zRodynamic; + bool zText; + bool zRetpolineplt; + bool zWxneeded; + DiscardPolicy discard; + ICFLevel icf; + OrphanHandlingPolicy orphanHandling; + SortSectionPolicy sortSection; + StripPolicy strip; + UnresolvedPolicy unresolvedSymbols; + Target2Policy target2; + ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default; + BuildIdKind buildId = BuildIdKind::None; + ELFKind ekind = ELFNoneKind; + uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; + uint16_t emachine = llvm::ELF::EM_NONE; + llvm::Optional<uint64_t> imageBase; + uint64_t commonPageSize; + uint64_t maxPageSize; + uint64_t mipsGotSize; + uint64_t zStackSize; + unsigned ltoPartitions; + unsigned ltoo; + unsigned optimize; + unsigned thinLTOJobs; + int32_t splitStackAdjustSize; // The following config options do not directly correspond to any // particualr command line options. // True if we need to pass through relocations in input files to the // output file. Usually false because we consume relocations. - bool CopyRelocs; + bool copyRelocs; // True if the target is ELF64. False if ELF32. - bool Is64; + bool is64; // True if the target is little-endian. False if big-endian. - bool IsLE; + bool isLE; - // endianness::little if IsLE is true. endianness::big otherwise. - llvm::support::endianness Endianness; + // endianness::little if isLE is true. endianness::big otherwise. + llvm::support::endianness endianness; // True if the target is the little-endian MIPS64. // @@ -250,10 +264,24 @@ struct Configuration { // name whatever that means. A fun hypothesis is that "EL" is short for // little-endian written in the little-endian order, but I don't know // if that's true.) - bool IsMips64EL; + bool isMips64EL; + + // True if we need to set the DF_STATIC_TLS flag to an output file, + // which works as a hint to the dynamic loader that the file contains + // code compiled with the static TLS model. The thread-local variable + // compiled with the static TLS model is faster but less flexible, and + // it may not be loaded using dlopen(). + // + // We set this flag to true when we see a relocation for the static TLS + // model. Once this becomes true, it will never become false. + // + // Since the flag is updated by multi-threaded code, we use std::atomic. + // (Writing to a variable is not considered thread-safe even if the + // variable is boolean and we always set the same value from all threads.) + std::atomic<bool> hasStaticTlsModel{false}; // Holds set of ELF header flags for the target. - uint32_t EFlags = 0; + uint32_t eflags = 0; // The ELF spec defines two types of relocation table entries, RELA and // REL. RELA is a triplet of (offset, info, addend) while REL is a @@ -269,23 +297,23 @@ struct Configuration { // Each ABI defines its relocation type. IsRela is true if target // uses RELA. As far as we know, all 64-bit ABIs are using RELA. A // few 32-bit ABIs are using RELA too. - bool IsRela; + bool isRela; // True if we are creating position-independent code. - bool Pic; + bool isPic; // 4 for ELF32, 8 for ELF64. - int Wordsize; + int wordsize; }; // The only instance of Configuration struct. -extern Configuration *Config; +extern Configuration *config; -static inline void errorOrWarn(const Twine &Msg) { - if (!Config->NoinhibitExec) - error(Msg); +static inline void errorOrWarn(const Twine &msg) { + if (!config->noinhibitExec) + error(msg); else - warn(Msg); + warn(msg); } } // namespace elf } // namespace lld diff --git a/ELF/DWARF.cpp b/ELF/DWARF.cpp index 17e1a4d600eb..1e4b36f71b54 100644 --- a/ELF/DWARF.cpp +++ b/ELF/DWARF.cpp @@ -1,9 +1,8 @@ //===- DWARF.cpp ----------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -26,81 +25,102 @@ using namespace llvm::object; using namespace lld; using namespace lld::elf; -template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) { - for (InputSectionBase *Sec : Obj->getSections()) { - if (!Sec) +template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) { + for (InputSectionBase *sec : obj->getSections()) { + if (!sec) continue; - if (LLDDWARFSection *M = - StringSwitch<LLDDWARFSection *>(Sec->Name) - .Case(".debug_addr", &AddrSection) - .Case(".debug_gnu_pubnames", &GnuPubNamesSection) - .Case(".debug_gnu_pubtypes", &GnuPubTypesSection) - .Case(".debug_info", &InfoSection) - .Case(".debug_ranges", &RangeSection) - .Case(".debug_rnglists", &RngListsSection) - .Case(".debug_line", &LineSection) + if (LLDDWARFSection *m = + StringSwitch<LLDDWARFSection *>(sec->name) + .Case(".debug_addr", &addrSection) + .Case(".debug_gnu_pubnames", &gnuPubNamesSection) + .Case(".debug_gnu_pubtypes", &gnuPubTypesSection) + .Case(".debug_info", &infoSection) + .Case(".debug_ranges", &rangeSection) + .Case(".debug_rnglists", &rngListsSection) + .Case(".debug_line", &lineSection) .Default(nullptr)) { - M->Data = toStringRef(Sec->data()); - M->Sec = Sec; + m->Data = toStringRef(sec->data()); + m->sec = sec; continue; } - if (Sec->Name == ".debug_abbrev") - AbbrevSection = toStringRef(Sec->data()); - else if (Sec->Name == ".debug_str") - StrSection = toStringRef(Sec->data()); - else if (Sec->Name == ".debug_line_str") - LineStringSection = toStringRef(Sec->data()); + if (sec->name == ".debug_abbrev") + abbrevSection = toStringRef(sec->data()); + else if (sec->name == ".debug_str") + strSection = toStringRef(sec->data()); + else if (sec->name == ".debug_line_str") + lineStringSection = toStringRef(sec->data()); } } +namespace { +template <class RelTy> struct LLDRelocationResolver { + // In the ELF ABIs, S sepresents the value of the symbol in the relocation + // entry. For Rela, the addend is stored as part of the relocation entry. + static uint64_t resolve(object::RelocationRef ref, uint64_t s, + uint64_t /* A */) { + return s + ref.getRawDataRefImpl().p; + } +}; + +template <class ELFT> struct LLDRelocationResolver<Elf_Rel_Impl<ELFT, false>> { + // For Rel, the addend A is supplied by the caller. + static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s, + uint64_t a) { + return s + a; + } +}; +} // namespace + // Find if there is a relocation at Pos in Sec. The code is a bit // more complicated than usual because we need to pass a section index // to llvm since it has no idea about InputSection. template <class ELFT> template <class RelTy> Optional<RelocAddrEntry> -LLDDwarfObj<ELFT>::findAux(const InputSectionBase &Sec, uint64_t Pos, - ArrayRef<RelTy> Rels) const { - auto It = std::lower_bound( - Rels.begin(), Rels.end(), Pos, - [](const RelTy &A, uint64_t B) { return A.r_offset < B; }); - if (It == Rels.end() || It->r_offset != Pos) +LLDDwarfObj<ELFT>::findAux(const InputSectionBase &sec, uint64_t pos, + ArrayRef<RelTy> rels) const { + auto it = + partition_point(rels, [=](const RelTy &a) { return a.r_offset < pos; }); + if (it == rels.end() || it->r_offset != pos) return None; - const RelTy &Rel = *It; + const RelTy &rel = *it; - const ObjFile<ELFT> *File = Sec.getFile<ELFT>(); - uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); - const typename ELFT::Sym &Sym = File->getELFSyms()[SymIndex]; - uint32_t SecIndex = File->getSectionIndex(Sym); + const ObjFile<ELFT> *file = sec.getFile<ELFT>(); + uint32_t symIndex = rel.getSymbol(config->isMips64EL); + const typename ELFT::Sym &sym = file->template getELFSyms<ELFT>()[symIndex]; + uint32_t secIndex = file->getSectionIndex(sym); - // Broken debug info can point to a non-Defined symbol. - auto *DR = dyn_cast<Defined>(&File->getRelocTargetSym(Rel)); - if (!DR) { - RelType Type = Rel.getType(Config->IsMips64EL); - if (Type != Target->NoneRel) - error(toString(File) + ": relocation " + lld::toString(Type) + " at 0x" + - llvm::utohexstr(Rel.r_offset) + " has unsupported target"); - return None; - } - uint64_t Val = DR->Value + getAddend<ELFT>(Rel); + // An undefined symbol may be a symbol defined in a discarded section. We + // shall still resolve it. This is important for --gdb-index: the end address + // offset of an entry in .debug_ranges is relocated. If it is not resolved, + // its zero value will terminate the decoding of .debug_ranges prematurely. + Symbol &s = file->getRelocTargetSym(rel); + uint64_t val = 0; + if (auto *dr = dyn_cast<Defined>(&s)) { + val = dr->value; - // FIXME: We should be consistent about always adding the file - // offset or not. - if (DR->Section->Flags & ELF::SHF_ALLOC) - Val += cast<InputSection>(DR->Section)->getOffsetInFile(); + // FIXME: We should be consistent about always adding the file + // offset or not. + if (dr->section->flags & ELF::SHF_ALLOC) + val += cast<InputSection>(dr->section)->getOffsetInFile(); + } - return RelocAddrEntry{SecIndex, Val}; + DataRefImpl d; + d.p = getAddend<ELFT>(rel); + return RelocAddrEntry{secIndex, RelocationRef(d, nullptr), + val, Optional<object::RelocationRef>(), + 0, LLDRelocationResolver<RelTy>::resolve}; } template <class ELFT> -Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &S, - uint64_t Pos) const { - auto &Sec = static_cast<const LLDDWARFSection &>(S); - if (Sec.Sec->AreRelocsRela) - return findAux(*Sec.Sec, Pos, Sec.Sec->template relas<ELFT>()); - return findAux(*Sec.Sec, Pos, Sec.Sec->template rels<ELFT>()); +Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s, + uint64_t pos) const { + auto &sec = static_cast<const LLDDWARFSection &>(s); + if (sec.sec->areRelocsRela) + return findAux(*sec.sec, pos, sec.sec->template relas<ELFT>()); + return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>()); } template class elf::LLDDwarfObj<ELF32LE>; diff --git a/ELF/DWARF.h b/ELF/DWARF.h index 8ecf02c77fb4..426022945007 100644 --- a/ELF/DWARF.h +++ b/ELF/DWARF.h @@ -1,9 +1,8 @@ //===- DWARF.h -----------------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===-------------------------------------------------------------------===// @@ -21,70 +20,70 @@ namespace elf { class InputSection; struct LLDDWARFSection final : public llvm::DWARFSection { - InputSectionBase *Sec = nullptr; + InputSectionBase *sec = nullptr; }; template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject { public: - explicit LLDDwarfObj(ObjFile<ELFT> *Obj); + explicit LLDDwarfObj(ObjFile<ELFT> *obj); void forEachInfoSections( - llvm::function_ref<void(const llvm::DWARFSection &)> F) const override { - F(InfoSection); + llvm::function_ref<void(const llvm::DWARFSection &)> f) const override { + f(infoSection); } const llvm::DWARFSection &getRangeSection() const override { - return RangeSection; + return rangeSection; } const llvm::DWARFSection &getRnglistsSection() const override { - return RngListsSection; + return rngListsSection; } const llvm::DWARFSection &getLineSection() const override { - return LineSection; + return lineSection; } const llvm::DWARFSection &getAddrSection() const override { - return AddrSection; + return addrSection; } const llvm::DWARFSection &getGnuPubNamesSection() const override { - return GnuPubNamesSection; + return gnuPubNamesSection; } const llvm::DWARFSection &getGnuPubTypesSection() const override { - return GnuPubTypesSection; + return gnuPubTypesSection; } StringRef getFileName() const override { return ""; } - StringRef getAbbrevSection() const override { return AbbrevSection; } - StringRef getStringSection() const override { return StrSection; } - StringRef getLineStringSection() const override { return LineStringSection; } + StringRef getAbbrevSection() const override { return abbrevSection; } + StringRef getStringSection() const override { return strSection; } + StringRef getLineStringSection() const override { return lineStringSection; } bool isLittleEndian() const override { return ELFT::TargetEndianness == llvm::support::little; } - llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec, - uint64_t Pos) const override; + llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &sec, + uint64_t pos) const override; private: template <class RelTy> - llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec, - uint64_t Pos, - ArrayRef<RelTy> Rels) const; - - LLDDWARFSection GnuPubNamesSection; - LLDDWARFSection GnuPubTypesSection; - LLDDWARFSection InfoSection; - LLDDWARFSection RangeSection; - LLDDWARFSection RngListsSection; - LLDDWARFSection LineSection; - LLDDWARFSection AddrSection; - StringRef AbbrevSection; - StringRef StrSection; - StringRef LineStringSection; + llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &sec, + uint64_t pos, + ArrayRef<RelTy> rels) const; + + LLDDWARFSection gnuPubNamesSection; + LLDDWARFSection gnuPubTypesSection; + LLDDWARFSection infoSection; + LLDDWARFSection rangeSection; + LLDDWARFSection rngListsSection; + LLDDWARFSection lineSection; + LLDDWARFSection addrSection; + StringRef abbrevSection; + StringRef strSection; + StringRef lineStringSection; }; } // namespace elf diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index 13b6119e2dc9..fbfc71d22b7e 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -1,9 +1,8 @@ //===- Driver.cpp ---------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -25,7 +24,6 @@ #include "Driver.h" #include "Config.h" -#include "Filesystem.h" #include "ICF.h" #include "InputFiles.h" #include "InputSection.h" @@ -41,6 +39,7 @@ #include "lld/Common/Args.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" @@ -51,6 +50,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/GlobPattern.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" @@ -68,44 +68,49 @@ using namespace llvm::support; using namespace lld; using namespace lld::elf; -Configuration *elf::Config; -LinkerDriver *elf::Driver; +Configuration *elf::config; +LinkerDriver *elf::driver; -static void setConfigs(opt::InputArgList &Args); +static void setConfigs(opt::InputArgList &args); +static void readConfigs(opt::InputArgList &args); -bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly, - raw_ostream &Error) { - errorHandler().LogName = args::getFilenameWithoutExe(Args[0]); - errorHandler().ErrorLimitExceededMsg = +bool elf::link(ArrayRef<const char *> args, bool canExitEarly, + raw_ostream &error) { + errorHandler().logName = args::getFilenameWithoutExe(args[0]); + errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; - errorHandler().ErrorOS = &Error; - errorHandler().ExitEarly = CanExitEarly; - errorHandler().ColorDiagnostics = Error.has_colors(); + errorHandler().errorOS = &error; + errorHandler().exitEarly = canExitEarly; + errorHandler().colorDiagnostics = error.has_colors(); + + inputSections.clear(); + outputSections.clear(); + binaryFiles.clear(); + bitcodeFiles.clear(); + objectFiles.clear(); + sharedFiles.clear(); + + config = make<Configuration>(); + driver = make<LinkerDriver>(); + script = make<LinkerScript>(); + symtab = make<SymbolTable>(); - InputSections.clear(); - OutputSections.clear(); - BinaryFiles.clear(); - BitcodeFiles.clear(); - ObjectFiles.clear(); - SharedFiles.clear(); + tar = nullptr; + memset(&in, 0, sizeof(in)); - Config = make<Configuration>(); - Driver = make<LinkerDriver>(); - Script = make<LinkerScript>(); - Symtab = make<SymbolTable>(); + partitions = {Partition()}; - Tar = nullptr; - memset(&In, 0, sizeof(In)); + SharedFile::vernauxNum = 0; - Config->ProgName = Args[0]; + config->progName = args[0]; - Driver->main(Args); + driver->main(args); // Exit immediately if we don't need to return to the caller. // This saves time because the overhead of calling destructors // for all globally-allocated objects is not negligible. - if (CanExitEarly) + if (canExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); @@ -113,16 +118,16 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly, } // Parses a linker -m option. -static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) { - uint8_t OSABI = 0; - StringRef S = Emul; - if (S.endswith("_fbsd")) { - S = S.drop_back(5); - OSABI = ELFOSABI_FREEBSD; +static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef emul) { + uint8_t osabi = 0; + StringRef s = emul; + if (s.endswith("_fbsd")) { + s = s.drop_back(5); + osabi = ELFOSABI_FREEBSD; } - std::pair<ELFKind, uint16_t> Ret = - StringSwitch<std::pair<ELFKind, uint16_t>>(S) + std::pair<ELFKind, uint16_t> ret = + StringSwitch<std::pair<ELFKind, uint16_t>>(s) .Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec", {ELF64LEKind, EM_AARCH64}) .Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM}) @@ -130,7 +135,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) { .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32lriscv", {ELF32LEKind, EM_RISCV}) - .Case("elf32ppc", {ELF32BEKind, EM_PPC}) + .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64lriscv", {ELF64LEKind, EM_RISCV}) @@ -141,91 +146,101 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) { .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) .Default({ELFNoneKind, EM_NONE}); - if (Ret.first == ELFNoneKind) - error("unknown emulation: " + Emul); - return std::make_tuple(Ret.first, Ret.second, OSABI); + if (ret.first == ELFNoneKind) + error("unknown emulation: " + emul); + return std::make_tuple(ret.first, ret.second, osabi); } // Returns slices of MB by parsing MB as an archive file. // Each slice consists of a member file in the archive. std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers( - MemoryBufferRef MB) { - std::unique_ptr<Archive> File = - CHECK(Archive::create(MB), - MB.getBufferIdentifier() + ": failed to parse archive"); - - std::vector<std::pair<MemoryBufferRef, uint64_t>> V; - Error Err = Error::success(); - bool AddToTar = File->isThin() && Tar; - for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) { - Archive::Child C = - CHECK(COrErr, MB.getBufferIdentifier() + + MemoryBufferRef mb) { + std::unique_ptr<Archive> file = + CHECK(Archive::create(mb), + mb.getBufferIdentifier() + ": failed to parse archive"); + + std::vector<std::pair<MemoryBufferRef, uint64_t>> v; + Error err = Error::success(); + bool addToTar = file->isThin() && tar; + for (const ErrorOr<Archive::Child> &cOrErr : file->children(err)) { + Archive::Child c = + CHECK(cOrErr, mb.getBufferIdentifier() + ": could not get the child of the archive"); - MemoryBufferRef MBRef = - CHECK(C.getMemoryBufferRef(), - MB.getBufferIdentifier() + + MemoryBufferRef mbref = + CHECK(c.getMemoryBufferRef(), + mb.getBufferIdentifier() + ": could not get the buffer for a child of the archive"); - if (AddToTar) - Tar->append(relativeToRoot(check(C.getFullName())), MBRef.getBuffer()); - V.push_back(std::make_pair(MBRef, C.getChildOffset())); + if (addToTar) + tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer()); + v.push_back(std::make_pair(mbref, c.getChildOffset())); } - if (Err) - fatal(MB.getBufferIdentifier() + ": Archive::children failed: " + - toString(std::move(Err))); + if (err) + fatal(mb.getBufferIdentifier() + ": Archive::children failed: " + + toString(std::move(err))); // Take ownership of memory buffers created for members of thin archives. - for (std::unique_ptr<MemoryBuffer> &MB : File->takeThinBuffers()) - make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); + for (std::unique_ptr<MemoryBuffer> &mb : file->takeThinBuffers()) + make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); - return V; + return v; } // Opens a file and create a file object. Path has to be resolved already. -void LinkerDriver::addFile(StringRef Path, bool WithLOption) { +void LinkerDriver::addFile(StringRef path, bool withLOption) { using namespace sys::fs; - Optional<MemoryBufferRef> Buffer = readFile(Path); - if (!Buffer.hasValue()) + Optional<MemoryBufferRef> buffer = readFile(path); + if (!buffer.hasValue()) return; - MemoryBufferRef MBRef = *Buffer; + MemoryBufferRef mbref = *buffer; - if (Config->FormatBinary) { - Files.push_back(make<BinaryFile>(MBRef)); + if (config->formatBinary) { + files.push_back(make<BinaryFile>(mbref)); return; } - switch (identify_magic(MBRef.getBuffer())) { + switch (identify_magic(mbref.getBuffer())) { case file_magic::unknown: - readLinkerScript(MBRef); + readLinkerScript(mbref); return; case file_magic::archive: { // Handle -whole-archive. - if (InWholeArchive) { - for (const auto &P : getArchiveMembers(MBRef)) - Files.push_back(createObjectFile(P.first, Path, P.second)); + if (inWholeArchive) { + for (const auto &p : getArchiveMembers(mbref)) + files.push_back(createObjectFile(p.first, path, p.second)); return; } - std::unique_ptr<Archive> File = - CHECK(Archive::create(MBRef), Path + ": failed to parse archive"); + std::unique_ptr<Archive> file = + CHECK(Archive::create(mbref), path + ": failed to parse archive"); // If an archive file has no symbol table, it is likely that a user // is attempting LTO and using a default ar command that doesn't // understand the LLVM bitcode file. It is a pretty common error, so // we'll handle it as if it had a symbol table. - if (!File->isEmpty() && !File->hasSymbolTable()) { - for (const auto &P : getArchiveMembers(MBRef)) - Files.push_back(make<LazyObjFile>(P.first, Path, P.second)); + if (!file->isEmpty() && !file->hasSymbolTable()) { + // Check if all members are bitcode files. If not, ignore, which is the + // default action without the LTO hack described above. + for (const std::pair<MemoryBufferRef, uint64_t> &p : + getArchiveMembers(mbref)) + if (identify_magic(p.first.getBuffer()) != file_magic::bitcode) { + error(path + ": archive has no index; run ranlib to add one"); + return; + } + + for (const std::pair<MemoryBufferRef, uint64_t> &p : + getArchiveMembers(mbref)) + files.push_back(make<LazyObjFile>(p.first, path, p.second)); return; } // Handle the regular case. - Files.push_back(make<ArchiveFile>(std::move(File))); + files.push_back(make<ArchiveFile>(std::move(file))); return; } case file_magic::elf_shared_object: - if (Config->Static || Config->Relocatable) { - error("attempted static link of dynamic object " + Path); + if (config->isStatic || config->relocatable) { + error("attempted static link of dynamic object " + path); return; } @@ -239,27 +254,27 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) { // If a file was specified by -lfoo, the directory part is not // significant, as a user did not specify it. This behavior is // compatible with GNU. - Files.push_back( - createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path)); + files.push_back( + make<SharedFile>(mbref, withLOption ? path::filename(path) : path)); return; case file_magic::bitcode: case file_magic::elf_relocatable: - if (InLib) - Files.push_back(make<LazyObjFile>(MBRef, "", 0)); + if (inLib) + files.push_back(make<LazyObjFile>(mbref, "", 0)); else - Files.push_back(createObjectFile(MBRef)); + files.push_back(createObjectFile(mbref)); break; default: - error(Path + ": unknown file type"); + error(path + ": unknown file type"); } } // Add a given library by searching it from input search paths. -void LinkerDriver::addLibrary(StringRef Name) { - if (Optional<std::string> Path = searchLibrary(Name)) - addFile(*Path, /*WithLOption=*/true); +void LinkerDriver::addLibrary(StringRef name) { + if (Optional<std::string> path = searchLibrary(name)) + addFile(*path, /*withLOption=*/true); else - error("unable to find library -l" + Name); + error("unable to find library -l" + name); } // This function is called on startup. We need this for LTO since @@ -278,102 +293,117 @@ static void initLLVM() { static void checkOptions() { // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup // table which is a relatively new feature. - if (Config->EMachine == EM_MIPS && Config->GnuHash) + if (config->emachine == EM_MIPS && config->gnuHash) error("the .gnu.hash section is not compatible with the MIPS target"); - if (Config->FixCortexA53Errata843419 && Config->EMachine != EM_AARCH64) + if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64) error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); - if (Config->TocOptimize && Config->EMachine != EM_PPC64) + if (config->tocOptimize && config->emachine != EM_PPC64) error("--toc-optimize is only supported on the PowerPC64 target"); - if (Config->Pie && Config->Shared) + if (config->pie && config->shared) error("-shared and -pie may not be used together"); - if (!Config->Shared && !Config->FilterList.empty()) + if (!config->shared && !config->filterList.empty()) error("-F may not be used without -shared"); - if (!Config->Shared && !Config->AuxiliaryList.empty()) + if (!config->shared && !config->auxiliaryList.empty()) error("-f may not be used without -shared"); - if (!Config->Relocatable && !Config->DefineCommon) + if (!config->relocatable && !config->defineCommon) error("-no-define-common not supported in non relocatable output"); - if (Config->Relocatable) { - if (Config->Shared) + if (config->zText && config->zIfuncNoplt) + error("-z text and -z ifunc-noplt may not be used together"); + + if (config->relocatable) { + if (config->shared) error("-r and -shared may not be used together"); - if (Config->GcSections) + if (config->gcSections) error("-r and --gc-sections may not be used together"); - if (Config->GdbIndex) + if (config->gdbIndex) error("-r and --gdb-index may not be used together"); - if (Config->ICF != ICFLevel::None) + if (config->icf != ICFLevel::None) error("-r and --icf may not be used together"); - if (Config->Pie) + if (config->pie) error("-r and -pie may not be used together"); } - if (Config->ExecuteOnly) { - if (Config->EMachine != EM_AARCH64) + if (config->executeOnly) { + if (config->emachine != EM_AARCH64) error("-execute-only is only supported on AArch64 targets"); - if (Config->SingleRoRx && !Script->HasSectionsCommand) + if (config->singleRoRx && !script->hasSectionsCommand) error("-execute-only and -no-rosegment cannot be used together"); } + + if (config->zRetpolineplt && config->requireCET) + error("--require-cet may not be used with -z retpolineplt"); + + if (config->emachine != EM_AARCH64) { + if (config->pacPlt) + error("--pac-plt only supported on AArch64"); + if (config->forceBTI) + error("--force-bti only supported on AArch64"); + } } -static const char *getReproduceOption(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_reproduce)) - return Arg->getValue(); +static const char *getReproduceOption(opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_reproduce)) + return arg->getValue(); return getenv("LLD_REPRODUCE"); } -static bool hasZOption(opt::InputArgList &Args, StringRef Key) { - for (auto *Arg : Args.filtered(OPT_z)) - if (Key == Arg->getValue()) +static bool hasZOption(opt::InputArgList &args, StringRef key) { + for (auto *arg : args.filtered(OPT_z)) + if (key == arg->getValue()) return true; return false; } -static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2, +static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2, bool Default) { - for (auto *Arg : Args.filtered_reverse(OPT_z)) { - if (K1 == Arg->getValue()) + for (auto *arg : args.filtered_reverse(OPT_z)) { + if (k1 == arg->getValue()) return true; - if (K2 == Arg->getValue()) + if (k2 == arg->getValue()) return false; } return Default; } -static bool isKnownZFlag(StringRef S) { - return S == "combreloc" || S == "copyreloc" || S == "defs" || - S == "execstack" || S == "global" || S == "hazardplt" || - S == "initfirst" || S == "interpose" || - S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" || - S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" || - S == "nodelete" || S == "nodlopen" || S == "noexecstack" || - S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" || - S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" || - S == "rodynamic" || S == "text" || S == "wxneeded" || - S.startswith("max-page-size=") || S.startswith("stack-size="); +static bool isKnownZFlag(StringRef s) { + return s == "combreloc" || s == "copyreloc" || s == "defs" || + s == "execstack" || s == "global" || s == "hazardplt" || + s == "ifunc-noplt" || s == "initfirst" || s == "interpose" || + s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" || + s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" || + s == "nodelete" || s == "nodlopen" || s == "noexecstack" || + s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" || + s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" || + s == "rodynamic" || s == "text" || s == "wxneeded" || + s.startswith("common-page-size") || s.startswith("max-page-size=") || + s.startswith("stack-size="); } // Report an error for an unknown -z option. -static void checkZOptions(opt::InputArgList &Args) { - for (auto *Arg : Args.filtered(OPT_z)) - if (!isKnownZFlag(Arg->getValue())) - error("unknown -z value: " + StringRef(Arg->getValue())); +static void checkZOptions(opt::InputArgList &args) { + for (auto *arg : args.filtered(OPT_z)) + if (!isKnownZFlag(arg->getValue())) + error("unknown -z value: " + StringRef(arg->getValue())); } -void LinkerDriver::main(ArrayRef<const char *> ArgsArr) { - ELFOptTable Parser; - opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); +void LinkerDriver::main(ArrayRef<const char *> argsArr) { + ELFOptTable parser; + opt::InputArgList args = parser.parse(argsArr.slice(1)); // Interpret this flag early because error() depends on them. - errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20); + errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20); + checkZOptions(args); // Handle -help - if (Args.hasArg(OPT_help)) { + if (args.hasArg(OPT_help)) { printHelp(); return; } @@ -393,213 +423,218 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) { // lot of "configure" scripts out there that are generated by old version // of Libtool. We cannot convince every software developer to migrate to // the latest version and re-generate scripts. So we have this hack. - if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version)) + if (args.hasArg(OPT_v) || args.hasArg(OPT_version)) message(getLLDVersion() + " (compatible with GNU linkers)"); - if (const char *Path = getReproduceOption(Args)) { + if (const char *path = getReproduceOption(args)) { // Note that --reproduce is a debug option so you can ignore it // if you are trying to understand the whole picture of the code. - Expected<std::unique_ptr<TarWriter>> ErrOrWriter = - TarWriter::create(Path, path::stem(Path)); - if (ErrOrWriter) { - Tar = std::move(*ErrOrWriter); - Tar->append("response.txt", createResponseFile(Args)); - Tar->append("version.txt", getLLDVersion() + "\n"); + Expected<std::unique_ptr<TarWriter>> errOrWriter = + TarWriter::create(path, path::stem(path)); + if (errOrWriter) { + tar = std::move(*errOrWriter); + tar->append("response.txt", createResponseFile(args)); + tar->append("version.txt", getLLDVersion() + "\n"); } else { - error("--reproduce: " + toString(ErrOrWriter.takeError())); + error("--reproduce: " + toString(errOrWriter.takeError())); } } - readConfigs(Args); - checkZOptions(Args); + readConfigs(args); // The behavior of -v or --version is a bit strange, but this is // needed for compatibility with GNU linkers. - if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT)) + if (args.hasArg(OPT_v) && !args.hasArg(OPT_INPUT)) return; - if (Args.hasArg(OPT_version)) + if (args.hasArg(OPT_version)) return; initLLVM(); - createFiles(Args); + createFiles(args); if (errorCount()) return; inferMachineType(); - setConfigs(Args); + setConfigs(args); checkOptions(); if (errorCount()) return; - switch (Config->EKind) { + // The Target instance handles target-specific stuff, such as applying + // relocations or writing a PLT section. It also contains target-dependent + // values such as a default image base address. + target = getTarget(); + + switch (config->ekind) { case ELF32LEKind: - link<ELF32LE>(Args); + link<ELF32LE>(args); return; case ELF32BEKind: - link<ELF32BE>(Args); + link<ELF32BE>(args); return; case ELF64LEKind: - link<ELF64LE>(Args); + link<ELF64LE>(args); return; case ELF64BEKind: - link<ELF64BE>(Args); + link<ELF64BE>(args); return; default: llvm_unreachable("unknown Config->EKind"); } } -static std::string getRpath(opt::InputArgList &Args) { - std::vector<StringRef> V = args::getStrings(Args, OPT_rpath); - return llvm::join(V.begin(), V.end(), ":"); +static std::string getRpath(opt::InputArgList &args) { + std::vector<StringRef> v = args::getStrings(args, OPT_rpath); + return llvm::join(v.begin(), v.end(), ":"); } // Determines what we should do if there are remaining unresolved // symbols after the name resolution. -static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) { - UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols, +static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { + UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols, OPT_warn_unresolved_symbols, true) ? UnresolvedPolicy::ReportError : UnresolvedPolicy::Warn; // Process the last of -unresolved-symbols, -no-undefined or -z defs. - for (auto *Arg : llvm::reverse(Args)) { - switch (Arg->getOption().getID()) { + for (auto *arg : llvm::reverse(args)) { + switch (arg->getOption().getID()) { case OPT_unresolved_symbols: { - StringRef S = Arg->getValue(); - if (S == "ignore-all" || S == "ignore-in-object-files") + StringRef s = arg->getValue(); + if (s == "ignore-all" || s == "ignore-in-object-files") return UnresolvedPolicy::Ignore; - if (S == "ignore-in-shared-libs" || S == "report-all") - return ErrorOrWarn; - error("unknown --unresolved-symbols value: " + S); + if (s == "ignore-in-shared-libs" || s == "report-all") + return errorOrWarn; + error("unknown --unresolved-symbols value: " + s); continue; } case OPT_no_undefined: - return ErrorOrWarn; + return errorOrWarn; case OPT_z: - if (StringRef(Arg->getValue()) == "defs") - return ErrorOrWarn; + if (StringRef(arg->getValue()) == "defs") + return errorOrWarn; continue; } } // -shared implies -unresolved-symbols=ignore-all because missing // symbols are likely to be resolved at runtime using other DSOs. - if (Config->Shared) + if (config->shared) return UnresolvedPolicy::Ignore; - return ErrorOrWarn; + return errorOrWarn; } -static Target2Policy getTarget2(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_target2, "got-rel"); - if (S == "rel") +static Target2Policy getTarget2(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_target2, "got-rel"); + if (s == "rel") return Target2Policy::Rel; - if (S == "abs") + if (s == "abs") return Target2Policy::Abs; - if (S == "got-rel") + if (s == "got-rel") return Target2Policy::GotRel; - error("unknown --target2 option: " + S); + error("unknown --target2 option: " + s); return Target2Policy::GotRel; } -static bool isOutputFormatBinary(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_oformat, "elf"); - if (S == "binary") +static bool isOutputFormatBinary(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_oformat, "elf"); + if (s == "binary") return true; - if (!S.startswith("elf")) - error("unknown --oformat value: " + S); + if (!s.startswith("elf")) + error("unknown --oformat value: " + s); return false; } -static DiscardPolicy getDiscard(opt::InputArgList &Args) { - if (Args.hasArg(OPT_relocatable)) +static DiscardPolicy getDiscard(opt::InputArgList &args) { + if (args.hasArg(OPT_relocatable)) return DiscardPolicy::None; - auto *Arg = - Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); - if (!Arg) + auto *arg = + args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); + if (!arg) return DiscardPolicy::Default; - if (Arg->getOption().getID() == OPT_discard_all) + if (arg->getOption().getID() == OPT_discard_all) return DiscardPolicy::All; - if (Arg->getOption().getID() == OPT_discard_locals) + if (arg->getOption().getID() == OPT_discard_locals) return DiscardPolicy::Locals; return DiscardPolicy::None; } -static StringRef getDynamicLinker(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); - if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker) +static StringRef getDynamicLinker(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); + if (!arg || arg->getOption().getID() == OPT_no_dynamic_linker) return ""; - return Arg->getValue(); + return arg->getValue(); } -static ICFLevel getICF(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); - if (!Arg || Arg->getOption().getID() == OPT_icf_none) +static ICFLevel getICF(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); + if (!arg || arg->getOption().getID() == OPT_icf_none) return ICFLevel::None; - if (Arg->getOption().getID() == OPT_icf_safe) + if (arg->getOption().getID() == OPT_icf_safe) return ICFLevel::Safe; return ICFLevel::All; } -static StripPolicy getStrip(opt::InputArgList &Args) { - if (Args.hasArg(OPT_relocatable)) +static StripPolicy getStrip(opt::InputArgList &args) { + if (args.hasArg(OPT_relocatable)) return StripPolicy::None; - auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug); - if (!Arg) + auto *arg = args.getLastArg(OPT_strip_all, OPT_strip_debug); + if (!arg) return StripPolicy::None; - if (Arg->getOption().getID() == OPT_strip_all) + if (arg->getOption().getID() == OPT_strip_all) return StripPolicy::All; return StripPolicy::Debug; } -static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) { - uint64_t VA = 0; - if (S.startswith("0x")) - S = S.drop_front(2); - if (!to_integer(S, VA, 16)) - error("invalid argument: " + toString(Arg)); - return VA; +static uint64_t parseSectionAddress(StringRef s, opt::InputArgList &args, + const opt::Arg &arg) { + uint64_t va = 0; + if (s.startswith("0x")) + s = s.drop_front(2); + if (!to_integer(s, va, 16)) + error("invalid argument: " + arg.getAsString(args)); + return va; } -static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) { - StringMap<uint64_t> Ret; - for (auto *Arg : Args.filtered(OPT_section_start)) { - StringRef Name; - StringRef Addr; - std::tie(Name, Addr) = StringRef(Arg->getValue()).split('='); - Ret[Name] = parseSectionAddress(Addr, *Arg); +static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &args) { + StringMap<uint64_t> ret; + for (auto *arg : args.filtered(OPT_section_start)) { + StringRef name; + StringRef addr; + std::tie(name, addr) = StringRef(arg->getValue()).split('='); + ret[name] = parseSectionAddress(addr, args, *arg); } - if (auto *Arg = Args.getLastArg(OPT_Ttext)) - Ret[".text"] = parseSectionAddress(Arg->getValue(), *Arg); - if (auto *Arg = Args.getLastArg(OPT_Tdata)) - Ret[".data"] = parseSectionAddress(Arg->getValue(), *Arg); - if (auto *Arg = Args.getLastArg(OPT_Tbss)) - Ret[".bss"] = parseSectionAddress(Arg->getValue(), *Arg); - return Ret; + if (auto *arg = args.getLastArg(OPT_Ttext)) + ret[".text"] = parseSectionAddress(arg->getValue(), args, *arg); + if (auto *arg = args.getLastArg(OPT_Tdata)) + ret[".data"] = parseSectionAddress(arg->getValue(), args, *arg); + if (auto *arg = args.getLastArg(OPT_Tbss)) + ret[".bss"] = parseSectionAddress(arg->getValue(), args, *arg); + return ret; } -static SortSectionPolicy getSortSection(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_sort_section); - if (S == "alignment") +static SortSectionPolicy getSortSection(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_sort_section); + if (s == "alignment") return SortSectionPolicy::Alignment; - if (S == "name") + if (s == "name") return SortSectionPolicy::Name; - if (!S.empty()) - error("unknown --sort-section rule: " + S); + if (!s.empty()) + error("unknown --sort-section rule: " + s); return SortSectionPolicy::Default; } -static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_orphan_handling, "place"); - if (S == "warn") +static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_orphan_handling, "place"); + if (s == "warn") return OrphanHandlingPolicy::Warn; - if (S == "error") + if (s == "error") return OrphanHandlingPolicy::Error; - if (S != "place") - error("unknown --orphan-handling mode: " + S); + if (s != "place") + error("unknown --orphan-handling mode: " + s); return OrphanHandlingPolicy::Place; } @@ -607,388 +642,412 @@ static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) { // synonym for "sha1" because all our hash functions including // -build-id=sha1 are actually tree hashes for performance reasons. static std::pair<BuildIdKind, std::vector<uint8_t>> -getBuildId(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_build_id, OPT_build_id_eq); - if (!Arg) +getBuildId(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_build_id, OPT_build_id_eq); + if (!arg) return {BuildIdKind::None, {}}; - if (Arg->getOption().getID() == OPT_build_id) + if (arg->getOption().getID() == OPT_build_id) return {BuildIdKind::Fast, {}}; - StringRef S = Arg->getValue(); - if (S == "fast") + StringRef s = arg->getValue(); + if (s == "fast") return {BuildIdKind::Fast, {}}; - if (S == "md5") + if (s == "md5") return {BuildIdKind::Md5, {}}; - if (S == "sha1" || S == "tree") + if (s == "sha1" || s == "tree") return {BuildIdKind::Sha1, {}}; - if (S == "uuid") + if (s == "uuid") return {BuildIdKind::Uuid, {}}; - if (S.startswith("0x")) - return {BuildIdKind::Hexstring, parseHex(S.substr(2))}; + if (s.startswith("0x")) + return {BuildIdKind::Hexstring, parseHex(s.substr(2))}; - if (S != "none") - error("unknown --build-id style: " + S); + if (s != "none") + error("unknown --build-id style: " + s); return {BuildIdKind::None, {}}; } -static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_pack_dyn_relocs, "none"); - if (S == "android") +static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_pack_dyn_relocs, "none"); + if (s == "android") return {true, false}; - if (S == "relr") + if (s == "relr") return {false, true}; - if (S == "android+relr") + if (s == "android+relr") return {true, true}; - if (S != "none") - error("unknown -pack-dyn-relocs format: " + S); + if (s != "none") + error("unknown -pack-dyn-relocs format: " + s); return {false, false}; } -static void readCallGraph(MemoryBufferRef MB) { +static void readCallGraph(MemoryBufferRef mb) { // Build a map from symbol name to section - DenseMap<StringRef, Symbol *> Map; - for (InputFile *File : ObjectFiles) - for (Symbol *Sym : File->getSymbols()) - Map[Sym->getName()] = Sym; - - auto FindSection = [&](StringRef Name) -> InputSectionBase * { - Symbol *Sym = Map.lookup(Name); - if (!Sym) { - if (Config->WarnSymbolOrdering) - warn(MB.getBufferIdentifier() + ": no such symbol: " + Name); + DenseMap<StringRef, Symbol *> map; + for (InputFile *file : objectFiles) + for (Symbol *sym : file->getSymbols()) + map[sym->getName()] = sym; + + auto findSection = [&](StringRef name) -> InputSectionBase * { + Symbol *sym = map.lookup(name); + if (!sym) { + if (config->warnSymbolOrdering) + warn(mb.getBufferIdentifier() + ": no such symbol: " + name); return nullptr; } - maybeWarnUnorderableSymbol(Sym); + maybeWarnUnorderableSymbol(sym); - if (Defined *DR = dyn_cast_or_null<Defined>(Sym)) - return dyn_cast_or_null<InputSectionBase>(DR->Section); + if (Defined *dr = dyn_cast_or_null<Defined>(sym)) + return dyn_cast_or_null<InputSectionBase>(dr->section); return nullptr; }; - for (StringRef Line : args::getLines(MB)) { - SmallVector<StringRef, 3> Fields; - Line.split(Fields, ' '); - uint64_t Count; + for (StringRef line : args::getLines(mb)) { + SmallVector<StringRef, 3> fields; + line.split(fields, ' '); + uint64_t count; - if (Fields.size() != 3 || !to_integer(Fields[2], Count)) { - error(MB.getBufferIdentifier() + ": parse error"); + if (fields.size() != 3 || !to_integer(fields[2], count)) { + error(mb.getBufferIdentifier() + ": parse error"); return; } - if (InputSectionBase *From = FindSection(Fields[0])) - if (InputSectionBase *To = FindSection(Fields[1])) - Config->CallGraphProfile[std::make_pair(From, To)] += Count; + if (InputSectionBase *from = findSection(fields[0])) + if (InputSectionBase *to = findSection(fields[1])) + config->callGraphProfile[std::make_pair(from, to)] += count; } } template <class ELFT> static void readCallGraphsFromObjectFiles() { - for (auto File : ObjectFiles) { - auto *Obj = cast<ObjFile<ELFT>>(File); + for (auto file : objectFiles) { + auto *obj = cast<ObjFile<ELFT>>(file); - for (const Elf_CGProfile_Impl<ELFT> &CGPE : Obj->CGProfile) { - auto *FromSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_from)); - auto *ToSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_to)); - if (!FromSym || !ToSym) + for (const Elf_CGProfile_Impl<ELFT> &cgpe : obj->cgProfile) { + auto *fromSym = dyn_cast<Defined>(&obj->getSymbol(cgpe.cgp_from)); + auto *toSym = dyn_cast<Defined>(&obj->getSymbol(cgpe.cgp_to)); + if (!fromSym || !toSym) continue; - auto *From = dyn_cast_or_null<InputSectionBase>(FromSym->Section); - auto *To = dyn_cast_or_null<InputSectionBase>(ToSym->Section); - if (From && To) - Config->CallGraphProfile[{From, To}] += CGPE.cgp_weight; + auto *from = dyn_cast_or_null<InputSectionBase>(fromSym->section); + auto *to = dyn_cast_or_null<InputSectionBase>(toSym->section); + if (from && to) + config->callGraphProfile[{from, to}] += cgpe.cgp_weight; } } } -static bool getCompressDebugSections(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none"); - if (S == "none") +static bool getCompressDebugSections(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_compress_debug_sections, "none"); + if (s == "none") return false; - if (S != "zlib") - error("unknown --compress-debug-sections value: " + S); + if (s != "zlib") + error("unknown --compress-debug-sections value: " + s); if (!zlib::isAvailable()) error("--compress-debug-sections: zlib is not available"); return true; } -static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &Args, - unsigned Id) { - auto *Arg = Args.getLastArg(Id); - if (!Arg) +static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args, + unsigned id) { + auto *arg = args.getLastArg(id); + if (!arg) return {"", ""}; - StringRef S = Arg->getValue(); - std::pair<StringRef, StringRef> Ret = S.split(';'); - if (Ret.second.empty()) - error(Arg->getSpelling() + " expects 'old;new' format, but got " + S); - return Ret; + StringRef s = arg->getValue(); + std::pair<StringRef, StringRef> ret = s.split(';'); + if (ret.second.empty()) + error(arg->getSpelling() + " expects 'old;new' format, but got " + s); + return ret; } // Parse the symbol ordering file and warn for any duplicate entries. -static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef MB) { - SetVector<StringRef> Names; - for (StringRef S : args::getLines(MB)) - if (!Names.insert(S) && Config->WarnSymbolOrdering) - warn(MB.getBufferIdentifier() + ": duplicate ordered symbol: " + S); +static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef mb) { + SetVector<StringRef> names; + for (StringRef s : args::getLines(mb)) + if (!names.insert(s) && config->warnSymbolOrdering) + warn(mb.getBufferIdentifier() + ": duplicate ordered symbol: " + s); - return Names.takeVector(); + return names.takeVector(); } -static void parseClangOption(StringRef Opt, const Twine &Msg) { - std::string Err; - raw_string_ostream OS(Err); +static void parseClangOption(StringRef opt, const Twine &msg) { + std::string err; + raw_string_ostream os(err); - const char *Argv[] = {Config->ProgName.data(), Opt.data()}; - if (cl::ParseCommandLineOptions(2, Argv, "", &OS)) + const char *argv[] = {config->progName.data(), opt.data()}; + if (cl::ParseCommandLineOptions(2, argv, "", &os)) return; - OS.flush(); - error(Msg + ": " + StringRef(Err).trim()); + os.flush(); + error(msg + ": " + StringRef(err).trim()); } // Initializes Config members by the command line options. -void LinkerDriver::readConfigs(opt::InputArgList &Args) { - errorHandler().Verbose = Args.hasArg(OPT_verbose); - errorHandler().FatalWarnings = - Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); - ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true); - - Config->AllowMultipleDefinition = - Args.hasFlag(OPT_allow_multiple_definition, +static void readConfigs(opt::InputArgList &args) { + errorHandler().verbose = args.hasArg(OPT_verbose); + errorHandler().fatalWarnings = + args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); + errorHandler().vsDiagnostics = + args.hasArg(OPT_visual_studio_diagnostics_format, false); + threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true); + + config->allowMultipleDefinition = + args.hasFlag(OPT_allow_multiple_definition, OPT_no_allow_multiple_definition, false) || - hasZOption(Args, "muldefs"); - Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary); - Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); - Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); - Config->CheckSections = - Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true); - Config->Chroot = Args.getLastArgValue(OPT_chroot); - Config->CompressDebugSections = getCompressDebugSections(Args); - Config->Cref = Args.hasFlag(OPT_cref, OPT_no_cref, false); - Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common, - !Args.hasArg(OPT_relocatable)); - Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true); - Config->DisableVerify = Args.hasArg(OPT_disable_verify); - Config->Discard = getDiscard(Args); - Config->DwoDir = Args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq); - Config->DynamicLinker = getDynamicLinker(Args); - Config->EhFrameHdr = - Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); - Config->EmitLLVM = Args.hasArg(OPT_plugin_opt_emit_llvm, false); - Config->EmitRelocs = Args.hasArg(OPT_emit_relocs); - Config->CallGraphProfileSort = Args.hasFlag( + hasZOption(args, "muldefs"); + config->allowShlibUndefined = + args.hasFlag(OPT_allow_shlib_undefined, OPT_no_allow_shlib_undefined, + args.hasArg(OPT_shared)); + config->auxiliaryList = args::getStrings(args, OPT_auxiliary); + config->bsymbolic = args.hasArg(OPT_Bsymbolic); + config->bsymbolicFunctions = args.hasArg(OPT_Bsymbolic_functions); + config->checkSections = + args.hasFlag(OPT_check_sections, OPT_no_check_sections, true); + config->chroot = args.getLastArgValue(OPT_chroot); + config->compressDebugSections = getCompressDebugSections(args); + config->cref = args.hasFlag(OPT_cref, OPT_no_cref, false); + config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common, + !args.hasArg(OPT_relocatable)); + config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true); + config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true); + config->disableVerify = args.hasArg(OPT_disable_verify); + config->discard = getDiscard(args); + config->dwoDir = args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq); + config->dynamicLinker = getDynamicLinker(args); + config->ehFrameHdr = + args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); + config->emitLLVM = args.hasArg(OPT_plugin_opt_emit_llvm, false); + config->emitRelocs = args.hasArg(OPT_emit_relocs); + config->callGraphProfileSort = args.hasFlag( OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true); - Config->EnableNewDtags = - Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true); - Config->Entry = Args.getLastArgValue(OPT_entry); - Config->ExecuteOnly = - Args.hasFlag(OPT_execute_only, OPT_no_execute_only, false); - Config->ExportDynamic = - Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); - Config->FilterList = args::getStrings(Args, OPT_filter); - Config->Fini = Args.getLastArgValue(OPT_fini, "_fini"); - Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419); - Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); - Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); - Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); - Config->ICF = getICF(Args); - Config->IgnoreDataAddressEquality = - Args.hasArg(OPT_ignore_data_address_equality); - Config->IgnoreFunctionAddressEquality = - Args.hasArg(OPT_ignore_function_address_equality); - Config->Init = Args.getLastArgValue(OPT_init, "_init"); - Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline); - Config->LTODebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager); - Config->LTONewPassManager = Args.hasArg(OPT_lto_new_pass_manager); - Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes); - Config->LTOO = args::getInteger(Args, OPT_lto_O, 2); - Config->LTOObjPath = Args.getLastArgValue(OPT_plugin_opt_obj_path_eq); - Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1); - Config->LTOSampleProfile = Args.getLastArgValue(OPT_lto_sample_profile); - Config->MapFile = Args.getLastArgValue(OPT_Map); - Config->MipsGotSize = args::getInteger(Args, OPT_mips_got_size, 0xfff0); - Config->MergeArmExidx = - Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true); - Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec); - Config->Nostdlib = Args.hasArg(OPT_nostdlib); - Config->OFormatBinary = isOutputFormatBinary(Args); - Config->Omagic = Args.hasFlag(OPT_omagic, OPT_no_omagic, false); - Config->OptRemarksFilename = Args.getLastArgValue(OPT_opt_remarks_filename); - Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness); - Config->Optimize = args::getInteger(Args, OPT_O, 1); - Config->OrphanHandling = getOrphanHandling(Args); - Config->OutputFile = Args.getLastArgValue(OPT_o); - Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false); - Config->PrintIcfSections = - Args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); - Config->PrintGcSections = - Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); - Config->Rpath = getRpath(Args); - Config->Relocatable = Args.hasArg(OPT_relocatable); - Config->SaveTemps = Args.hasArg(OPT_save_temps); - Config->SearchPaths = args::getStrings(Args, OPT_library_path); - Config->SectionStartMap = getSectionStartMap(Args); - Config->Shared = Args.hasArg(OPT_shared); - Config->SingleRoRx = Args.hasArg(OPT_no_rosegment); - Config->SoName = Args.getLastArgValue(OPT_soname); - Config->SortSection = getSortSection(Args); - Config->SplitStackAdjustSize = args::getInteger(Args, OPT_split_stack_adjust_size, 16384); - Config->Strip = getStrip(Args); - Config->Sysroot = Args.getLastArgValue(OPT_sysroot); - Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false); - Config->Target2 = getTarget2(Args); - Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir); - Config->ThinLTOCachePolicy = CHECK( - parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)), + config->enableNewDtags = + args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true); + config->entry = args.getLastArgValue(OPT_entry); + config->executeOnly = + args.hasFlag(OPT_execute_only, OPT_no_execute_only, false); + config->exportDynamic = + args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); + config->filterList = args::getStrings(args, OPT_filter); + config->fini = args.getLastArgValue(OPT_fini, "_fini"); + config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419); + config->forceBTI = args.hasArg(OPT_force_bti); + config->requireCET = args.hasArg(OPT_require_cet); + config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); + config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); + config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); + config->icf = getICF(args); + config->ignoreDataAddressEquality = + args.hasArg(OPT_ignore_data_address_equality); + config->ignoreFunctionAddressEquality = + args.hasArg(OPT_ignore_function_address_equality); + config->init = args.getLastArgValue(OPT_init, "_init"); + config->ltoAAPipeline = args.getLastArgValue(OPT_lto_aa_pipeline); + config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate); + config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file); + config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager); + config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager); + config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes); + config->ltoo = args::getInteger(args, OPT_lto_O, 2); + config->ltoObjPath = args.getLastArgValue(OPT_plugin_opt_obj_path_eq); + config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1); + config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile); + config->mapFile = args.getLastArgValue(OPT_Map); + config->mipsGotSize = args::getInteger(args, OPT_mips_got_size, 0xfff0); + config->mergeArmExidx = + args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true); + config->nmagic = args.hasFlag(OPT_nmagic, OPT_no_nmagic, false); + config->noinhibitExec = args.hasArg(OPT_noinhibit_exec); + config->nostdlib = args.hasArg(OPT_nostdlib); + config->oFormatBinary = isOutputFormatBinary(args); + config->omagic = args.hasFlag(OPT_omagic, OPT_no_omagic, false); + config->optRemarksFilename = args.getLastArgValue(OPT_opt_remarks_filename); + config->optRemarksPasses = args.getLastArgValue(OPT_opt_remarks_passes); + config->optRemarksWithHotness = args.hasArg(OPT_opt_remarks_with_hotness); + config->optRemarksFormat = args.getLastArgValue(OPT_opt_remarks_format); + config->optimize = args::getInteger(args, OPT_O, 1); + config->orphanHandling = getOrphanHandling(args); + config->outputFile = args.getLastArgValue(OPT_o); + config->pacPlt = args.hasArg(OPT_pac_plt); + config->pie = args.hasFlag(OPT_pie, OPT_no_pie, false); + config->printIcfSections = + args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); + config->printGcSections = + args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); + config->printSymbolOrder = + args.getLastArgValue(OPT_print_symbol_order); + config->rpath = getRpath(args); + config->relocatable = args.hasArg(OPT_relocatable); + config->saveTemps = args.hasArg(OPT_save_temps); + config->searchPaths = args::getStrings(args, OPT_library_path); + config->sectionStartMap = getSectionStartMap(args); + config->shared = args.hasArg(OPT_shared); + config->singleRoRx = args.hasArg(OPT_no_rosegment); + config->soName = args.getLastArgValue(OPT_soname); + config->sortSection = getSortSection(args); + config->splitStackAdjustSize = args::getInteger(args, OPT_split_stack_adjust_size, 16384); + config->strip = getStrip(args); + config->sysroot = args.getLastArgValue(OPT_sysroot); + config->target1Rel = args.hasFlag(OPT_target1_rel, OPT_target1_abs, false); + config->target2 = getTarget2(args); + config->thinLTOCacheDir = args.getLastArgValue(OPT_thinlto_cache_dir); + config->thinLTOCachePolicy = CHECK( + parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)), "--thinlto-cache-policy: invalid cache policy"); - Config->ThinLTOEmitImportsFiles = - Args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files); - Config->ThinLTOIndexOnly = Args.hasArg(OPT_plugin_opt_thinlto_index_only) || - Args.hasArg(OPT_plugin_opt_thinlto_index_only_eq); - Config->ThinLTOIndexOnlyArg = - Args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq); - Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u); - Config->ThinLTOObjectSuffixReplace = - getOldNewOptions(Args, OPT_plugin_opt_thinlto_object_suffix_replace_eq); - Config->ThinLTOPrefixReplace = - getOldNewOptions(Args, OPT_plugin_opt_thinlto_prefix_replace_eq); - Config->Trace = Args.hasArg(OPT_trace); - Config->Undefined = args::getStrings(Args, OPT_undefined); - Config->UndefinedVersion = - Args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true); - Config->UseAndroidRelrTags = Args.hasFlag( + config->thinLTOEmitImportsFiles = + args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files); + config->thinLTOIndexOnly = args.hasArg(OPT_plugin_opt_thinlto_index_only) || + args.hasArg(OPT_plugin_opt_thinlto_index_only_eq); + config->thinLTOIndexOnlyArg = + args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq); + config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u); + config->thinLTOObjectSuffixReplace = + getOldNewOptions(args, OPT_plugin_opt_thinlto_object_suffix_replace_eq); + config->thinLTOPrefixReplace = + getOldNewOptions(args, OPT_plugin_opt_thinlto_prefix_replace_eq); + config->trace = args.hasArg(OPT_trace); + config->undefined = args::getStrings(args, OPT_undefined); + config->undefinedVersion = + args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true); + config->useAndroidRelrTags = args.hasFlag( OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false); - Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args); - Config->WarnBackrefs = - Args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false); - Config->WarnCommon = Args.hasFlag(OPT_warn_common, OPT_no_warn_common, false); - Config->WarnIfuncTextrel = - Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false); - Config->WarnSymbolOrdering = - Args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); - Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true); - Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true); - Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false); - Config->ZGlobal = hasZOption(Args, "global"); - Config->ZHazardplt = hasZOption(Args, "hazardplt"); - Config->ZInitfirst = hasZOption(Args, "initfirst"); - Config->ZInterpose = hasZOption(Args, "interpose"); - Config->ZKeepTextSectionPrefix = getZFlag( - Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false); - Config->ZNodefaultlib = hasZOption(Args, "nodefaultlib"); - Config->ZNodelete = hasZOption(Args, "nodelete"); - Config->ZNodlopen = hasZOption(Args, "nodlopen"); - Config->ZNow = getZFlag(Args, "now", "lazy", false); - Config->ZOrigin = hasZOption(Args, "origin"); - Config->ZRelro = getZFlag(Args, "relro", "norelro", true); - Config->ZRetpolineplt = hasZOption(Args, "retpolineplt"); - Config->ZRodynamic = hasZOption(Args, "rodynamic"); - Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", 0); - Config->ZText = getZFlag(Args, "text", "notext", true); - Config->ZWxneeded = hasZOption(Args, "wxneeded"); + config->unresolvedSymbols = getUnresolvedSymbolPolicy(args); + config->warnBackrefs = + args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false); + config->warnCommon = args.hasFlag(OPT_warn_common, OPT_no_warn_common, false); + config->warnIfuncTextrel = + args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false); + config->warnSymbolOrdering = + args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); + config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true); + config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true); + config->zExecstack = getZFlag(args, "execstack", "noexecstack", false); + config->zGlobal = hasZOption(args, "global"); + config->zHazardplt = hasZOption(args, "hazardplt"); + config->zIfuncNoplt = hasZOption(args, "ifunc-noplt"); + config->zInitfirst = hasZOption(args, "initfirst"); + config->zInterpose = hasZOption(args, "interpose"); + config->zKeepTextSectionPrefix = getZFlag( + args, "keep-text-section-prefix", "nokeep-text-section-prefix", false); + config->zNodefaultlib = hasZOption(args, "nodefaultlib"); + config->zNodelete = hasZOption(args, "nodelete"); + config->zNodlopen = hasZOption(args, "nodlopen"); + config->zNow = getZFlag(args, "now", "lazy", false); + config->zOrigin = hasZOption(args, "origin"); + config->zRelro = getZFlag(args, "relro", "norelro", true); + config->zRetpolineplt = hasZOption(args, "retpolineplt"); + config->zRodynamic = hasZOption(args, "rodynamic"); + config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0); + config->zText = getZFlag(args, "text", "notext", true); + config->zWxneeded = hasZOption(args, "wxneeded"); // Parse LTO options. - if (auto *Arg = Args.getLastArg(OPT_plugin_opt_mcpu_eq)) - parseClangOption(Saver.save("-mcpu=" + StringRef(Arg->getValue())), - Arg->getSpelling()); + if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq)) + parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), + arg->getSpelling()); - for (auto *Arg : Args.filtered(OPT_plugin_opt)) - parseClangOption(Arg->getValue(), Arg->getSpelling()); + for (auto *arg : args.filtered(OPT_plugin_opt)) + parseClangOption(arg->getValue(), arg->getSpelling()); // Parse -mllvm options. - for (auto *Arg : Args.filtered(OPT_mllvm)) - parseClangOption(Arg->getValue(), Arg->getSpelling()); + for (auto *arg : args.filtered(OPT_mllvm)) + parseClangOption(arg->getValue(), arg->getSpelling()); - if (Config->LTOO > 3) - error("invalid optimization level for LTO: " + Twine(Config->LTOO)); - if (Config->LTOPartitions == 0) + if (config->ltoo > 3) + error("invalid optimization level for LTO: " + Twine(config->ltoo)); + if (config->ltoPartitions == 0) error("--lto-partitions: number of threads must be > 0"); - if (Config->ThinLTOJobs == 0) + if (config->thinLTOJobs == 0) error("--thinlto-jobs: number of threads must be > 0"); - if (Config->SplitStackAdjustSize < 0) + if (config->splitStackAdjustSize < 0) error("--split-stack-adjust-size: size must be >= 0"); // Parse ELF{32,64}{LE,BE} and CPU type. - if (auto *Arg = Args.getLastArg(OPT_m)) { - StringRef S = Arg->getValue(); - std::tie(Config->EKind, Config->EMachine, Config->OSABI) = - parseEmulation(S); - Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32"); - Config->Emulation = S; + if (auto *arg = args.getLastArg(OPT_m)) { + StringRef s = arg->getValue(); + std::tie(config->ekind, config->emachine, config->osabi) = + parseEmulation(s); + config->mipsN32Abi = (s == "elf32btsmipn32" || s == "elf32ltsmipn32"); + config->emulation = s; } // Parse -hash-style={sysv,gnu,both}. - if (auto *Arg = Args.getLastArg(OPT_hash_style)) { - StringRef S = Arg->getValue(); - if (S == "sysv") - Config->SysvHash = true; - else if (S == "gnu") - Config->GnuHash = true; - else if (S == "both") - Config->SysvHash = Config->GnuHash = true; + if (auto *arg = args.getLastArg(OPT_hash_style)) { + StringRef s = arg->getValue(); + if (s == "sysv") + config->sysvHash = true; + else if (s == "gnu") + config->gnuHash = true; + else if (s == "both") + config->sysvHash = config->gnuHash = true; else - error("unknown -hash-style: " + S); + error("unknown -hash-style: " + s); } - if (Args.hasArg(OPT_print_map)) - Config->MapFile = "-"; - - // --omagic is an option to create old-fashioned executables in which - // .text segments are writable. Today, the option is still in use to - // create special-purpose programs such as boot loaders. It doesn't - // make sense to create PT_GNU_RELRO for such executables. - if (Config->Omagic) - Config->ZRelro = false; - - std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args); - - std::tie(Config->AndroidPackDynRelocs, Config->RelrPackDynRelocs) = - getPackDynRelocs(Args); - - if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file)) - if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) - Config->SymbolOrderingFile = getSymbolOrderingFile(*Buffer); + if (args.hasArg(OPT_print_map)) + config->mapFile = "-"; + + // Page alignment can be disabled by the -n (--nmagic) and -N (--omagic). + // As PT_GNU_RELRO relies on Paging, do not create it when we have disabled + // it. + if (config->nmagic || config->omagic) + config->zRelro = false; + + std::tie(config->buildId, config->buildIdVector) = getBuildId(args); + + std::tie(config->androidPackDynRelocs, config->relrPackDynRelocs) = + getPackDynRelocs(args); + + if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){ + if (args.hasArg(OPT_call_graph_ordering_file)) + error("--symbol-ordering-file and --call-graph-order-file " + "may not be used together"); + if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())){ + config->symbolOrderingFile = getSymbolOrderingFile(*buffer); + // Also need to disable CallGraphProfileSort to prevent + // LLD order symbols with CGProfile + config->callGraphProfileSort = false; + } + } // If --retain-symbol-file is used, we'll keep only the symbols listed in // the file and discard all others. - if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) { - Config->DefaultSymbolVersion = VER_NDX_LOCAL; - if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) - for (StringRef S : args::getLines(*Buffer)) - Config->VersionScriptGlobals.push_back( - {S, /*IsExternCpp*/ false, /*HasWildcard*/ false}); + if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) { + config->defaultSymbolVersion = VER_NDX_LOCAL; + if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())) + for (StringRef s : args::getLines(*buffer)) + config->versionScriptGlobals.push_back( + {s, /*IsExternCpp*/ false, /*HasWildcard*/ false}); } - bool HasExportDynamic = - Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); + bool hasExportDynamic = + args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); // Parses -dynamic-list and -export-dynamic-symbol. They make some // symbols private. Note that -export-dynamic takes precedence over them // as it says all symbols should be exported. - if (!HasExportDynamic) { - for (auto *Arg : Args.filtered(OPT_dynamic_list)) - if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) - readDynamicList(*Buffer); - - for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol)) - Config->DynamicList.push_back( - {Arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false}); + if (!hasExportDynamic) { + for (auto *arg : args.filtered(OPT_dynamic_list)) + if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())) + readDynamicList(*buffer); + + for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) + config->dynamicList.push_back( + {arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false}); } // If --export-dynamic-symbol=foo is given and symbol foo is defined in // an object file in an archive file, that object file should be pulled // out and linked. (It doesn't have to behave like that from technical // point of view, but this is needed for compatibility with GNU.) - for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol)) - Config->Undefined.push_back(Arg->getValue()); + for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) + config->undefined.push_back(arg->getValue()); - for (auto *Arg : Args.filtered(OPT_version_script)) - if (Optional<std::string> Path = searchScript(Arg->getValue())) { - if (Optional<MemoryBufferRef> Buffer = readFile(*Path)) - readVersionScript(*Buffer); + for (auto *arg : args.filtered(OPT_version_script)) + if (Optional<std::string> path = searchScript(arg->getValue())) { + if (Optional<MemoryBufferRef> buffer = readFile(*path)) + readVersionScript(*buffer); } else { - error(Twine("cannot find version script ") + Arg->getValue()); + error(Twine("cannot find version script ") + arg->getValue()); } } @@ -996,17 +1055,18 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { // command line options, but computed based on other Config values. // This function initialize such members. See Config.h for the details // of these values. -static void setConfigs(opt::InputArgList &Args) { - ELFKind K = Config->EKind; - uint16_t M = Config->EMachine; - - Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs); - Config->Is64 = (K == ELF64LEKind || K == ELF64BEKind); - Config->IsLE = (K == ELF32LEKind || K == ELF64LEKind); - Config->Endianness = Config->IsLE ? endianness::little : endianness::big; - Config->IsMips64EL = (K == ELF64LEKind && M == EM_MIPS); - Config->Pic = Config->Pie || Config->Shared; - Config->Wordsize = Config->Is64 ? 8 : 4; +static void setConfigs(opt::InputArgList &args) { + ELFKind k = config->ekind; + uint16_t m = config->emachine; + + config->copyRelocs = (config->relocatable || config->emitRelocs); + config->is64 = (k == ELF64LEKind || k == ELF64BEKind); + config->isLE = (k == ELF32LEKind || k == ELF64LEKind); + config->endianness = config->isLE ? endianness::little : endianness::big; + config->isMips64EL = (k == ELF64LEKind && m == EM_MIPS); + config->isPic = config->pie || config->shared; + config->picThunk = args.hasArg(OPT_pic_veneer, config->isPic); + config->wordsize = config->is64 ? 8 : 4; // ELF defines two different ways to store relocation addends as shown below: // @@ -1021,148 +1081,150 @@ static void setConfigs(opt::InputArgList &Args) { // You cannot choose which one, Rel or Rela, you want to use. Instead each // ABI defines which one you need to use. The following expression expresses // that. - Config->IsRela = M == EM_AARCH64 || M == EM_AMDGPU || M == EM_HEXAGON || - M == EM_PPC || M == EM_PPC64 || M == EM_RISCV || - M == EM_X86_64; + config->isRela = m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || + m == EM_PPC || m == EM_PPC64 || m == EM_RISCV || + m == EM_X86_64; // If the output uses REL relocations we must store the dynamic relocation // addends to the output sections. We also store addends for RELA relocations // if --apply-dynamic-relocs is used. // We default to not writing the addends when using RELA relocations since // any standard conforming tool can find it in r_addend. - Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs, + config->writeAddends = args.hasFlag(OPT_apply_dynamic_relocs, OPT_no_apply_dynamic_relocs, false) || - !Config->IsRela; + !config->isRela; - Config->TocOptimize = - Args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, M == EM_PPC64); + config->tocOptimize = + args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, m == EM_PPC64); } // Returns a value of "-format" option. -static bool isFormatBinary(StringRef S) { - if (S == "binary") +static bool isFormatBinary(StringRef s) { + if (s == "binary") return true; - if (S == "elf" || S == "default") + if (s == "elf" || s == "default") return false; - error("unknown -format value: " + S + + error("unknown -format value: " + s + " (supported formats: elf, default, binary)"); return false; } -void LinkerDriver::createFiles(opt::InputArgList &Args) { +void LinkerDriver::createFiles(opt::InputArgList &args) { // For --{push,pop}-state. - std::vector<std::tuple<bool, bool, bool>> Stack; + std::vector<std::tuple<bool, bool, bool>> stack; // Iterate over argv to process input files and positional arguments. - for (auto *Arg : Args) { - switch (Arg->getOption().getUnaliasedOption().getID()) { + for (auto *arg : args) { + switch (arg->getOption().getID()) { case OPT_library: - addLibrary(Arg->getValue()); + addLibrary(arg->getValue()); break; case OPT_INPUT: - addFile(Arg->getValue(), /*WithLOption=*/false); + addFile(arg->getValue(), /*withLOption=*/false); break; case OPT_defsym: { - StringRef From; - StringRef To; - std::tie(From, To) = StringRef(Arg->getValue()).split('='); - if (From.empty() || To.empty()) - error("-defsym: syntax error: " + StringRef(Arg->getValue())); + StringRef from; + StringRef to; + std::tie(from, to) = StringRef(arg->getValue()).split('='); + if (from.empty() || to.empty()) + error("-defsym: syntax error: " + StringRef(arg->getValue())); else - readDefsym(From, MemoryBufferRef(To, "-defsym")); + readDefsym(from, MemoryBufferRef(to, "-defsym")); break; } case OPT_script: - if (Optional<std::string> Path = searchScript(Arg->getValue())) { - if (Optional<MemoryBufferRef> MB = readFile(*Path)) - readLinkerScript(*MB); + if (Optional<std::string> path = searchScript(arg->getValue())) { + if (Optional<MemoryBufferRef> mb = readFile(*path)) + readLinkerScript(*mb); break; } - error(Twine("cannot find linker script ") + Arg->getValue()); + error(Twine("cannot find linker script ") + arg->getValue()); break; case OPT_as_needed: - Config->AsNeeded = true; + config->asNeeded = true; break; case OPT_format: - Config->FormatBinary = isFormatBinary(Arg->getValue()); + config->formatBinary = isFormatBinary(arg->getValue()); break; case OPT_no_as_needed: - Config->AsNeeded = false; + config->asNeeded = false; break; case OPT_Bstatic: - Config->Static = true; + case OPT_omagic: + case OPT_nmagic: + config->isStatic = true; break; case OPT_Bdynamic: - Config->Static = false; + config->isStatic = false; break; case OPT_whole_archive: - InWholeArchive = true; + inWholeArchive = true; break; case OPT_no_whole_archive: - InWholeArchive = false; + inWholeArchive = false; break; case OPT_just_symbols: - if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue())) { - Files.push_back(createObjectFile(*MB)); - Files.back()->JustSymbols = true; + if (Optional<MemoryBufferRef> mb = readFile(arg->getValue())) { + files.push_back(createObjectFile(*mb)); + files.back()->justSymbols = true; } break; case OPT_start_group: - if (InputFile::IsInGroup) + if (InputFile::isInGroup) error("nested --start-group"); - InputFile::IsInGroup = true; + InputFile::isInGroup = true; break; case OPT_end_group: - if (!InputFile::IsInGroup) + if (!InputFile::isInGroup) error("stray --end-group"); - InputFile::IsInGroup = false; - ++InputFile::NextGroupId; + InputFile::isInGroup = false; + ++InputFile::nextGroupId; break; case OPT_start_lib: - if (InLib) + if (inLib) error("nested --start-lib"); - if (InputFile::IsInGroup) + if (InputFile::isInGroup) error("may not nest --start-lib in --start-group"); - InLib = true; - InputFile::IsInGroup = true; + inLib = true; + InputFile::isInGroup = true; break; case OPT_end_lib: - if (!InLib) + if (!inLib) error("stray --end-lib"); - InLib = false; - InputFile::IsInGroup = false; - ++InputFile::NextGroupId; + inLib = false; + InputFile::isInGroup = false; + ++InputFile::nextGroupId; break; case OPT_push_state: - Stack.emplace_back(Config->AsNeeded, Config->Static, InWholeArchive); + stack.emplace_back(config->asNeeded, config->isStatic, inWholeArchive); break; case OPT_pop_state: - if (Stack.empty()) { + if (stack.empty()) { error("unbalanced --push-state/--pop-state"); break; } - std::tie(Config->AsNeeded, Config->Static, InWholeArchive) = Stack.back(); - Stack.pop_back(); + std::tie(config->asNeeded, config->isStatic, inWholeArchive) = stack.back(); + stack.pop_back(); break; } } - if (Files.empty() && errorCount() == 0) + if (files.empty() && errorCount() == 0) error("no input files"); } // If -m <machine_type> was not given, infer it from object files. void LinkerDriver::inferMachineType() { - if (Config->EKind != ELFNoneKind) + if (config->ekind != ELFNoneKind) return; - for (InputFile *F : Files) { - if (F->EKind == ELFNoneKind) + for (InputFile *f : files) { + if (f->ekind == ELFNoneKind) continue; - Config->EKind = F->EKind; - Config->EMachine = F->EMachine; - Config->OSABI = F->OSABI; - Config->MipsN32Abi = Config->EMachine == EM_MIPS && isMipsN32Abi(F); + config->ekind = f->ekind; + config->emachine = f->emachine; + config->osabi = f->osabi; + config->mipsN32Abi = config->emachine == EM_MIPS && isMipsN32Abi(f); return; } error("target emulation unknown: -m or at least one .o file required"); @@ -1170,49 +1232,72 @@ void LinkerDriver::inferMachineType() { // Parse -z max-page-size=<value>. The default value is defined by // each target. -static uint64_t getMaxPageSize(opt::InputArgList &Args) { - uint64_t Val = args::getZOptionValue(Args, OPT_z, "max-page-size", - Target->DefaultMaxPageSize); - if (!isPowerOf2_64(Val)) +static uint64_t getMaxPageSize(opt::InputArgList &args) { + uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size", + target->defaultMaxPageSize); + if (!isPowerOf2_64(val)) error("max-page-size: value isn't a power of 2"); - return Val; + if (config->nmagic || config->omagic) { + if (val != target->defaultMaxPageSize) + warn("-z max-page-size set, but paging disabled by omagic or nmagic"); + return 1; + } + return val; +} + +// Parse -z common-page-size=<value>. The default value is defined by +// each target. +static uint64_t getCommonPageSize(opt::InputArgList &args) { + uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size", + target->defaultCommonPageSize); + if (!isPowerOf2_64(val)) + error("common-page-size: value isn't a power of 2"); + if (config->nmagic || config->omagic) { + if (val != target->defaultCommonPageSize) + warn("-z common-page-size set, but paging disabled by omagic or nmagic"); + return 1; + } + // commonPageSize can't be larger than maxPageSize. + if (val > config->maxPageSize) + val = config->maxPageSize; + return val; } // Parses -image-base option. -static Optional<uint64_t> getImageBase(opt::InputArgList &Args) { - // Because we are using "Config->MaxPageSize" here, this function has to be +static Optional<uint64_t> getImageBase(opt::InputArgList &args) { + // Because we are using "Config->maxPageSize" here, this function has to be // called after the variable is initialized. - auto *Arg = Args.getLastArg(OPT_image_base); - if (!Arg) + auto *arg = args.getLastArg(OPT_image_base); + if (!arg) return None; - StringRef S = Arg->getValue(); - uint64_t V; - if (!to_integer(S, V)) { - error("-image-base: number expected, but got " + S); + StringRef s = arg->getValue(); + uint64_t v; + if (!to_integer(s, v)) { + error("-image-base: number expected, but got " + s); return 0; } - if ((V % Config->MaxPageSize) != 0) - warn("-image-base: address isn't multiple of page size: " + S); - return V; + if ((v % config->maxPageSize) != 0) + warn("-image-base: address isn't multiple of page size: " + s); + return v; } // Parses `--exclude-libs=lib,lib,...`. // The library names may be delimited by commas or colons. -static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) { - DenseSet<StringRef> Ret; - for (auto *Arg : Args.filtered(OPT_exclude_libs)) { - StringRef S = Arg->getValue(); +static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &args) { + DenseSet<StringRef> ret; + for (auto *arg : args.filtered(OPT_exclude_libs)) { + StringRef s = arg->getValue(); for (;;) { - size_t Pos = S.find_first_of(",:"); - if (Pos == StringRef::npos) + size_t pos = s.find_first_of(",:"); + if (pos == StringRef::npos) break; - Ret.insert(S.substr(0, Pos)); - S = S.substr(Pos + 1); + ret.insert(s.substr(0, pos)); + s = s.substr(pos + 1); } - Ret.insert(S); + ret.insert(s); } - return Ret; + return ret; } // Handles the -exclude-libs option. If a static library file is specified @@ -1221,139 +1306,247 @@ static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) { // A special library name "ALL" means all archive files. // // This is not a popular option, but some programs such as bionic libc use it. -template <class ELFT> -static void excludeLibs(opt::InputArgList &Args) { - DenseSet<StringRef> Libs = getExcludeLibs(Args); - bool All = Libs.count("ALL"); - - auto Visit = [&](InputFile *File) { - if (!File->ArchiveName.empty()) - if (All || Libs.count(path::filename(File->ArchiveName))) - for (Symbol *Sym : File->getSymbols()) - if (!Sym->isLocal() && Sym->File == File) - Sym->VersionId = VER_NDX_LOCAL; +static void excludeLibs(opt::InputArgList &args) { + DenseSet<StringRef> libs = getExcludeLibs(args); + bool all = libs.count("ALL"); + + auto visit = [&](InputFile *file) { + if (!file->archiveName.empty()) + if (all || libs.count(path::filename(file->archiveName))) + for (Symbol *sym : file->getSymbols()) + if (!sym->isLocal() && sym->file == file) + sym->versionId = VER_NDX_LOCAL; }; - for (InputFile *File : ObjectFiles) - Visit(File); + for (InputFile *file : objectFiles) + visit(file); - for (BitcodeFile *File : BitcodeFiles) - Visit(File); + for (BitcodeFile *file : bitcodeFiles) + visit(file); } // Force Sym to be entered in the output. Used for -u or equivalent. -template <class ELFT> static void handleUndefined(StringRef Name) { - Symbol *Sym = Symtab->find(Name); - if (!Sym) +static void handleUndefined(Symbol *sym) { + // Since a symbol may not be used inside the program, LTO may + // eliminate it. Mark the symbol as "used" to prevent it. + sym->isUsedInRegularObj = true; + + if (sym->isLazy()) + sym->fetch(); +} + +// As an extention to GNU linkers, lld supports a variant of `-u` +// which accepts wildcard patterns. All symbols that match a given +// pattern are handled as if they were given by `-u`. +static void handleUndefinedGlob(StringRef arg) { + Expected<GlobPattern> pat = GlobPattern::create(arg); + if (!pat) { + error("--undefined-glob: " + toString(pat.takeError())); return; + } - // Since symbol S may not be used inside the program, LTO may - // eliminate it. Mark the symbol as "used" to prevent it. - Sym->IsUsedInRegularObj = true; + std::vector<Symbol *> syms; + symtab->forEachSymbol([&](Symbol *sym) { + // Calling Sym->fetch() from here is not safe because it may + // add new symbols to the symbol table, invalidating the + // current iterator. So we just keep a note. + if (pat->match(sym->getName())) + syms.push_back(sym); + }); - if (Sym->isLazy()) - Symtab->fetchLazy<ELFT>(Sym); + for (Symbol *sym : syms) + handleUndefined(sym); } -template <class ELFT> static void handleLibcall(StringRef Name) { - Symbol *Sym = Symtab->find(Name); - if (!Sym || !Sym->isLazy()) +static void handleLibcall(StringRef name) { + Symbol *sym = symtab->find(name); + if (!sym || !sym->isLazy()) return; - MemoryBufferRef MB; - if (auto *LO = dyn_cast<LazyObject>(Sym)) - MB = LO->File->MB; + MemoryBufferRef mb; + if (auto *lo = dyn_cast<LazyObject>(sym)) + mb = lo->file->mb; else - MB = cast<LazyArchive>(Sym)->getMemberBuffer(); + mb = cast<LazyArchive>(sym)->getMemberBuffer(); - if (isBitcode(MB)) - Symtab->fetchLazy<ELFT>(Sym); + if (isBitcode(mb)) + sym->fetch(); +} + +// Replaces common symbols with defined symbols reside in .bss sections. +// This function is called after all symbol names are resolved. As a +// result, the passes after the symbol resolution won't see any +// symbols of type CommonSymbol. +static void replaceCommonSymbols() { + symtab->forEachSymbol([](Symbol *sym) { + auto *s = dyn_cast<CommonSymbol>(sym); + if (!s) + return; + + auto *bss = make<BssSection>("COMMON", s->size, s->alignment); + bss->file = s->file; + bss->markDead(); + inputSections.push_back(bss); + s->replace(Defined{s->file, s->getName(), s->binding, s->stOther, s->type, + /*value=*/0, s->size, bss}); + }); } // If all references to a DSO happen to be weak, the DSO is not added // to DT_NEEDED. If that happens, we need to eliminate shared symbols // created from the DSO. Otherwise, they become dangling references // that point to a non-existent DSO. -template <class ELFT> static void demoteSharedSymbols() { - for (Symbol *Sym : Symtab->getSymbols()) { - if (auto *S = dyn_cast<SharedSymbol>(Sym)) { - if (!S->getFile<ELFT>().IsNeeded) { - bool Used = S->Used; - replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther, - S->Type); - S->Used = Used; - } - } - } +static void demoteSharedSymbols() { + symtab->forEachSymbol([](Symbol *sym) { + auto *s = dyn_cast<SharedSymbol>(sym); + if (!s || s->getFile().isNeeded) + return; + + bool used = s->used; + s->replace(Undefined{nullptr, s->getName(), STB_WEAK, s->stOther, s->type}); + s->used = used; + }); } -// The section referred to by S is considered address-significant. Set the -// KeepUnique flag on the section if appropriate. -static void markAddrsig(Symbol *S) { - if (auto *D = dyn_cast_or_null<Defined>(S)) - if (D->Section) +// The section referred to by `s` is considered address-significant. Set the +// keepUnique flag on the section if appropriate. +static void markAddrsig(Symbol *s) { + if (auto *d = dyn_cast_or_null<Defined>(s)) + if (d->section) // We don't need to keep text sections unique under --icf=all even if they // are address-significant. - if (Config->ICF == ICFLevel::Safe || !(D->Section->Flags & SHF_EXECINSTR)) - D->Section->KeepUnique = true; + if (config->icf == ICFLevel::Safe || !(d->section->flags & SHF_EXECINSTR)) + d->section->keepUnique = true; } // Record sections that define symbols mentioned in --keep-unique <symbol> // and symbols referred to by address-significance tables. These sections are // ineligible for ICF. template <class ELFT> -static void findKeepUniqueSections(opt::InputArgList &Args) { - for (auto *Arg : Args.filtered(OPT_keep_unique)) { - StringRef Name = Arg->getValue(); - auto *D = dyn_cast_or_null<Defined>(Symtab->find(Name)); - if (!D || !D->Section) { - warn("could not find symbol " + Name + " to keep unique"); +static void findKeepUniqueSections(opt::InputArgList &args) { + for (auto *arg : args.filtered(OPT_keep_unique)) { + StringRef name = arg->getValue(); + auto *d = dyn_cast_or_null<Defined>(symtab->find(name)); + if (!d || !d->section) { + warn("could not find symbol " + name + " to keep unique"); continue; } - D->Section->KeepUnique = true; + d->section->keepUnique = true; } // --icf=all --ignore-data-address-equality means that we can ignore // the dynsym and address-significance tables entirely. - if (Config->ICF == ICFLevel::All && Config->IgnoreDataAddressEquality) + if (config->icf == ICFLevel::All && config->ignoreDataAddressEquality) return; // Symbols in the dynsym could be address-significant in other executables // or DSOs, so we conservatively mark them as address-significant. - for (Symbol *S : Symtab->getSymbols()) - if (S->includeInDynsym()) - markAddrsig(S); + symtab->forEachSymbol([&](Symbol *sym) { + if (sym->includeInDynsym()) + markAddrsig(sym); + }); // Visit the address-significance table in each object file and mark each // referenced symbol as address-significant. - for (InputFile *F : ObjectFiles) { - auto *Obj = cast<ObjFile<ELFT>>(F); - ArrayRef<Symbol *> Syms = Obj->getSymbols(); - if (Obj->AddrsigSec) { - ArrayRef<uint8_t> Contents = - check(Obj->getObj().getSectionContents(Obj->AddrsigSec)); - const uint8_t *Cur = Contents.begin(); - while (Cur != Contents.end()) { - unsigned Size; - const char *Err; - uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); - if (Err) - fatal(toString(F) + ": could not decode addrsig section: " + Err); - markAddrsig(Syms[SymIndex]); - Cur += Size; + for (InputFile *f : objectFiles) { + auto *obj = cast<ObjFile<ELFT>>(f); + ArrayRef<Symbol *> syms = obj->getSymbols(); + if (obj->addrsigSec) { + ArrayRef<uint8_t> contents = + check(obj->getObj().getSectionContents(obj->addrsigSec)); + const uint8_t *cur = contents.begin(); + while (cur != contents.end()) { + unsigned size; + const char *err; + uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err); + if (err) + fatal(toString(f) + ": could not decode addrsig section: " + err); + markAddrsig(syms[symIndex]); + cur += size; } } else { // If an object file does not have an address-significance table, // conservatively mark all of its symbols as address-significant. - for (Symbol *S : Syms) - markAddrsig(S); + for (Symbol *s : syms) + markAddrsig(s); + } + } +} + +// This function reads a symbol partition specification section. These sections +// are used to control which partition a symbol is allocated to. See +// https://lld.llvm.org/Partitions.html for more details on partitions. +template <typename ELFT> +static void readSymbolPartitionSection(InputSectionBase *s) { + // Read the relocation that refers to the partition's entry point symbol. + Symbol *sym; + if (s->areRelocsRela) + sym = &s->getFile<ELFT>()->getRelocTargetSym(s->template relas<ELFT>()[0]); + else + sym = &s->getFile<ELFT>()->getRelocTargetSym(s->template rels<ELFT>()[0]); + if (!isa<Defined>(sym) || !sym->includeInDynsym()) + return; + + StringRef partName = reinterpret_cast<const char *>(s->data().data()); + for (Partition &part : partitions) { + if (part.name == partName) { + sym->partition = part.getNumber(); + return; } } + + // Forbid partitions from being used on incompatible targets, and forbid them + // from being used together with various linker features that assume a single + // set of output sections. + if (script->hasSectionsCommand) + error(toString(s->file) + + ": partitions cannot be used with the SECTIONS command"); + if (script->hasPhdrsCommands()) + error(toString(s->file) + + ": partitions cannot be used with the PHDRS command"); + if (!config->sectionStartMap.empty()) + error(toString(s->file) + ": partitions cannot be used with " + "--section-start, -Ttext, -Tdata or -Tbss"); + if (config->emachine == EM_MIPS) + error(toString(s->file) + ": partitions cannot be used on this target"); + + // Impose a limit of no more than 254 partitions. This limit comes from the + // sizes of the Partition fields in InputSectionBase and Symbol, as well as + // the amount of space devoted to the partition number in RankFlags. + if (partitions.size() == 254) + fatal("may not have more than 254 partitions"); + + partitions.emplace_back(); + Partition &newPart = partitions.back(); + newPart.name = partName; + sym->partition = newPart.getNumber(); } -template <class ELFT> static Symbol *addUndefined(StringRef Name) { - return Symtab->addUndefined<ELFT>(Name, STB_GLOBAL, STV_DEFAULT, 0, false, - nullptr); +static Symbol *addUndefined(StringRef name) { + return symtab->addSymbol( + Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0}); +} + +// This function is where all the optimizations of link-time +// optimization takes place. When LTO is in use, some input files are +// not in native object file format but in the LLVM bitcode format. +// This function compiles bitcode files into a few big native files +// using LLVM functions and replaces bitcode symbols with the results. +// Because all bitcode files that the program consists of are passed to +// the compiler at once, it can do a whole-program optimization. +template <class ELFT> void LinkerDriver::compileBitcodeFiles() { + // Compile bitcode files and replace bitcode symbols. + lto.reset(new BitcodeCompiler); + for (BitcodeFile *file : bitcodeFiles) + lto->add(*file); + + for (InputFile *file : lto->compile()) { + auto *obj = cast<ObjFile<ELFT>>(file); + obj->parse(/*ignoreComdats=*/true); + for (Symbol *sym : obj->getGlobalSymbols()) + sym->parseSymbolVersion(); + objectFiles.push_back(file); + } } // The --wrap option is a feature to rename symbols so that you can write @@ -1365,9 +1558,9 @@ template <class ELFT> static Symbol *addUndefined(StringRef Name) { // // This data structure is instantiated for each -wrap option. struct WrappedSymbol { - Symbol *Sym; - Symbol *Real; - Symbol *Wrap; + Symbol *sym; + Symbol *real; + Symbol *wrap; }; // Handles -wrap option. @@ -1375,34 +1568,33 @@ struct WrappedSymbol { // This function instantiates wrapper symbols. At this point, they seem // like they are not being used at all, so we explicitly set some flags so // that LTO won't eliminate them. -template <class ELFT> -static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) { - std::vector<WrappedSymbol> V; - DenseSet<StringRef> Seen; +static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &args) { + std::vector<WrappedSymbol> v; + DenseSet<StringRef> seen; - for (auto *Arg : Args.filtered(OPT_wrap)) { - StringRef Name = Arg->getValue(); - if (!Seen.insert(Name).second) + for (auto *arg : args.filtered(OPT_wrap)) { + StringRef name = arg->getValue(); + if (!seen.insert(name).second) continue; - Symbol *Sym = Symtab->find(Name); - if (!Sym) + Symbol *sym = symtab->find(name); + if (!sym) continue; - Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name)); - Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name)); - V.push_back({Sym, Real, Wrap}); + Symbol *real = addUndefined(saver.save("__real_" + name)); + Symbol *wrap = addUndefined(saver.save("__wrap_" + name)); + v.push_back({sym, real, wrap}); // We want to tell LTO not to inline symbols to be overwritten // because LTO doesn't know the final symbol contents after renaming. - Real->CanInline = false; - Sym->CanInline = false; + real->canInline = false; + sym->canInline = false; // Tell LTO not to eliminate these symbols. - Sym->IsUsedInRegularObj = true; - Wrap->IsUsedInRegularObj = true; + sym->isUsedInRegularObj = true; + wrap->isUsedInRegularObj = true; } - return V; + return v; } // Do renaming for -wrap by updating pointers to symbols. @@ -1410,27 +1602,63 @@ static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) { // When this function is executed, only InputFiles and symbol table // contain pointers to symbol objects. We visit them to replace pointers, // so that wrapped symbols are swapped as instructed by the command line. -template <class ELFT> static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) { - DenseMap<Symbol *, Symbol *> Map; - for (const WrappedSymbol &W : Wrapped) { - Map[W.Sym] = W.Wrap; - Map[W.Real] = W.Sym; +static void wrapSymbols(ArrayRef<WrappedSymbol> wrapped) { + DenseMap<Symbol *, Symbol *> map; + for (const WrappedSymbol &w : wrapped) { + map[w.sym] = w.wrap; + map[w.real] = w.sym; } // Update pointers in input files. - parallelForEach(ObjectFiles, [&](InputFile *File) { - std::vector<Symbol *> &Syms = File->getMutableSymbols(); - for (size_t I = 0, E = Syms.size(); I != E; ++I) - if (Symbol *S = Map.lookup(Syms[I])) - Syms[I] = S; + parallelForEach(objectFiles, [&](InputFile *file) { + MutableArrayRef<Symbol *> syms = file->getMutableSymbols(); + for (size_t i = 0, e = syms.size(); i != e; ++i) + if (Symbol *s = map.lookup(syms[i])) + syms[i] = s; }); // Update pointers in the symbol table. - for (const WrappedSymbol &W : Wrapped) - Symtab->wrap(W.Sym, W.Real, W.Wrap); + for (const WrappedSymbol &w : wrapped) + symtab->wrap(w.sym, w.real, w.wrap); } -static const char *LibcallRoutineNames[] = { +// To enable CET (x86's hardware-assited control flow enforcement), each +// source file must be compiled with -fcf-protection. Object files compiled +// with the flag contain feature flags indicating that they are compatible +// with CET. We enable the feature only when all object files are compatible +// with CET. +// +// This function returns the merged feature flags. If 0, we cannot enable CET. +// This is also the case with AARCH64's BTI and PAC which use the similar +// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism. +// +// Note that the CET-aware PLT is not implemented yet. We do error +// check only. +template <class ELFT> static uint32_t getAndFeatures() { + if (config->emachine != EM_386 && config->emachine != EM_X86_64 && + config->emachine != EM_AARCH64) + return 0; + + uint32_t ret = -1; + for (InputFile *f : objectFiles) { + uint32_t features = cast<ObjFile<ELFT>>(f)->andFeatures; + if (config->forceBTI && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) { + warn(toString(f) + ": --force-bti: file does not have BTI property"); + features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI; + } else if (!features && config->requireCET) + error(toString(f) + ": --require-cet: file is not compatible with CET"); + ret &= features; + } + + // Force enable pointer authentication Plt, we don't warn in this case as + // this does not require support in the object for correctness. + if (config->pacPlt) + ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC; + + return ret; +} + +static const char *libcallRoutineNames[] = { #define HANDLE_LIBCALL(code, name) name, #include "llvm/IR/RuntimeLibcalls.def" #undef HANDLE_LIBCALL @@ -1438,53 +1666,48 @@ static const char *LibcallRoutineNames[] = { // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. -template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { - Target = getTarget(); - InX<ELFT>::VerSym = nullptr; - InX<ELFT>::VerNeed = nullptr; - - Config->MaxPageSize = getMaxPageSize(Args); - Config->ImageBase = getImageBase(Args); - +template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) { // If a -hash-style option was not given, set to a default value, // which varies depending on the target. - if (!Args.hasArg(OPT_hash_style)) { - if (Config->EMachine == EM_MIPS) - Config->SysvHash = true; + if (!args.hasArg(OPT_hash_style)) { + if (config->emachine == EM_MIPS) + config->sysvHash = true; else - Config->SysvHash = Config->GnuHash = true; + config->sysvHash = config->gnuHash = true; } // Default output filename is "a.out" by the Unix tradition. - if (Config->OutputFile.empty()) - Config->OutputFile = "a.out"; + if (config->outputFile.empty()) + config->outputFile = "a.out"; // Fail early if the output file or map file is not writable. If a user has a // long link, e.g. due to a large LTO link, they do not wish to run it and // find that it failed because there was a mistake in their command-line. - if (auto E = tryCreateFile(Config->OutputFile)) - error("cannot open output file " + Config->OutputFile + ": " + E.message()); - if (auto E = tryCreateFile(Config->MapFile)) - error("cannot open map file " + Config->MapFile + ": " + E.message()); + if (auto e = tryCreateFile(config->outputFile)) + error("cannot open output file " + config->outputFile + ": " + e.message()); + if (auto e = tryCreateFile(config->mapFile)) + error("cannot open map file " + config->mapFile + ": " + e.message()); if (errorCount()) return; // Use default entry point name if no name was given via the command // line nor linker scripts. For some reason, MIPS entry point name is // different from others. - Config->WarnMissingEntry = - (!Config->Entry.empty() || (!Config->Shared && !Config->Relocatable)); - if (Config->Entry.empty() && !Config->Relocatable) - Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start"; + config->warnMissingEntry = + (!config->entry.empty() || (!config->shared && !config->relocatable)); + if (config->entry.empty() && !config->relocatable) + config->entry = (config->emachine == EM_MIPS) ? "__start" : "_start"; // Handle --trace-symbol. - for (auto *Arg : Args.filtered(OPT_trace_symbol)) - Symtab->trace(Arg->getValue()); + for (auto *arg : args.filtered(OPT_trace_symbol)) + symtab->insert(arg->getValue())->traced = true; // Add all files to the symbol table. This will add almost all - // symbols that we need to the symbol table. - for (InputFile *F : Files) - Symtab->addFile<ELFT>(F); + // symbols that we need to the symbol table. This process might + // add files to the link, via autolinking, these files are always + // appended to the Files vector. + for (size_t i = 0; i < files.size(); ++i) + parseFile(files[i]); // Now that we have every file, we can decide if we will need a // dynamic symbol table. @@ -1492,20 +1715,26 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // producing a shared library. // We also need one if any shared libraries are used and for pie executables // (probably because the dynamic linker needs it). - Config->HasDynSymTab = - !SharedFiles.empty() || Config->Pic || Config->ExportDynamic; + config->hasDynSymTab = + !sharedFiles.empty() || config->isPic || config->exportDynamic; // Some symbols (such as __ehdr_start) are defined lazily only when there // are undefined symbols for them, so we add these to trigger that logic. - for (StringRef Name : Script->ReferencedSymbols) - addUndefined<ELFT>(Name); + for (StringRef name : script->referencedSymbols) + addUndefined(name); // Handle the `--undefined <sym>` options. - for (StringRef S : Config->Undefined) - handleUndefined<ELFT>(S); + for (StringRef arg : config->undefined) + if (Symbol *sym = symtab->find(arg)) + handleUndefined(sym); // If an entry symbol is in a static archive, pull out that file now. - handleUndefined<ELFT>(Config->Entry); + if (Symbol *sym = symtab->find(config->entry)) + handleUndefined(sym); + + // Handle the `--undefined-glob <pattern>` options. + for (StringRef pat : args::getStrings(args, OPT_undefined_glob)) + handleUndefinedGlob(pat); // If any of our inputs are bitcode files, the LTO code generator may create // references to certain library functions that might not be explicit in the @@ -1524,9 +1753,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // to, i.e. if the symbol's definition is in bitcode. Any other required // libcall symbols will be added to the link after LTO when we add the LTO // object file to the link. - if (!BitcodeFiles.empty()) - for (const char *S : LibcallRoutineNames) - handleLibcall<ELFT>(S); + if (!bitcodeFiles.empty()) + for (const char *s : libcallRoutineNames) + handleLibcall(s); // Return if there were name resolution errors. if (errorCount()) @@ -1534,27 +1763,27 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // Now when we read all script files, we want to finalize order of linker // script commands, which can be not yet final because of INSERT commands. - Script->processInsertCommands(); + script->processInsertCommands(); // We want to declare linker script's symbols early, // so that we can version them. // They also might be exported if referenced by DSOs. - Script->declareSymbols(); + script->declareSymbols(); // Handle the -exclude-libs option. - if (Args.hasArg(OPT_exclude_libs)) - excludeLibs<ELFT>(Args); + if (args.hasArg(OPT_exclude_libs)) + excludeLibs(args); - // Create ElfHeader early. We need a dummy section in + // Create elfHeader early. We need a dummy section in // addReservedSymbols to mark the created symbols as not absolute. - Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC); - Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr); + Out::elfHeader = make<OutputSection>("", 0, SHF_ALLOC); + Out::elfHeader->size = sizeof(typename ELFT::Ehdr); // Create wrapped symbols for -wrap option. - std::vector<WrappedSymbol> Wrapped = addWrappedSymbols<ELFT>(Args); + std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args); // We need to create some reserved symbols such as _end. Create them. - if (!Config->Relocatable) + if (!config->relocatable) addReservedSymbols(); // Apply version scripts. @@ -1562,84 +1791,118 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // For a relocatable output, version scripts don't make sense, and // parsing a symbol version string (e.g. dropping "@ver1" from a symbol // name "foo@ver1") rather do harm, so we don't call this if -r is given. - if (!Config->Relocatable) - Symtab->scanVersionScript(); + if (!config->relocatable) + symtab->scanVersionScript(); // Do link-time optimization if given files are LLVM bitcode files. // This compiles bitcode files into real object files. // // With this the symbol table should be complete. After this, no new names // except a few linker-synthesized ones will be added to the symbol table. - Symtab->addCombinedLTOObject<ELFT>(); + compileBitcodeFiles<ELFT>(); if (errorCount()) return; // If -thinlto-index-only is given, we should create only "index // files" and not object files. Index file creation is already done // in addCombinedLTOObject, so we are done if that's the case. - if (Config->ThinLTOIndexOnly) + if (config->thinLTOIndexOnly) return; // Likewise, --plugin-opt=emit-llvm is an option to make LTO create // an output file in bitcode and exit, so that you can just get a // combined bitcode file. - if (Config->EmitLLVM) + if (config->emitLLVM) return; // Apply symbol renames for -wrap. - if (!Wrapped.empty()) - wrapSymbols<ELFT>(Wrapped); + if (!wrapped.empty()) + wrapSymbols(wrapped); // Now that we have a complete list of input files. // Beyond this point, no new files are added. // Aggregate all input sections into one place. - for (InputFile *F : ObjectFiles) - for (InputSectionBase *S : F->getSections()) - if (S && S != &InputSection::Discarded) - InputSections.push_back(S); - for (BinaryFile *F : BinaryFiles) - for (InputSectionBase *S : F->getSections()) - InputSections.push_back(cast<InputSection>(S)); - - // We do not want to emit debug sections if --strip-all - // or -strip-debug are given. - if (Config->Strip != StripPolicy::None) - llvm::erase_if(InputSections, [](InputSectionBase *S) { - return S->Name.startswith(".debug") || S->Name.startswith(".zdebug"); - }); - - Config->EFlags = Target->calcEFlags(); - - if (Config->EMachine == EM_ARM) { + for (InputFile *f : objectFiles) + for (InputSectionBase *s : f->getSections()) + if (s && s != &InputSection::discarded) + inputSections.push_back(s); + for (BinaryFile *f : binaryFiles) + for (InputSectionBase *s : f->getSections()) + inputSections.push_back(cast<InputSection>(s)); + + llvm::erase_if(inputSections, [](InputSectionBase *s) { + if (s->type == SHT_LLVM_SYMPART) { + readSymbolPartitionSection<ELFT>(s); + return true; + } + + // We do not want to emit debug sections if --strip-all + // or -strip-debug are given. + return config->strip != StripPolicy::None && + (s->name.startswith(".debug") || s->name.startswith(".zdebug")); + }); + + // Now that the number of partitions is fixed, save a pointer to the main + // partition. + mainPart = &partitions[0]; + + // Read .note.gnu.property sections from input object files which + // contain a hint to tweak linker's and loader's behaviors. + config->andFeatures = getAndFeatures<ELFT>(); + + // The Target instance handles target-specific stuff, such as applying + // relocations or writing a PLT section. It also contains target-dependent + // values such as a default image base address. + target = getTarget(); + + config->eflags = target->calcEFlags(); + // maxPageSize (sometimes called abi page size) is the maximum page size that + // the output can be run on. For example if the OS can use 4k or 64k page + // sizes then maxPageSize must be 64k for the output to be useable on both. + // All important alignment decisions must use this value. + config->maxPageSize = getMaxPageSize(args); + // commonPageSize is the most common page size that the output will be run on. + // For example if an OS can use 4k or 64k page sizes and 4k is more common + // than 64k then commonPageSize is set to 4k. commonPageSize can be used for + // optimizations such as DATA_SEGMENT_ALIGN in linker scripts. LLD's use of it + // is limited to writing trap instructions on the last executable segment. + config->commonPageSize = getCommonPageSize(args); + + config->imageBase = getImageBase(args); + + if (config->emachine == EM_ARM) { // FIXME: These warnings can be removed when lld only uses these features // when the input objects have been compiled with an architecture that // supports them. - if (Config->ARMHasBlx == false) + if (config->armHasBlx == false) warn("lld uses blx instruction, no object with architecture supporting " "feature detected"); } // This adds a .comment section containing a version string. We have to add it // before mergeSections because the .comment section is a mergeable section. - if (!Config->Relocatable) - InputSections.push_back(createCommentSection()); + if (!config->relocatable) + inputSections.push_back(createCommentSection()); + + // Replace common symbols with regular symbols. + replaceCommonSymbols(); // Do size optimizations: garbage collection, merging of SHF_MERGE sections // and identical code folding. splitSections<ELFT>(); markLive<ELFT>(); - demoteSharedSymbols<ELFT>(); + demoteSharedSymbols(); mergeSections(); - if (Config->ICF != ICFLevel::None) { - findKeepUniqueSections<ELFT>(Args); + if (config->icf != ICFLevel::None) { + findKeepUniqueSections<ELFT>(args); doIcf<ELFT>(); } // Read the callgraph now that we know what was gced or icfed - if (Config->CallGraphProfileSort) { - if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file)) - if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) - readCallGraph(*Buffer); + if (config->callGraphProfileSort) { + if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) + if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())) + readCallGraph(*buffer); readCallGraphsFromObjectFiles<ELFT>(); } diff --git a/ELF/Driver.h b/ELF/Driver.h index 81d7f608e588..3115e28d1669 100644 --- a/ELF/Driver.h +++ b/ELF/Driver.h @@ -1,15 +1,15 @@ //===- Driver.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_ELF_DRIVER_H #define LLD_ELF_DRIVER_H +#include "LTO.h" #include "SymbolTable.h" #include "lld/Common/LLVM.h" #include "lld/Common/Reproduce.h" @@ -22,34 +22,37 @@ namespace lld { namespace elf { -extern class LinkerDriver *Driver; +extern class LinkerDriver *driver; class LinkerDriver { public: - void main(ArrayRef<const char *> Args); - void addFile(StringRef Path, bool WithLOption); - void addLibrary(StringRef Name); + void main(ArrayRef<const char *> args); + void addFile(StringRef path, bool withLOption); + void addLibrary(StringRef name); private: - void readConfigs(llvm::opt::InputArgList &Args); - void createFiles(llvm::opt::InputArgList &Args); + void createFiles(llvm::opt::InputArgList &args); void inferMachineType(); - template <class ELFT> void link(llvm::opt::InputArgList &Args); + template <class ELFT> void link(llvm::opt::InputArgList &args); + template <class ELFT> void compileBitcodeFiles(); // True if we are in --whole-archive and --no-whole-archive. - bool InWholeArchive = false; + bool inWholeArchive = false; // True if we are in --start-lib and --end-lib. - bool InLib = false; + bool inLib = false; + + // For LTO. + std::unique_ptr<BitcodeCompiler> lto; - std::vector<InputFile *> Files; + std::vector<InputFile *> files; }; // Parses command line options. class ELFOptTable : public llvm::opt::OptTable { public: ELFOptTable(); - llvm::opt::InputArgList parse(ArrayRef<const char *> Argv); + llvm::opt::InputArgList parse(ArrayRef<const char *> argv); }; // Create enum with OPT_xxx values for each option in Options.td @@ -61,11 +64,12 @@ enum { }; void printHelp(); -std::string createResponseFile(const llvm::opt::InputArgList &Args); +std::string createResponseFile(const llvm::opt::InputArgList &args); -llvm::Optional<std::string> findFromSearchPaths(StringRef Path); -llvm::Optional<std::string> searchScript(StringRef Path); -llvm::Optional<std::string> searchLibrary(StringRef Path); +llvm::Optional<std::string> findFromSearchPaths(StringRef path); +llvm::Optional<std::string> searchScript(StringRef path); +llvm::Optional<std::string> searchLibraryBaseName(StringRef path); +llvm::Optional<std::string> searchLibrary(StringRef path); } // namespace elf } // namespace lld diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp index e51d02e38da1..87f0aa2a9827 100644 --- a/ELF/DriverUtils.cpp +++ b/ELF/DriverUtils.cpp @@ -1,9 +1,8 @@ //===- DriverUtils.cpp ----------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -42,7 +41,7 @@ using namespace lld::elf; #undef PREFIX // Create table mapping all options defined in Options.td -static const opt::OptTable::Info OptInfo[] = { +static const opt::OptTable::Info optInfo[] = { #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, @@ -50,36 +49,36 @@ static const opt::OptTable::Info OptInfo[] = { #undef OPTION }; -ELFOptTable::ELFOptTable() : OptTable(OptInfo) {} +ELFOptTable::ELFOptTable() : OptTable(optInfo) {} // Set color diagnostics according to -color-diagnostics={auto,always,never} // or -no-color-diagnostics flags. -static void handleColorDiagnostics(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, +static void handleColorDiagnostics(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, OPT_no_color_diagnostics); - if (!Arg) + if (!arg) return; - if (Arg->getOption().getID() == OPT_color_diagnostics) { - errorHandler().ColorDiagnostics = true; - } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) { - errorHandler().ColorDiagnostics = false; + if (arg->getOption().getID() == OPT_color_diagnostics) { + errorHandler().colorDiagnostics = true; + } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { + errorHandler().colorDiagnostics = false; } else { - StringRef S = Arg->getValue(); - if (S == "always") - errorHandler().ColorDiagnostics = true; - else if (S == "never") - errorHandler().ColorDiagnostics = false; - else if (S != "auto") - error("unknown option: --color-diagnostics=" + S); + StringRef s = arg->getValue(); + if (s == "always") + errorHandler().colorDiagnostics = true; + else if (s == "never") + errorHandler().colorDiagnostics = false; + else if (s != "auto") + error("unknown option: --color-diagnostics=" + s); } } -static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) { - StringRef S = Arg->getValue(); - if (S != "windows" && S != "posix") - error("invalid response file quoting: " + S); - if (S == "windows") +static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_rsp_quoting)) { + StringRef s = arg->getValue(); + if (s != "windows" && s != "posix") + error("invalid response file quoting: " + s); + if (s == "windows") return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } @@ -97,50 +96,56 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { // `--plugin-opt <foo>` is converted to `--plugin-opt=<foo>`. This is a // bit hacky, but looks like it is still better than handling --plugin-opt // options by hand. -static void concatLTOPluginOptions(SmallVectorImpl<const char *> &Args) { - SmallVector<const char *, 256> V; - for (size_t I = 0, E = Args.size(); I != E; ++I) { - StringRef S = Args[I]; - if ((S == "-plugin-opt" || S == "--plugin-opt") && I + 1 != E) { - V.push_back(Saver.save(S + "=" + Args[I + 1]).data()); - ++I; +static void concatLTOPluginOptions(SmallVectorImpl<const char *> &args) { + SmallVector<const char *, 256> v; + for (size_t i = 0, e = args.size(); i != e; ++i) { + StringRef s = args[i]; + if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) { + v.push_back(saver.save(s + "=" + args[i + 1]).data()); + ++i; } else { - V.push_back(Args[I]); + v.push_back(args[i]); } } - Args = std::move(V); + args = std::move(v); } // Parses a given list of options. -opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) { +opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) { // Make InputArgList from string vectors. - unsigned MissingIndex; - unsigned MissingCount; - SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size()); + unsigned missingIndex; + unsigned missingCount; + SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size()); // We need to get the quoting style for response files before parsing all // options so we parse here before and ignore all the options but // --rsp-quoting. - opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount); + opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount); // Expand response files (arguments in the form of @<filename>) // and then parse the argument again. - cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec); - concatLTOPluginOptions(Vec); - Args = this->ParseArgs(Vec, MissingIndex, MissingCount); - - handleColorDiagnostics(Args); - if (MissingCount) - error(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); - - for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - error("unknown argument: " + Arg->getSpelling()); - return Args; + cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec); + concatLTOPluginOptions(vec); + args = this->ParseArgs(vec, missingIndex, missingCount); + + handleColorDiagnostics(args); + if (missingCount) + error(Twine(args.getArgString(missingIndex)) + ": missing argument"); + + for (auto *arg : args.filtered(OPT_UNKNOWN)) { + std::string nearest; + if (findNearest(arg->getAsString(args), nearest) > 1) + error("unknown argument '" + arg->getAsString(args) + "'"); + else + error("unknown argument '" + arg->getAsString(args) + + "', did you mean '" + nearest + "'"); + } + return args; } void elf::printHelp() { ELFOptTable().PrintHelp( - outs(), (Config->ProgName + " [options] file...").str().c_str(), "lld", + outs(), (config->progName + " [options] file...").str().c_str(), "lld", false /*ShowHidden*/, true /*ShowAllAliases*/); outs() << "\n"; @@ -149,30 +154,36 @@ void elf::printHelp() { // in a message for the -help option. If it doesn't match, the scripts // assume that the linker doesn't support very basic features such as // shared libraries. Therefore, we need to print out at least "elf". - outs() << Config->ProgName << ": supported targets: elf\n"; + outs() << config->progName << ": supported targets: elf\n"; +} + +static std::string rewritePath(StringRef s) { + if (fs::exists(s)) + return relativeToRoot(s); + return s; } // Reconstructs command line arguments so that so that you can re-run // the same command with the same inputs. This is for --reproduce. -std::string elf::createResponseFile(const opt::InputArgList &Args) { - SmallString<0> Data; - raw_svector_ostream OS(Data); - OS << "--chroot .\n"; +std::string elf::createResponseFile(const opt::InputArgList &args) { + SmallString<0> data; + raw_svector_ostream os(data); + os << "--chroot .\n"; // Copy the command line to the output while rewriting paths. - for (auto *Arg : Args) { - switch (Arg->getOption().getUnaliasedOption().getID()) { + for (auto *arg : args) { + switch (arg->getOption().getID()) { case OPT_reproduce: break; case OPT_INPUT: - OS << quote(rewritePath(Arg->getValue())) << "\n"; + os << quote(rewritePath(arg->getValue())) << "\n"; break; case OPT_o: // If -o path contains directories, "lld @response.txt" will likely // fail because the archive we are creating doesn't contain empty // directories for the output path (-o doesn't create directories). // Strip directories to prevent the issue. - OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n"; + os << "-o " << quote(sys::path::filename(arg->getValue())) << "\n"; break; case OPT_dynamic_list: case OPT_library_path: @@ -181,58 +192,62 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) { case OPT_symbol_ordering_file: case OPT_sysroot: case OPT_version_script: - OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue())) + os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue())) << "\n"; break; default: - OS << toString(*Arg) << "\n"; + os << toString(*arg) << "\n"; } } - return Data.str(); + return data.str(); } // Find a file by concatenating given paths. If a resulting path // starts with "=", the character is replaced with a --sysroot value. -static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) { - SmallString<128> S; - if (Path1.startswith("=")) - path::append(S, Config->Sysroot, Path1.substr(1), Path2); +static Optional<std::string> findFile(StringRef path1, const Twine &path2) { + SmallString<128> s; + if (path1.startswith("=")) + path::append(s, config->sysroot, path1.substr(1), path2); else - path::append(S, Path1, Path2); + path::append(s, path1, path2); - if (fs::exists(S)) - return S.str().str(); + if (fs::exists(s)) + return s.str().str(); return None; } -Optional<std::string> elf::findFromSearchPaths(StringRef Path) { - for (StringRef Dir : Config->SearchPaths) - if (Optional<std::string> S = findFile(Dir, Path)) - return S; +Optional<std::string> elf::findFromSearchPaths(StringRef path) { + for (StringRef dir : config->searchPaths) + if (Optional<std::string> s = findFile(dir, path)) + return s; return None; } -// This is for -lfoo. We'll look for libfoo.so or libfoo.a from +// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from // search paths. -Optional<std::string> elf::searchLibrary(StringRef Name) { - if (Name.startswith(":")) - return findFromSearchPaths(Name.substr(1)); - - for (StringRef Dir : Config->SearchPaths) { - if (!Config->Static) - if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".so")) - return S; - if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a")) - return S; +Optional<std::string> elf::searchLibraryBaseName(StringRef name) { + for (StringRef dir : config->searchPaths) { + if (!config->isStatic) + if (Optional<std::string> s = findFile(dir, "lib" + name + ".so")) + return s; + if (Optional<std::string> s = findFile(dir, "lib" + name + ".a")) + return s; } return None; } +// This is for -l<namespec>. +Optional<std::string> elf::searchLibrary(StringRef name) { + if (name.startswith(":")) + return findFromSearchPaths(name.substr(1)); + return searchLibraryBaseName (name); +} + // If a linker/version script doesn't exist in the current directory, we also // look for the script in the '-L' search paths. This matches the behaviour of // '-T', --version-script=, and linker script INPUT() command in ld.bfd. -Optional<std::string> elf::searchScript(StringRef Name) { - if (fs::exists(Name)) - return Name.str(); - return findFromSearchPaths(Name); +Optional<std::string> elf::searchScript(StringRef name) { + if (fs::exists(name)) + return name.str(); + return findFromSearchPaths(name); } diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp index 95d444bdc2a1..b3245dd01669 100644 --- a/ELF/EhFrame.cpp +++ b/ELF/EhFrame.cpp @@ -1,9 +1,8 @@ //===- EhFrame.cpp -------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -37,72 +36,72 @@ using namespace lld::elf; namespace { class EhReader { public: - EhReader(InputSectionBase *S, ArrayRef<uint8_t> D) : IS(S), D(D) {} + EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {} size_t readEhRecordSize(); uint8_t getFdeEncoding(); private: - template <class P> void failOn(const P *Loc, const Twine &Msg) { - fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " + - IS->getObjMsg((const uint8_t *)Loc - IS->data().data())); + template <class P> void failOn(const P *loc, const Twine &msg) { + fatal("corrupted .eh_frame: " + msg + "\n>>> defined in " + + isec->getObjMsg((const uint8_t *)loc - isec->data().data())); } uint8_t readByte(); - void skipBytes(size_t Count); + void skipBytes(size_t count); StringRef readString(); void skipLeb128(); void skipAugP(); - InputSectionBase *IS; - ArrayRef<uint8_t> D; + InputSectionBase *isec; + ArrayRef<uint8_t> d; }; } -size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) { - return EhReader(S, S->data().slice(Off)).readEhRecordSize(); +size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) { + return EhReader(s, s->data().slice(off)).readEhRecordSize(); } // .eh_frame section is a sequence of records. Each record starts with // a 4 byte length field. This function reads the length. size_t EhReader::readEhRecordSize() { - if (D.size() < 4) - failOn(D.data(), "CIE/FDE too small"); + if (d.size() < 4) + failOn(d.data(), "CIE/FDE too small"); // First 4 bytes of CIE/FDE is the size of the record. // If it is 0xFFFFFFFF, the next 8 bytes contain the size instead, // but we do not support that format yet. - uint64_t V = read32(D.data()); - if (V == UINT32_MAX) - failOn(D.data(), "CIE/FDE too large"); - uint64_t Size = V + 4; - if (Size > D.size()) - failOn(D.data(), "CIE/FDE ends past the end of the section"); - return Size; + uint64_t v = read32(d.data()); + if (v == UINT32_MAX) + failOn(d.data(), "CIE/FDE too large"); + uint64_t size = v + 4; + if (size > d.size()) + failOn(d.data(), "CIE/FDE ends past the end of the section"); + return size; } // Read a byte and advance D by one byte. uint8_t EhReader::readByte() { - if (D.empty()) - failOn(D.data(), "unexpected end of CIE"); - uint8_t B = D.front(); - D = D.slice(1); - return B; + if (d.empty()) + failOn(d.data(), "unexpected end of CIE"); + uint8_t b = d.front(); + d = d.slice(1); + return b; } -void EhReader::skipBytes(size_t Count) { - if (D.size() < Count) - failOn(D.data(), "CIE is too small"); - D = D.slice(Count); +void EhReader::skipBytes(size_t count) { + if (d.size() < count) + failOn(d.data(), "CIE is too small"); + d = d.slice(count); } // Read a null-terminated string. StringRef EhReader::readString() { - const uint8_t *End = std::find(D.begin(), D.end(), '\0'); - if (End == D.end()) - failOn(D.data(), "corrupted CIE (failed to read string)"); - StringRef S = toStringRef(D.slice(0, End - D.begin())); - D = D.slice(S.size() + 1); - return S; + const uint8_t *end = llvm::find(d, '\0'); + if (end == d.end()) + failOn(d.data(), "corrupted CIE (failed to read string)"); + StringRef s = toStringRef(d.slice(0, end - d.begin())); + d = d.slice(s.size() + 1); + return s; } // Skip an integer encoded in the LEB128 format. @@ -110,21 +109,21 @@ StringRef EhReader::readString() { // But we need to be at least able to skip it so that we can read // the field that follows a LEB128 number. void EhReader::skipLeb128() { - const uint8_t *ErrPos = D.data(); - while (!D.empty()) { - uint8_t Val = D.front(); - D = D.slice(1); - if ((Val & 0x80) == 0) + const uint8_t *errPos = d.data(); + while (!d.empty()) { + uint8_t val = d.front(); + d = d.slice(1); + if ((val & 0x80) == 0) return; } - failOn(ErrPos, "corrupted CIE (failed to read LEB128)"); + failOn(errPos, "corrupted CIE (failed to read LEB128)"); } -static size_t getAugPSize(unsigned Enc) { - switch (Enc & 0x0f) { +static size_t getAugPSize(unsigned enc) { + switch (enc & 0x0f) { case DW_EH_PE_absptr: case DW_EH_PE_signed: - return Config->Wordsize; + return config->wordsize; case DW_EH_PE_udata2: case DW_EH_PE_sdata2: return 2; @@ -139,29 +138,29 @@ static size_t getAugPSize(unsigned Enc) { } void EhReader::skipAugP() { - uint8_t Enc = readByte(); - if ((Enc & 0xf0) == DW_EH_PE_aligned) - failOn(D.data() - 1, "DW_EH_PE_aligned encoding is not supported"); - size_t Size = getAugPSize(Enc); - if (Size == 0) - failOn(D.data() - 1, "unknown FDE encoding"); - if (Size >= D.size()) - failOn(D.data() - 1, "corrupted CIE"); - D = D.slice(Size); + uint8_t enc = readByte(); + if ((enc & 0xf0) == DW_EH_PE_aligned) + failOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported"); + size_t size = getAugPSize(enc); + if (size == 0) + failOn(d.data() - 1, "unknown FDE encoding"); + if (size >= d.size()) + failOn(d.data() - 1, "corrupted CIE"); + d = d.slice(size); } -uint8_t elf::getFdeEncoding(EhSectionPiece *P) { - return EhReader(P->Sec, P->data()).getFdeEncoding(); +uint8_t elf::getFdeEncoding(EhSectionPiece *p) { + return EhReader(p->sec, p->data()).getFdeEncoding(); } uint8_t EhReader::getFdeEncoding() { skipBytes(8); - int Version = readByte(); - if (Version != 1 && Version != 3) - failOn(D.data() - 1, - "FDE version 1 or 3 expected, but got " + Twine(Version)); + int version = readByte(); + if (version != 1 && version != 3) + failOn(d.data() - 1, + "FDE version 1 or 3 expected, but got " + Twine(version)); - StringRef Aug = readString(); + StringRef aug = readString(); // Skip code and data alignment factors. skipLeb128(); @@ -169,7 +168,7 @@ uint8_t EhReader::getFdeEncoding() { // Skip the return address register. In CIE version 1 this is a single // byte. In CIE version 3 this is an unsigned LEB128. - if (Version == 1) + if (version == 1) readByte(); else skipLeb128(); @@ -177,22 +176,22 @@ uint8_t EhReader::getFdeEncoding() { // We only care about an 'R' value, but other records may precede an 'R' // record. Unfortunately records are not in TLV (type-length-value) format, // so we need to teach the linker how to skip records for each type. - for (char C : Aug) { - if (C == 'R') + for (char c : aug) { + if (c == 'R') return readByte(); - if (C == 'z') { + if (c == 'z') { skipLeb128(); continue; } - if (C == 'P') { + if (c == 'P') { skipAugP(); continue; } - if (C == 'L') { + if (c == 'L') { readByte(); continue; } - failOn(Aug.data(), "unknown .eh_frame augmentation string: " + Aug); + failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug); } return DW_EH_PE_absptr; } diff --git a/ELF/EhFrame.h b/ELF/EhFrame.h index 5112891a911e..20dd6126ec8e 100644 --- a/ELF/EhFrame.h +++ b/ELF/EhFrame.h @@ -1,9 +1,8 @@ //===- EhFrame.h ------------------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -17,8 +16,8 @@ namespace elf { class InputSectionBase; struct EhSectionPiece; -size_t readEhRecordSize(InputSectionBase *S, size_t Off); -uint8_t getFdeEncoding(EhSectionPiece *P); +size_t readEhRecordSize(InputSectionBase *s, size_t off); +uint8_t getFdeEncoding(EhSectionPiece *p); } // namespace elf } // namespace lld diff --git a/ELF/Filesystem.cpp b/ELF/Filesystem.cpp deleted file mode 100644 index 5cf240eeca56..000000000000 --- 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 987a74a6bcb6..000000000000 --- a/ELF/Filesystem.h +++ /dev/null @@ -1,23 +0,0 @@ -//===- Filesystem.h ---------------------------------------------*- C++ -*-===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_ELF_FILESYSTEM_H -#define LLD_ELF_FILESYSTEM_H - -#include "lld/Common/LLVM.h" -#include <system_error> - -namespace lld { -namespace elf { -void unlinkAsync(StringRef Path); -std::error_code tryCreateFile(StringRef Path); -} // namespace elf -} // namespace lld - -#endif diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp index e917ae76a689..8b01d06b0248 100644 --- a/ELF/ICF.cpp +++ b/ELF/ICF.cpp @@ -1,9 +1,8 @@ //===- ICF.cpp ------------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -99,33 +98,33 @@ public: void run(); private: - void segregate(size_t Begin, size_t End, bool Constant); + void segregate(size_t begin, size_t end, bool constant); template <class RelTy> - bool constantEq(const InputSection *A, ArrayRef<RelTy> RelsA, - const InputSection *B, ArrayRef<RelTy> RelsB); + bool constantEq(const InputSection *a, ArrayRef<RelTy> relsA, + const InputSection *b, ArrayRef<RelTy> relsB); template <class RelTy> - bool variableEq(const InputSection *A, ArrayRef<RelTy> RelsA, - const InputSection *B, ArrayRef<RelTy> RelsB); + bool variableEq(const InputSection *a, ArrayRef<RelTy> relsA, + const InputSection *b, ArrayRef<RelTy> relsB); - bool equalsConstant(const InputSection *A, const InputSection *B); - bool equalsVariable(const InputSection *A, const InputSection *B); + bool equalsConstant(const InputSection *a, const InputSection *b); + bool equalsVariable(const InputSection *a, const InputSection *b); - size_t findBoundary(size_t Begin, size_t End); + size_t findBoundary(size_t begin, size_t end); - void forEachClassRange(size_t Begin, size_t End, - llvm::function_ref<void(size_t, size_t)> Fn); + void forEachClassRange(size_t begin, size_t end, + llvm::function_ref<void(size_t, size_t)> fn); - void forEachClass(llvm::function_ref<void(size_t, size_t)> Fn); + void forEachClass(llvm::function_ref<void(size_t, size_t)> fn); - std::vector<InputSection *> Sections; + std::vector<InputSection *> sections; // We repeat the main loop while `Repeat` is true. - std::atomic<bool> Repeat; + std::atomic<bool> repeat; // The main loop counter. - int Cnt = 0; + int cnt = 0; // We have two locations for equivalence classes. On the first iteration // of the main loop, Class[0] has a valid value, and Class[1] contains @@ -151,42 +150,42 @@ private: // because we can safely read the next class without worrying about race // conditions. Using the same location makes this algorithm converge // faster because it uses results of the same iteration earlier. - int Current = 0; - int Next = 0; + int current = 0; + int next = 0; }; } // Returns true if section S is subject of ICF. -static bool isEligible(InputSection *S) { - if (!S->Live || S->KeepUnique || !(S->Flags & SHF_ALLOC)) +static bool isEligible(InputSection *s) { + if (!s->isLive() || s->keepUnique || !(s->flags & SHF_ALLOC)) return false; // Don't merge writable sections. .data.rel.ro sections are marked as writable // but are semantically read-only. - if ((S->Flags & SHF_WRITE) && S->Name != ".data.rel.ro" && - !S->Name.startswith(".data.rel.ro.")) + if ((s->flags & SHF_WRITE) && s->name != ".data.rel.ro" && + !s->name.startswith(".data.rel.ro.")) return false; // SHF_LINK_ORDER sections are ICF'd as a unit with their dependent sections, // so we don't consider them for ICF individually. - if (S->Flags & SHF_LINK_ORDER) + if (s->flags & SHF_LINK_ORDER) return false; // Don't merge synthetic sections as their Data member is not valid and empty. // The Data member needs to be valid for ICF as it is used by ICF to determine // the equality of section contents. - if (isa<SyntheticSection>(S)) + if (isa<SyntheticSection>(s)) return false; // .init and .fini contains instructions that must be executed to initialize // and finalize the process. They cannot and should not be merged. - if (S->Name == ".init" || S->Name == ".fini") + if (s->name == ".init" || s->name == ".fini") return false; // A user program may enumerate sections named with a C identifier using // __start_* and __stop_* symbols. We cannot ICF any such sections because // that could change program semantics. - if (isValidCIdentifier(S->Name)) + if (isValidCIdentifier(s->name)) return false; return true; @@ -194,7 +193,7 @@ static bool isEligible(InputSection *S) { // Split an equivalence class into smaller classes. template <class ELFT> -void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) { +void ICF<ELFT>::segregate(size_t begin, size_t end, bool constant) { // This loop rearranges sections in [Begin, End) so that all sections // that are equal in terms of equals{Constant,Variable} are contiguous // in [Begin, End). @@ -203,93 +202,93 @@ void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) { // issue in practice because the number of the distinct sections in // each range is usually very small. - while (Begin < End) { + while (begin < end) { // Divide [Begin, End) into two. Let Mid be the start index of the // second group. - auto Bound = - std::stable_partition(Sections.begin() + Begin + 1, - Sections.begin() + End, [&](InputSection *S) { - if (Constant) - return equalsConstant(Sections[Begin], S); - return equalsVariable(Sections[Begin], S); + auto bound = + std::stable_partition(sections.begin() + begin + 1, + sections.begin() + end, [&](InputSection *s) { + if (constant) + return equalsConstant(sections[begin], s); + return equalsVariable(sections[begin], s); }); - size_t Mid = Bound - Sections.begin(); + size_t mid = bound - sections.begin(); // Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by // updating the sections in [Begin, Mid). We use Mid as an equivalence // class ID because every group ends with a unique index. - for (size_t I = Begin; I < Mid; ++I) - Sections[I]->Class[Next] = Mid; + for (size_t i = begin; i < mid; ++i) + sections[i]->eqClass[next] = mid; // If we created a group, we need to iterate the main loop again. - if (Mid != End) - Repeat = true; + if (mid != end) + repeat = true; - Begin = Mid; + begin = mid; } } // Compare two lists of relocations. template <class ELFT> template <class RelTy> -bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA, - const InputSection *SecB, ArrayRef<RelTy> RB) { - for (size_t I = 0; I < RA.size(); ++I) { - if (RA[I].r_offset != RB[I].r_offset || - RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL)) +bool ICF<ELFT>::constantEq(const InputSection *secA, ArrayRef<RelTy> ra, + const InputSection *secB, ArrayRef<RelTy> rb) { + for (size_t i = 0; i < ra.size(); ++i) { + if (ra[i].r_offset != rb[i].r_offset || + ra[i].getType(config->isMips64EL) != rb[i].getType(config->isMips64EL)) return false; - uint64_t AddA = getAddend<ELFT>(RA[I]); - uint64_t AddB = getAddend<ELFT>(RB[I]); + uint64_t addA = getAddend<ELFT>(ra[i]); + uint64_t addB = getAddend<ELFT>(rb[i]); - Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]); - Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]); - if (&SA == &SB) { - if (AddA == AddB) + Symbol &sa = secA->template getFile<ELFT>()->getRelocTargetSym(ra[i]); + Symbol &sb = secB->template getFile<ELFT>()->getRelocTargetSym(rb[i]); + if (&sa == &sb) { + if (addA == addB) continue; return false; } - auto *DA = dyn_cast<Defined>(&SA); - auto *DB = dyn_cast<Defined>(&SB); + auto *da = dyn_cast<Defined>(&sa); + auto *db = dyn_cast<Defined>(&sb); // Placeholder symbols generated by linker scripts look the same now but // may have different values later. - if (!DA || !DB || DA->ScriptDefined || DB->ScriptDefined) + if (!da || !db || da->scriptDefined || db->scriptDefined) return false; // Relocations referring to absolute symbols are constant-equal if their // values are equal. - if (!DA->Section && !DB->Section && DA->Value + AddA == DB->Value + AddB) + if (!da->section && !db->section && da->value + addA == db->value + addB) continue; - if (!DA->Section || !DB->Section) + if (!da->section || !db->section) return false; - if (DA->Section->kind() != DB->Section->kind()) + if (da->section->kind() != db->section->kind()) return false; // Relocations referring to InputSections are constant-equal if their // section offsets are equal. - if (isa<InputSection>(DA->Section)) { - if (DA->Value + AddA == DB->Value + AddB) + if (isa<InputSection>(da->section)) { + if (da->value + addA == db->value + addB) continue; return false; } // Relocations referring to MergeInputSections are constant-equal if their // offsets in the output section are equal. - auto *X = dyn_cast<MergeInputSection>(DA->Section); - if (!X) + auto *x = dyn_cast<MergeInputSection>(da->section); + if (!x) return false; - auto *Y = cast<MergeInputSection>(DB->Section); - if (X->getParent() != Y->getParent()) + auto *y = cast<MergeInputSection>(db->section); + if (x->getParent() != y->getParent()) return false; - uint64_t OffsetA = - SA.isSection() ? X->getOffset(AddA) : X->getOffset(DA->Value) + AddA; - uint64_t OffsetB = - SB.isSection() ? Y->getOffset(AddB) : Y->getOffset(DB->Value) + AddB; - if (OffsetA != OffsetB) + uint64_t offsetA = + sa.isSection() ? x->getOffset(addA) : x->getOffset(da->value) + addA; + uint64_t offsetB = + sb.isSection() ? y->getOffset(addB) : y->getOffset(db->value) + addB; + if (offsetA != offsetB) return false; } @@ -299,57 +298,57 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA, // Compare "non-moving" part of two InputSections, namely everything // except relocation targets. template <class ELFT> -bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) { - if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags || - A->getSize() != B->getSize() || A->data() != B->data()) +bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) { + if (a->numRelocations != b->numRelocations || a->flags != b->flags || + a->getSize() != b->getSize() || a->data() != b->data()) return false; // If two sections have different output sections, we cannot merge them. // FIXME: This doesn't do the right thing in the case where there is a linker // script. We probably need to move output section assignment before ICF to // get the correct behaviour here. - if (getOutputSectionName(A) != getOutputSectionName(B)) + if (getOutputSectionName(a) != getOutputSectionName(b)) return false; - if (A->AreRelocsRela) - return constantEq(A, A->template relas<ELFT>(), B, - B->template relas<ELFT>()); - return constantEq(A, A->template rels<ELFT>(), B, B->template rels<ELFT>()); + if (a->areRelocsRela) + return constantEq(a, a->template relas<ELFT>(), b, + b->template relas<ELFT>()); + return constantEq(a, a->template rels<ELFT>(), b, b->template rels<ELFT>()); } // Compare two lists of relocations. Returns true if all pairs of // relocations point to the same section in terms of ICF. template <class ELFT> template <class RelTy> -bool ICF<ELFT>::variableEq(const InputSection *SecA, ArrayRef<RelTy> RA, - const InputSection *SecB, ArrayRef<RelTy> RB) { - assert(RA.size() == RB.size()); +bool ICF<ELFT>::variableEq(const InputSection *secA, ArrayRef<RelTy> ra, + const InputSection *secB, ArrayRef<RelTy> rb) { + assert(ra.size() == rb.size()); - for (size_t I = 0; I < RA.size(); ++I) { + for (size_t i = 0; i < ra.size(); ++i) { // The two sections must be identical. - Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]); - Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]); - if (&SA == &SB) + Symbol &sa = secA->template getFile<ELFT>()->getRelocTargetSym(ra[i]); + Symbol &sb = secB->template getFile<ELFT>()->getRelocTargetSym(rb[i]); + if (&sa == &sb) continue; - auto *DA = cast<Defined>(&SA); - auto *DB = cast<Defined>(&SB); + auto *da = cast<Defined>(&sa); + auto *db = cast<Defined>(&sb); // We already dealt with absolute and non-InputSection symbols in // constantEq, and for InputSections we have already checked everything // except the equivalence class. - if (!DA->Section) + if (!da->section) continue; - auto *X = dyn_cast<InputSection>(DA->Section); - if (!X) + auto *x = dyn_cast<InputSection>(da->section); + if (!x) continue; - auto *Y = cast<InputSection>(DB->Section); + auto *y = cast<InputSection>(db->section); // Ineligible sections are in the special equivalence class 0. // They can never be the same in terms of the equivalence class. - if (X->Class[Current] == 0) + if (x->eqClass[current] == 0) return false; - if (X->Class[Current] != Y->Class[Current]) + if (x->eqClass[current] != y->eqClass[current]) return false; }; @@ -358,19 +357,19 @@ bool ICF<ELFT>::variableEq(const InputSection *SecA, ArrayRef<RelTy> RA, // Compare "moving" part of two InputSections, namely relocation targets. template <class ELFT> -bool ICF<ELFT>::equalsVariable(const InputSection *A, const InputSection *B) { - if (A->AreRelocsRela) - return variableEq(A, A->template relas<ELFT>(), B, - B->template relas<ELFT>()); - return variableEq(A, A->template rels<ELFT>(), B, B->template rels<ELFT>()); +bool ICF<ELFT>::equalsVariable(const InputSection *a, const InputSection *b) { + if (a->areRelocsRela) + return variableEq(a, a->template relas<ELFT>(), b, + b->template relas<ELFT>()); + return variableEq(a, a->template rels<ELFT>(), b, b->template rels<ELFT>()); } -template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) { - uint32_t Class = Sections[Begin]->Class[Current]; - for (size_t I = Begin + 1; I < End; ++I) - if (Class != Sections[I]->Class[Current]) - return I; - return End; +template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t begin, size_t end) { + uint32_t eqClass = sections[begin]->eqClass[current]; + for (size_t i = begin + 1; i < end; ++i) + if (eqClass != sections[i]->eqClass[current]) + return i; + return end; } // Sections in the same equivalence class are contiguous in Sections @@ -379,123 +378,125 @@ template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) { // // This function calls Fn on every group within [Begin, End). template <class ELFT> -void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End, - llvm::function_ref<void(size_t, size_t)> Fn) { - while (Begin < End) { - size_t Mid = findBoundary(Begin, End); - Fn(Begin, Mid); - Begin = Mid; +void ICF<ELFT>::forEachClassRange(size_t begin, size_t end, + llvm::function_ref<void(size_t, size_t)> fn) { + while (begin < end) { + size_t mid = findBoundary(begin, end); + fn(begin, mid); + begin = mid; } } // Call Fn on each equivalence class. template <class ELFT> -void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> Fn) { +void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> fn) { // If threading is disabled or the number of sections are // too small to use threading, call Fn sequentially. - if (!ThreadsEnabled || Sections.size() < 1024) { - forEachClassRange(0, Sections.size(), Fn); - ++Cnt; + if (!threadsEnabled || sections.size() < 1024) { + forEachClassRange(0, sections.size(), fn); + ++cnt; return; } - Current = Cnt % 2; - Next = (Cnt + 1) % 2; + current = cnt % 2; + next = (cnt + 1) % 2; // Shard into non-overlapping intervals, and call Fn in parallel. // The sharding must be completed before any calls to Fn are made // so that Fn can modify the Chunks in its shard without causing data // races. - const size_t NumShards = 256; - size_t Step = Sections.size() / NumShards; - size_t Boundaries[NumShards + 1]; - Boundaries[0] = 0; - Boundaries[NumShards] = Sections.size(); - - parallelForEachN(1, NumShards, [&](size_t I) { - Boundaries[I] = findBoundary((I - 1) * Step, Sections.size()); + const size_t numShards = 256; + size_t step = sections.size() / numShards; + size_t boundaries[numShards + 1]; + boundaries[0] = 0; + boundaries[numShards] = sections.size(); + + parallelForEachN(1, numShards, [&](size_t i) { + boundaries[i] = findBoundary((i - 1) * step, sections.size()); }); - parallelForEachN(1, NumShards + 1, [&](size_t I) { - if (Boundaries[I - 1] < Boundaries[I]) - forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn); + parallelForEachN(1, numShards + 1, [&](size_t i) { + if (boundaries[i - 1] < boundaries[i]) + forEachClassRange(boundaries[i - 1], boundaries[i], fn); }); - ++Cnt; + ++cnt; } // Combine the hashes of the sections referenced by the given section into its // hash. template <class ELFT, class RelTy> -static void combineRelocHashes(InputSection *IS, ArrayRef<RelTy> Rels) { - uint32_t Hash = IS->Class[1]; - for (RelTy Rel : Rels) { - Symbol &S = IS->template getFile<ELFT>()->getRelocTargetSym(Rel); - if (auto *D = dyn_cast<Defined>(&S)) - if (auto *RelSec = dyn_cast_or_null<InputSection>(D->Section)) - Hash ^= RelSec->Class[1]; +static void combineRelocHashes(unsigned cnt, InputSection *isec, + ArrayRef<RelTy> rels) { + uint32_t hash = isec->eqClass[cnt % 2]; + for (RelTy rel : rels) { + Symbol &s = isec->template getFile<ELFT>()->getRelocTargetSym(rel); + if (auto *d = dyn_cast<Defined>(&s)) + if (auto *relSec = dyn_cast_or_null<InputSection>(d->section)) + hash += relSec->eqClass[cnt % 2]; } // Set MSB to 1 to avoid collisions with non-hash IDs. - IS->Class[0] = Hash | (1U << 31); + isec->eqClass[(cnt + 1) % 2] = hash | (1U << 31); } -static void print(const Twine &S) { - if (Config->PrintIcfSections) - message(S); +static void print(const Twine &s) { + if (config->printIcfSections) + message(s); } // The main function of ICF. template <class ELFT> void ICF<ELFT>::run() { // Collect sections to merge. - for (InputSectionBase *Sec : InputSections) - if (auto *S = dyn_cast<InputSection>(Sec)) - if (isEligible(S)) - Sections.push_back(S); + for (InputSectionBase *sec : inputSections) + if (auto *s = dyn_cast<InputSection>(sec)) + if (isEligible(s)) + sections.push_back(s); // Initially, we use hash values to partition sections. - parallelForEach(Sections, [&](InputSection *S) { - S->Class[1] = xxHash64(S->data()); + parallelForEach(sections, [&](InputSection *s) { + s->eqClass[0] = xxHash64(s->data()); }); - parallelForEach(Sections, [&](InputSection *S) { - if (S->AreRelocsRela) - combineRelocHashes<ELFT>(S, S->template relas<ELFT>()); - else - combineRelocHashes<ELFT>(S, S->template rels<ELFT>()); - }); + for (unsigned cnt = 0; cnt != 2; ++cnt) { + parallelForEach(sections, [&](InputSection *s) { + if (s->areRelocsRela) + combineRelocHashes<ELFT>(cnt, s, s->template relas<ELFT>()); + else + combineRelocHashes<ELFT>(cnt, s, s->template rels<ELFT>()); + }); + } // From now on, sections in Sections vector are ordered so that sections // in the same equivalence class are consecutive in the vector. - std::stable_sort(Sections.begin(), Sections.end(), - [](InputSection *A, InputSection *B) { - return A->Class[0] < B->Class[0]; - }); + llvm::stable_sort(sections, [](const InputSection *a, const InputSection *b) { + return a->eqClass[0] < b->eqClass[0]; + }); // Compare static contents and assign unique IDs for each static content. - forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); }); + forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); }); // Split groups by comparing relocations until convergence is obtained. do { - Repeat = false; + repeat = false; forEachClass( - [&](size_t Begin, size_t End) { segregate(Begin, End, false); }); - } while (Repeat); + [&](size_t begin, size_t end) { segregate(begin, end, false); }); + } while (repeat); - log("ICF needed " + Twine(Cnt) + " iterations"); + log("ICF needed " + Twine(cnt) + " iterations"); // Merge sections by the equivalence class. - forEachClassRange(0, Sections.size(), [&](size_t Begin, size_t End) { - if (End - Begin == 1) + forEachClassRange(0, sections.size(), [&](size_t begin, size_t end) { + if (end - begin == 1) return; - print("selected section " + toString(Sections[Begin])); - for (size_t I = Begin + 1; I < End; ++I) { - print(" removing identical section " + toString(Sections[I])); - Sections[Begin]->replace(Sections[I]); + print("selected section " + toString(sections[begin])); + for (size_t i = begin + 1; i < end; ++i) { + print(" removing identical section " + toString(sections[i])); + sections[begin]->replace(sections[i]); // At this point we know sections merged are fully identical and hence // we want to remove duplicate implicit dependencies such as link order // and relocation sections. - for (InputSection *IS : Sections[I]->DependentSections) - IS->Live = false; + for (InputSection *isec : sections[i]->dependentSections) + isec->markDead(); } }); } diff --git a/ELF/ICF.h b/ELF/ICF.h index a6c8636ead6d..ed828fc4a949 100644 --- a/ELF/ICF.h +++ b/ELF/ICF.h @@ -1,9 +1,8 @@ //===- ICF.h --------------------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp index e4d1dec7cbcb..98b88283cf09 100644 --- a/ELF/InputFiles.cpp +++ b/ELF/InputFiles.cpp @@ -1,13 +1,13 @@ //===- InputFiles.cpp -----------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "Driver.h" #include "InputSection.h" #include "LinkerScript.h" #include "SymbolTable.h" @@ -25,6 +25,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" @@ -34,145 +35,272 @@ using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::sys; using namespace llvm::sys::fs; +using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; -bool InputFile::IsInGroup; -uint32_t InputFile::NextGroupId; -std::vector<BinaryFile *> elf::BinaryFiles; -std::vector<BitcodeFile *> elf::BitcodeFiles; -std::vector<LazyObjFile *> elf::LazyObjFiles; -std::vector<InputFile *> elf::ObjectFiles; -std::vector<InputFile *> elf::SharedFiles; - -std::unique_ptr<TarWriter> elf::Tar; +bool InputFile::isInGroup; +uint32_t InputFile::nextGroupId; +std::vector<BinaryFile *> elf::binaryFiles; +std::vector<BitcodeFile *> elf::bitcodeFiles; +std::vector<LazyObjFile *> elf::lazyObjFiles; +std::vector<InputFile *> elf::objectFiles; +std::vector<SharedFile *> elf::sharedFiles; + +std::unique_ptr<TarWriter> elf::tar; + +static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) { + unsigned char size; + unsigned char endian; + std::tie(size, endian) = getElfArchType(mb.getBuffer()); + + auto report = [&](StringRef msg) { + StringRef filename = mb.getBufferIdentifier(); + if (archiveName.empty()) + fatal(filename + ": " + msg); + else + fatal(archiveName + "(" + filename + "): " + msg); + }; + + if (!mb.getBuffer().startswith(ElfMagic)) + report("not an ELF file"); + if (endian != ELFDATA2LSB && endian != ELFDATA2MSB) + report("corrupted ELF file: invalid data encoding"); + if (size != ELFCLASS32 && size != ELFCLASS64) + report("corrupted ELF file: invalid file class"); + + size_t bufSize = mb.getBuffer().size(); + if ((size == ELFCLASS32 && bufSize < sizeof(Elf32_Ehdr)) || + (size == ELFCLASS64 && bufSize < sizeof(Elf64_Ehdr))) + report("corrupted ELF file: file is too short"); + + if (size == ELFCLASS32) + return (endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind; + return (endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind; +} -InputFile::InputFile(Kind K, MemoryBufferRef M) - : MB(M), GroupId(NextGroupId), FileKind(K) { +InputFile::InputFile(Kind k, MemoryBufferRef m) + : mb(m), groupId(nextGroupId), fileKind(k) { // All files within the same --{start,end}-group get the same group ID. // Otherwise, a new file will get a new group ID. - if (!IsInGroup) - ++NextGroupId; + if (!isInGroup) + ++nextGroupId; } -Optional<MemoryBufferRef> elf::readFile(StringRef Path) { +Optional<MemoryBufferRef> elf::readFile(StringRef path) { // The --chroot option changes our virtual root directory. // This is useful when you are dealing with files created by --reproduce. - if (!Config->Chroot.empty() && Path.startswith("/")) - Path = Saver.save(Config->Chroot + Path); + if (!config->chroot.empty() && path.startswith("/")) + path = saver.save(config->chroot + path); - log(Path); + log(path); - auto MBOrErr = MemoryBuffer::getFile(Path, -1, false); - if (auto EC = MBOrErr.getError()) { - error("cannot open " + Path + ": " + EC.message()); + auto mbOrErr = MemoryBuffer::getFile(path, -1, false); + if (auto ec = mbOrErr.getError()) { + error("cannot open " + path + ": " + ec.message()); return None; } - std::unique_ptr<MemoryBuffer> &MB = *MBOrErr; - MemoryBufferRef MBRef = MB->getMemBufferRef(); - make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take MB ownership + std::unique_ptr<MemoryBuffer> &mb = *mbOrErr; + MemoryBufferRef mbref = mb->getMemBufferRef(); + make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take MB ownership + + if (tar) + tar->append(relativeToRoot(path), mbref.getBuffer()); + return mbref; +} + +// All input object files must be for the same architecture +// (e.g. it does not make sense to link x86 object files with +// MIPS object files.) This function checks for that error. +static bool isCompatible(InputFile *file) { + if (!file->isElf() && !isa<BitcodeFile>(file)) + return true; + + if (file->ekind == config->ekind && file->emachine == config->emachine) { + if (config->emachine != EM_MIPS) + return true; + if (isMipsN32Abi(file) == config->mipsN32Abi) + return true; + } + + if (!config->emulation.empty()) { + error(toString(file) + " is incompatible with " + config->emulation); + } else { + InputFile *existing; + if (!objectFiles.empty()) + existing = objectFiles[0]; + else if (!sharedFiles.empty()) + existing = sharedFiles[0]; + else + existing = bitcodeFiles[0]; + + error(toString(file) + " is incompatible with " + toString(existing)); + } + + return false; +} + +template <class ELFT> static void doParseFile(InputFile *file) { + if (!isCompatible(file)) + return; + + // Binary file + if (auto *f = dyn_cast<BinaryFile>(file)) { + binaryFiles.push_back(f); + f->parse(); + return; + } + + // .a file + if (auto *f = dyn_cast<ArchiveFile>(file)) { + f->parse(); + return; + } + + // Lazy object file + if (auto *f = dyn_cast<LazyObjFile>(file)) { + lazyObjFiles.push_back(f); + f->parse<ELFT>(); + return; + } + + if (config->trace) + message(toString(file)); + + // .so file + if (auto *f = dyn_cast<SharedFile>(file)) { + f->parse<ELFT>(); + return; + } + + // LLVM bitcode file + if (auto *f = dyn_cast<BitcodeFile>(file)) { + bitcodeFiles.push_back(f); + f->parse<ELFT>(); + return; + } - if (Tar) - Tar->append(relativeToRoot(Path), MBRef.getBuffer()); - return MBRef; + // Regular object file + objectFiles.push_back(file); + cast<ObjFile<ELFT>>(file)->parse(); +} + +// Add symbols in File to the symbol table. +void elf::parseFile(InputFile *file) { + switch (config->ekind) { + case ELF32LEKind: + doParseFile<ELF32LE>(file); + return; + case ELF32BEKind: + doParseFile<ELF32BE>(file); + return; + case ELF64LEKind: + doParseFile<ELF64LE>(file); + return; + case ELF64BEKind: + doParseFile<ELF64BE>(file); + return; + default: + llvm_unreachable("unknown ELFT"); + } } // Concatenates arguments to construct a string representing an error location. -static std::string createFileLineMsg(StringRef Path, unsigned Line) { - std::string Filename = path::filename(Path); - std::string Lineno = ":" + std::to_string(Line); - if (Filename == Path) - return Filename + Lineno; - return Filename + Lineno + " (" + Path.str() + Lineno + ")"; +static std::string createFileLineMsg(StringRef path, unsigned line) { + std::string filename = path::filename(path); + std::string lineno = ":" + std::to_string(line); + if (filename == path) + return filename + lineno; + return filename + lineno + " (" + path.str() + lineno + ")"; } template <class ELFT> -static std::string getSrcMsgAux(ObjFile<ELFT> &File, const Symbol &Sym, - InputSectionBase &Sec, uint64_t Offset) { +static std::string getSrcMsgAux(ObjFile<ELFT> &file, const Symbol &sym, + InputSectionBase &sec, uint64_t offset) { // In DWARF, functions and variables are stored to different places. // First, lookup a function for a given offset. - if (Optional<DILineInfo> Info = File.getDILineInfo(&Sec, Offset)) - return createFileLineMsg(Info->FileName, Info->Line); + if (Optional<DILineInfo> info = file.getDILineInfo(&sec, offset)) + return createFileLineMsg(info->FileName, info->Line); // If it failed, lookup again as a variable. - if (Optional<std::pair<std::string, unsigned>> FileLine = - File.getVariableLoc(Sym.getName())) - return createFileLineMsg(FileLine->first, FileLine->second); + if (Optional<std::pair<std::string, unsigned>> fileLine = + file.getVariableLoc(sym.getName())) + return createFileLineMsg(fileLine->first, fileLine->second); - // File.SourceFile contains STT_FILE symbol, and that is a last resort. - return File.SourceFile; + // File.sourceFile contains STT_FILE symbol, and that is a last resort. + return file.sourceFile; } -std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec, - uint64_t Offset) { +std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec, + uint64_t offset) { if (kind() != ObjKind) return ""; - switch (Config->EKind) { + switch (config->ekind) { default: llvm_unreachable("Invalid kind"); case ELF32LEKind: - return getSrcMsgAux(cast<ObjFile<ELF32LE>>(*this), Sym, Sec, Offset); + return getSrcMsgAux(cast<ObjFile<ELF32LE>>(*this), sym, sec, offset); case ELF32BEKind: - return getSrcMsgAux(cast<ObjFile<ELF32BE>>(*this), Sym, Sec, Offset); + return getSrcMsgAux(cast<ObjFile<ELF32BE>>(*this), sym, sec, offset); case ELF64LEKind: - return getSrcMsgAux(cast<ObjFile<ELF64LE>>(*this), Sym, Sec, Offset); + return getSrcMsgAux(cast<ObjFile<ELF64LE>>(*this), sym, sec, offset); case ELF64BEKind: - return getSrcMsgAux(cast<ObjFile<ELF64BE>>(*this), Sym, Sec, Offset); + return getSrcMsgAux(cast<ObjFile<ELF64BE>>(*this), sym, sec, offset); } } template <class ELFT> void ObjFile<ELFT>::initializeDwarf() { - Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this)); - for (std::unique_ptr<DWARFUnit> &CU : Dwarf->compile_units()) { - auto Report = [](Error Err) { - handleAllErrors(std::move(Err), - [](ErrorInfoBase &Info) { warn(Info.message()); }); + dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this)); + for (std::unique_ptr<DWARFUnit> &cu : dwarf->compile_units()) { + auto report = [](Error err) { + handleAllErrors(std::move(err), + [](ErrorInfoBase &info) { warn(info.message()); }); }; - Expected<const DWARFDebugLine::LineTable *> ExpectedLT = - Dwarf->getLineTableForUnit(CU.get(), Report); - const DWARFDebugLine::LineTable *LT = nullptr; - if (ExpectedLT) - LT = *ExpectedLT; + Expected<const DWARFDebugLine::LineTable *> expectedLT = + dwarf->getLineTableForUnit(cu.get(), report); + const DWARFDebugLine::LineTable *lt = nullptr; + if (expectedLT) + lt = *expectedLT; else - Report(ExpectedLT.takeError()); - if (!LT) + report(expectedLT.takeError()); + if (!lt) continue; - LineTables.push_back(LT); + lineTables.push_back(lt); - // Loop over variable records and insert them to VariableLoc. - for (const auto &Entry : CU->dies()) { - DWARFDie Die(CU.get(), &Entry); + // Loop over variable records and insert them to variableLoc. + for (const auto &entry : cu->dies()) { + DWARFDie die(cu.get(), &entry); // Skip all tags that are not variables. - if (Die.getTag() != dwarf::DW_TAG_variable) + if (die.getTag() != dwarf::DW_TAG_variable) continue; // Skip if a local variable because we don't need them for generating // error messages. In general, only non-local symbols can fail to be // linked. - if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0)) + if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0)) continue; // Get the source filename index for the variable. - unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0); - if (!LT->hasFileAtIndex(File)) + unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0); + if (!lt->hasFileAtIndex(file)) continue; // Get the line number on which the variable is declared. - unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0); + unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0); - // Here we want to take the variable name to add it into VariableLoc. + // Here we want to take the variable name to add it into variableLoc. // Variable can have regular and linkage name associated. At first, we try // to get linkage name as it can be different, for example when we have // two variables in different namespaces of the same object. Use common // name otherwise, but handle the case when it also absent in case if the // input object file lacks some debug info. - StringRef Name = - dwarf::toString(Die.find(dwarf::DW_AT_linkage_name), - dwarf::toString(Die.find(dwarf::DW_AT_name), "")); - if (!Name.empty()) - VariableLoc.insert({Name, {LT, File, Line}}); + StringRef name = + dwarf::toString(die.find(dwarf::DW_AT_linkage_name), + dwarf::toString(die.find(dwarf::DW_AT_name), "")); + if (!name.empty()) + variableLoc.insert({name, {lt, file, line}}); } } } @@ -181,112 +309,152 @@ template <class ELFT> void ObjFile<ELFT>::initializeDwarf() { // object (variable, array, etc) definition. template <class ELFT> Optional<std::pair<std::string, unsigned>> -ObjFile<ELFT>::getVariableLoc(StringRef Name) { - llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); }); +ObjFile<ELFT>::getVariableLoc(StringRef name) { + llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); // Return if we have no debug information about data object. - auto It = VariableLoc.find(Name); - if (It == VariableLoc.end()) + auto it = variableLoc.find(name); + if (it == variableLoc.end()) return None; // Take file name string from line table. - std::string FileName; - if (!It->second.LT->getFileNameByIndex( - It->second.File, nullptr, - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName)) + std::string fileName; + if (!it->second.lt->getFileNameByIndex( + it->second.file, {}, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName)) return None; - return std::make_pair(FileName, It->second.Line); + return std::make_pair(fileName, it->second.line); } // Returns source line information for a given offset // using DWARF debug info. template <class ELFT> -Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S, - uint64_t Offset) { - llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); }); +Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s, + uint64_t offset) { + llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); + + // Detect SectionIndex for specified section. + uint64_t sectionIndex = object::SectionedAddress::UndefSection; + ArrayRef<InputSectionBase *> sections = s->file->getSections(); + for (uint64_t curIndex = 0; curIndex < sections.size(); ++curIndex) { + if (s == sections[curIndex]) { + sectionIndex = curIndex; + break; + } + } // Use fake address calcuated by adding section file offset and offset in // section. See comments for ObjectInfo class. - DILineInfo Info; - for (const llvm::DWARFDebugLine::LineTable *LT : LineTables) - if (LT->getFileLineInfoForAddress( - S->getOffsetInFile() + Offset, nullptr, - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info)) - return Info; + DILineInfo info; + for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) { + if (lt->getFileLineInfoForAddress( + {s->getOffsetInFile() + offset, sectionIndex}, nullptr, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info)) + return info; + } return None; } // Returns "<internal>", "foo.a(bar.o)" or "baz.o". -std::string lld::toString(const InputFile *F) { - if (!F) +std::string lld::toString(const InputFile *f) { + if (!f) return "<internal>"; - if (F->ToStringCache.empty()) { - if (F->ArchiveName.empty()) - F->ToStringCache = F->getName(); + if (f->toStringCache.empty()) { + if (f->archiveName.empty()) + f->toStringCache = f->getName(); else - F->ToStringCache = (F->ArchiveName + "(" + F->getName() + ")").str(); + f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str(); } - return F->ToStringCache; + return f->toStringCache; } -template <class ELFT> -ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) { - if (ELFT::TargetEndianness == support::little) - EKind = ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind; - else - EKind = ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind; +ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) { + ekind = getELFKind(mb, ""); - EMachine = getObj().getHeader()->e_machine; - OSABI = getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI]; + switch (ekind) { + case ELF32LEKind: + init<ELF32LE>(); + break; + case ELF32BEKind: + init<ELF32BE>(); + break; + case ELF64LEKind: + init<ELF64LE>(); + break; + case ELF64BEKind: + init<ELF64BE>(); + break; + default: + llvm_unreachable("getELFKind"); + } } -template <class ELFT> -typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() { - return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end()); +template <typename Elf_Shdr> +static const Elf_Shdr *findSection(ArrayRef<Elf_Shdr> sections, uint32_t type) { + for (const Elf_Shdr &sec : sections) + if (sec.sh_type == type) + return &sec; + return nullptr; } -template <class ELFT> -uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const { - return CHECK(getObj().getSectionIndex(&Sym, ELFSyms, SymtabSHNDX), this); -} +template <class ELFT> void ELFFileBase::init() { + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; -template <class ELFT> -void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections, - const Elf_Shdr *Symtab) { - FirstGlobal = Symtab->sh_info; - ELFSyms = CHECK(getObj().symbols(Symtab), this); - if (FirstGlobal == 0 || FirstGlobal > ELFSyms.size()) + // Initialize trivial attributes. + const ELFFile<ELFT> &obj = getObj<ELFT>(); + emachine = obj.getHeader()->e_machine; + osabi = obj.getHeader()->e_ident[llvm::ELF::EI_OSABI]; + abiVersion = obj.getHeader()->e_ident[llvm::ELF::EI_ABIVERSION]; + + ArrayRef<Elf_Shdr> sections = CHECK(obj.sections(), this); + + // Find a symbol table. + bool isDSO = + (identify_magic(mb.getBuffer()) == file_magic::elf_shared_object); + const Elf_Shdr *symtabSec = + findSection(sections, isDSO ? SHT_DYNSYM : SHT_SYMTAB); + + if (!symtabSec) + return; + + // Initialize members corresponding to a symbol table. + firstGlobal = symtabSec->sh_info; + + ArrayRef<Elf_Sym> eSyms = CHECK(obj.symbols(symtabSec), this); + if (firstGlobal == 0 || firstGlobal > eSyms.size()) fatal(toString(this) + ": invalid sh_info in symbol table"); - StringTable = - CHECK(getObj().getStringTableForSymtab(*Symtab, Sections), this); + elfSyms = reinterpret_cast<const void *>(eSyms.data()); + numELFSyms = eSyms.size(); + stringTable = CHECK(obj.getStringTableForSymtab(*symtabSec, sections), this); } template <class ELFT> -ObjFile<ELFT>::ObjFile(MemoryBufferRef M, StringRef ArchiveName) - : ELFFileBase<ELFT>(Base::ObjKind, M) { - this->ArchiveName = ArchiveName; +uint32_t ObjFile<ELFT>::getSectionIndex(const Elf_Sym &sym) const { + return CHECK( + this->getObj().getSectionIndex(&sym, getELFSyms<ELFT>(), shndxTable), + this); } template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() { - if (this->Symbols.empty()) + if (this->symbols.empty()) return {}; - return makeArrayRef(this->Symbols).slice(1, this->FirstGlobal - 1); + return makeArrayRef(this->symbols).slice(1, this->firstGlobal - 1); } template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getGlobalSymbols() { - return makeArrayRef(this->Symbols).slice(this->FirstGlobal); + return makeArrayRef(this->symbols).slice(this->firstGlobal); } -template <class ELFT> -void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) { - // Read a section table. JustSymbols is usually false. - if (this->JustSymbols) +template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) { + // Read a section table. justSymbols is usually false. + if (this->justSymbols) initializeJustSymbols(); else - initializeSections(ComdatGroups); + initializeSections(ignoreComdats); // Read a symbol table. initializeSymbols(); @@ -296,17 +464,13 @@ void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) { // They are identified and deduplicated by group name. This function // returns a group name. template <class ELFT> -StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections, - const Elf_Shdr &Sec) { - // Group signatures are stored as symbol names in object files. - // sh_info contains a symbol index, so we fetch a symbol and read its name. - if (this->ELFSyms.empty()) - this->initSymtab( - Sections, CHECK(object::getSection<ELFT>(Sections, Sec.sh_link), this)); - - const Elf_Sym *Sym = - CHECK(object::getSymbol<ELFT>(this->ELFSyms, Sec.sh_info), this); - StringRef Signature = CHECK(Sym->getName(this->StringTable), this); +StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> sections, + const Elf_Shdr &sec) { + typename ELFT::SymRange symbols = this->getELFSyms<ELFT>(); + if (sec.sh_info >= symbols.size()) + fatal(toString(this) + ": invalid symbol index"); + const typename ELFT::Sym &sym = symbols[sec.sh_info]; + StringRef signature = CHECK(sym.getName(this->stringTable), this); // As a special case, if a symbol is a section symbol and has no name, // we use a section name as a signature. @@ -315,23 +479,12 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections, // standard, but GNU gold 1.14 (the newest version as of July 2017) or // older produce such sections as outputs for the -r option, so we need // a bug-compatibility. - if (Signature.empty() && Sym->getType() == STT_SECTION) - return getSectionName(Sec); - return Signature; -} - -template <class ELFT> -ArrayRef<typename ObjFile<ELFT>::Elf_Word> -ObjFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) { - const ELFFile<ELFT> &Obj = this->getObj(); - ArrayRef<Elf_Word> Entries = - CHECK(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec), this); - if (Entries.empty() || Entries[0] != GRP_COMDAT) - fatal(toString(this) + ": unsupported SHT_GROUP format"); - return Entries.slice(1); + if (signature.empty() && sym.getType() == STT_SECTION) + return getSectionName(sec); + return signature; } -template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) { +template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec) { // On a regular link we don't merge sections if -O0 (default is -O1). This // sometimes makes the linker significantly faster, although the output will // be bigger. @@ -344,14 +497,14 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) { // SHF_MERGE sections based both on their name and sh_entsize, but that seems // to be more trouble than it is worth. Instead, we just use the regular (-O1) // logic for -r. - if (Config->Optimize == 0 && !Config->Relocatable) + if (config->optimize == 0 && !config->relocatable) return false; // A mergeable section with size 0 is useless because they don't have // any data to merge. A mergeable string section with size 0 can be // argued as invalid because it doesn't end with a null character. // We'll avoid a mess by handling them as if they were non-mergeable. - if (Sec.sh_size == 0) + if (sec.sh_size == 0) return false; // Check for sh_entsize. The ELF spec is not clear about the zero @@ -359,17 +512,17 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) { // the section does not hold a table of fixed-size entries". We know // that Rust 1.13 produces a string mergeable section with a zero // sh_entsize. Here we just accept it rather than being picky about it. - uint64_t EntSize = Sec.sh_entsize; - if (EntSize == 0) + uint64_t entSize = sec.sh_entsize; + if (entSize == 0) return false; - if (Sec.sh_size % EntSize) + if (sec.sh_size % entSize) fatal(toString(this) + ": SHF_MERGE section size must be a multiple of sh_entsize"); - uint64_t Flags = Sec.sh_flags; - if (!(Flags & SHF_MERGE)) + uint64_t flags = sec.sh_flags; + if (!(flags & SHF_MERGE)) return false; - if (Flags & SHF_WRITE) + if (flags & SHF_WRITE) fatal(toString(this) + ": writable SHF_MERGE section is not supported"); return true; @@ -385,118 +538,138 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) { // When the option is given, we link "just symbols". The section table is // initialized with null pointers. template <class ELFT> void ObjFile<ELFT>::initializeJustSymbols() { - ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this); - this->Sections.resize(ObjSections.size()); + ArrayRef<Elf_Shdr> sections = CHECK(this->getObj().sections(), this); + this->sections.resize(sections.size()); +} - for (const Elf_Shdr &Sec : ObjSections) { - if (Sec.sh_type != SHT_SYMTAB) - continue; - this->initSymtab(ObjSections, &Sec); +// An ELF object file may contain a `.deplibs` section. If it exists, the +// section contains a list of library specifiers such as `m` for libm. This +// function resolves a given name by finding the first matching library checking +// the various ways that a library can be specified to LLD. This ELF extension +// is a form of autolinking and is called `dependent libraries`. It is currently +// unique to LLVM and lld. +static void addDependentLibrary(StringRef specifier, const InputFile *f) { + if (!config->dependentLibraries) return; - } + if (fs::exists(specifier)) + driver->addFile(specifier, /*withLOption=*/false); + else if (Optional<std::string> s = findFromSearchPaths(specifier)) + driver->addFile(*s, /*withLOption=*/true); + else if (Optional<std::string> s = searchLibraryBaseName(specifier)) + driver->addFile(*s, /*withLOption=*/true); + else + error(toString(f) + + ": unable to find library from dependent library specifier: " + + specifier); } template <class ELFT> -void ObjFile<ELFT>::initializeSections( - DenseSet<CachedHashStringRef> &ComdatGroups) { - const ELFFile<ELFT> &Obj = this->getObj(); - - ArrayRef<Elf_Shdr> ObjSections = CHECK(Obj.sections(), this); - uint64_t Size = ObjSections.size(); - this->Sections.resize(Size); - this->SectionStringTable = - CHECK(Obj.getSectionStringTable(ObjSections), this); - - for (size_t I = 0, E = ObjSections.size(); I < E; I++) { - if (this->Sections[I] == &InputSection::Discarded) +void ObjFile<ELFT>::initializeSections(bool ignoreComdats) { + const ELFFile<ELFT> &obj = this->getObj(); + + ArrayRef<Elf_Shdr> objSections = CHECK(obj.sections(), this); + uint64_t size = objSections.size(); + this->sections.resize(size); + this->sectionStringTable = + CHECK(obj.getSectionStringTable(objSections), this); + + for (size_t i = 0, e = objSections.size(); i < e; i++) { + if (this->sections[i] == &InputSection::discarded) continue; - const Elf_Shdr &Sec = ObjSections[I]; + const Elf_Shdr &sec = objSections[i]; - if (Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) - CGProfile = check( - this->getObj().template getSectionContentsAsArray<Elf_CGProfile>( - &Sec)); + if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) + cgProfile = + check(obj.template getSectionContentsAsArray<Elf_CGProfile>(&sec)); // SHF_EXCLUDE'ed sections are discarded by the linker. However, // if -r is given, we'll let the final link discard such sections. // This is compatible with GNU. - if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) { - if (Sec.sh_type == SHT_LLVM_ADDRSIG) { + if ((sec.sh_flags & SHF_EXCLUDE) && !config->relocatable) { + if (sec.sh_type == SHT_LLVM_ADDRSIG) { // We ignore the address-significance table if we know that the object // file was created by objcopy or ld -r. This is because these tools // will reorder the symbols in the symbol table, invalidating the data // in the address-significance table, which refers to symbols by index. - if (Sec.sh_link != 0) - this->AddrsigSec = &Sec; - else if (Config->ICF == ICFLevel::Safe) + if (sec.sh_link != 0) + this->addrsigSec = &sec; + else if (config->icf == ICFLevel::Safe) warn(toString(this) + ": --icf=safe is incompatible with object " "files created using objcopy or ld -r"); } - this->Sections[I] = &InputSection::Discarded; + this->sections[i] = &InputSection::discarded; continue; } - switch (Sec.sh_type) { + switch (sec.sh_type) { case SHT_GROUP: { // De-duplicate section groups by their signatures. - StringRef Signature = getShtGroupSignature(ObjSections, Sec); - bool IsNew = ComdatGroups.insert(CachedHashStringRef(Signature)).second; - this->Sections[I] = &InputSection::Discarded; - - // We only support GRP_COMDAT type of group. Get the all entries of the - // section here to let getShtGroupEntries to check the type early for us. - ArrayRef<Elf_Word> Entries = getShtGroupEntries(Sec); - - // If it is a new section group, we want to keep group members. - // Group leader sections, which contain indices of group members, are - // discarded because they are useless beyond this point. The only - // exception is the -r option because in order to produce re-linkable - // object files, we want to pass through basically everything. - if (IsNew) { - if (Config->Relocatable) - this->Sections[I] = createInputSection(Sec); + StringRef signature = getShtGroupSignature(objSections, sec); + this->sections[i] = &InputSection::discarded; + + + ArrayRef<Elf_Word> entries = + CHECK(obj.template getSectionContentsAsArray<Elf_Word>(&sec), this); + if (entries.empty()) + fatal(toString(this) + ": empty SHT_GROUP"); + + // The first word of a SHT_GROUP section contains flags. Currently, + // the standard defines only "GRP_COMDAT" flag for the COMDAT group. + // An group with the empty flag doesn't define anything; such sections + // are just skipped. + if (entries[0] == 0) + continue; + + if (entries[0] != GRP_COMDAT) + fatal(toString(this) + ": unsupported SHT_GROUP format"); + + bool isNew = + ignoreComdats || + symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this) + .second; + if (isNew) { + if (config->relocatable) + this->sections[i] = createInputSection(sec); continue; } // Otherwise, discard group members. - for (uint32_t SecIndex : Entries) { - if (SecIndex >= Size) + for (uint32_t secIndex : entries.slice(1)) { + if (secIndex >= size) fatal(toString(this) + - ": invalid section index in group: " + Twine(SecIndex)); - this->Sections[SecIndex] = &InputSection::Discarded; + ": invalid section index in group: " + Twine(secIndex)); + this->sections[secIndex] = &InputSection::discarded; } break; } - case SHT_SYMTAB: - this->initSymtab(ObjSections, &Sec); - break; case SHT_SYMTAB_SHNDX: - this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, ObjSections), this); + shndxTable = CHECK(obj.getSHNDXTable(sec, objSections), this); break; + case SHT_SYMTAB: case SHT_STRTAB: case SHT_NULL: break; default: - this->Sections[I] = createInputSection(Sec); + this->sections[i] = createInputSection(sec); } // .ARM.exidx sections have a reverse dependency on the InputSection they // have a SHF_LINK_ORDER dependency, this is identified by the sh_link. - if (Sec.sh_flags & SHF_LINK_ORDER) { - InputSectionBase *LinkSec = nullptr; - if (Sec.sh_link < this->Sections.size()) - LinkSec = this->Sections[Sec.sh_link]; - if (!LinkSec) + if (sec.sh_flags & SHF_LINK_ORDER) { + InputSectionBase *linkSec = nullptr; + if (sec.sh_link < this->sections.size()) + linkSec = this->sections[sec.sh_link]; + if (!linkSec) fatal(toString(this) + - ": invalid sh_link index: " + Twine(Sec.sh_link)); + ": invalid sh_link index: " + Twine(sec.sh_link)); - InputSection *IS = cast<InputSection>(this->Sections[I]); - LinkSec->DependentSections.push_back(IS); - if (!isa<InputSection>(LinkSec)) - error("a section " + IS->Name + + InputSection *isec = cast<InputSection>(this->sections[i]); + linkSec->dependentSections.push_back(isec); + if (!isa<InputSection>(linkSec)) + error("a section " + isec->name + " with SHF_LINK_ORDER should not refer a non-regular " "section: " + - toString(LinkSec)); + toString(linkSec)); } } } @@ -504,9 +677,9 @@ void ObjFile<ELFT>::initializeSections( // For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD // flag in the ELF Header we need to look at Tag_ABI_VFP_args to find out how // the input objects have been compiled. -static void updateARMVFPArgs(const ARMAttributeParser &Attributes, - const InputFile *F) { - if (!Attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args)) +static void updateARMVFPArgs(const ARMAttributeParser &attributes, + const InputFile *f) { + if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args)) // If an ABI tag isn't present then it is implicitly given the value of 0 // which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files, // including some in glibc that don't use FP args (and should have value 3) @@ -514,31 +687,31 @@ static void updateARMVFPArgs(const ARMAttributeParser &Attributes, // as a clash. return; - unsigned VFPArgs = Attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); - ARMVFPArgKind Arg; - switch (VFPArgs) { + unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); + ARMVFPArgKind arg; + switch (vfpArgs) { case ARMBuildAttrs::BaseAAPCS: - Arg = ARMVFPArgKind::Base; + arg = ARMVFPArgKind::Base; break; case ARMBuildAttrs::HardFPAAPCS: - Arg = ARMVFPArgKind::VFP; + arg = ARMVFPArgKind::VFP; break; case ARMBuildAttrs::ToolChainFPPCS: // Tool chain specific convention that conforms to neither AAPCS variant. - Arg = ARMVFPArgKind::ToolChain; + arg = ARMVFPArgKind::ToolChain; break; case ARMBuildAttrs::CompatibleFPAAPCS: // Object compatible with all conventions. return; default: - error(toString(F) + ": unknown Tag_ABI_VFP_args value: " + Twine(VFPArgs)); + error(toString(f) + ": unknown Tag_ABI_VFP_args value: " + Twine(vfpArgs)); return; } // Follow ld.bfd and error if there is a mix of calling conventions. - if (Config->ARMVFPArgs != Arg && Config->ARMVFPArgs != ARMVFPArgKind::Default) - error(toString(F) + ": incompatible Tag_ABI_VFP_args"); + if (config->armVFPArgs != arg && config->armVFPArgs != ARMVFPArgKind::Default) + error(toString(f) + ": incompatible Tag_ABI_VFP_args"); else - Config->ARMVFPArgs = Arg; + config->armVFPArgs = arg; } // The ARM support in lld makes some use of instructions that are not available @@ -550,11 +723,11 @@ static void updateARMVFPArgs(const ARMAttributeParser &Attributes, // at compile time. We follow the convention that if at least one input object // is compiled with an architecture that supports these features then lld is // permitted to use them. -static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) { - if (!Attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) +static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) { + if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) return; - auto Arch = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); - switch (Arch) { + auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + switch (arch) { case ARMBuildAttrs::Pre_v4: case ARMBuildAttrs::v4: case ARMBuildAttrs::v4T: @@ -566,70 +739,156 @@ static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) { case ARMBuildAttrs::v6: case ARMBuildAttrs::v6KZ: case ARMBuildAttrs::v6K: - Config->ARMHasBlx = true; + config->armHasBlx = true; // Architectures used in pre-Cortex processors do not support // The J1 = 1 J2 = 1 Thumb branch range extension, with the exception // of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do. break; default: // All other Architectures have BLX and extended branch encoding - Config->ARMHasBlx = true; - Config->ARMJ1J2BranchEncoding = true; - if (Arch != ARMBuildAttrs::v6_M && Arch != ARMBuildAttrs::v6S_M) + config->armHasBlx = true; + config->armJ1J2BranchEncoding = true; + if (arch != ARMBuildAttrs::v6_M && arch != ARMBuildAttrs::v6S_M) // All Architectures used in Cortex processors with the exception // of v6-M and v6S-M have the MOVT and MOVW instructions. - Config->ARMHasMovtMovw = true; + config->armHasMovtMovw = true; break; } } +// If a source file is compiled with x86 hardware-assisted call flow control +// enabled, the generated object file contains feature flags indicating that +// fact. This function reads the feature flags and returns it. +// +// Essentially we want to read a single 32-bit value in this function, but this +// function is rather complicated because the value is buried deep inside a +// .note.gnu.property section. +// +// The section consists of one or more NOTE records. Each NOTE record consists +// of zero or more type-length-value fields. We want to find a field of a +// certain type. It seems a bit too much to just store a 32-bit value, perhaps +// the ABI is unnecessarily complicated. +template <class ELFT> +static uint32_t readAndFeatures(ObjFile<ELFT> *obj, ArrayRef<uint8_t> data) { + using Elf_Nhdr = typename ELFT::Nhdr; + using Elf_Note = typename ELFT::Note; + + uint32_t featuresSet = 0; + while (!data.empty()) { + // Read one NOTE record. + if (data.size() < sizeof(Elf_Nhdr)) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data()); + if (data.size() < nhdr->getSize()) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + Elf_Note note(*nhdr); + if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") { + data = data.slice(nhdr->getSize()); + continue; + } + + uint32_t featureAndType = config->emachine == EM_AARCH64 + ? GNU_PROPERTY_AARCH64_FEATURE_1_AND + : GNU_PROPERTY_X86_FEATURE_1_AND; + + // Read a body of a NOTE record, which consists of type-length-value fields. + ArrayRef<uint8_t> desc = note.getDesc(); + while (!desc.empty()) { + if (desc.size() < 8) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + uint32_t type = read32le(desc.data()); + uint32_t size = read32le(desc.data() + 4); + + if (type == featureAndType) { + // We found a FEATURE_1_AND field. There may be more than one of these + // in a .note.gnu.propery section, for a relocatable object we + // accumulate the bits set. + featuresSet |= read32le(desc.data() + 8); + } + + // On 64-bit, a payload may be followed by a 4-byte padding to make its + // size a multiple of 8. + if (ELFT::Is64Bits) + size = alignTo(size, 8); + + desc = desc.slice(size + 8); // +8 for Type and Size + } + + // Go to next NOTE record to look for more FEATURE_1_AND descriptions. + data = data.slice(nhdr->getSize()); + } + + return featuresSet; +} + template <class ELFT> -InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) { - uint32_t Idx = Sec.sh_info; - if (Idx >= this->Sections.size()) - fatal(toString(this) + ": invalid relocated section index: " + Twine(Idx)); - InputSectionBase *Target = this->Sections[Idx]; +InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &sec) { + uint32_t idx = sec.sh_info; + if (idx >= this->sections.size()) + fatal(toString(this) + ": invalid relocated section index: " + Twine(idx)); + InputSectionBase *target = this->sections[idx]; // Strictly speaking, a relocation section must be included in the // group of the section it relocates. However, LLVM 3.3 and earlier // would fail to do so, so we gracefully handle that case. - if (Target == &InputSection::Discarded) + if (target == &InputSection::discarded) return nullptr; - if (!Target) + if (!target) fatal(toString(this) + ": unsupported relocation reference"); - return Target; + return target; } // Create a regular InputSection class that has the same contents // as a given section. -static InputSection *toRegularSection(MergeInputSection *Sec) { - return make<InputSection>(Sec->File, Sec->Flags, Sec->Type, Sec->Alignment, - Sec->data(), Sec->Name); +static InputSection *toRegularSection(MergeInputSection *sec) { + return make<InputSection>(sec->file, sec->flags, sec->type, sec->alignment, + sec->data(), sec->name); } template <class ELFT> -InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { - StringRef Name = getSectionName(Sec); +InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &sec) { + StringRef name = getSectionName(sec); - switch (Sec.sh_type) { + switch (sec.sh_type) { case SHT_ARM_ATTRIBUTES: { - if (Config->EMachine != EM_ARM) + if (config->emachine != EM_ARM) break; - ARMAttributeParser Attributes; - ArrayRef<uint8_t> Contents = check(this->getObj().getSectionContents(&Sec)); - Attributes.Parse(Contents, /*isLittle*/ Config->EKind == ELF32LEKind); - updateSupportedARMFeatures(Attributes); - updateARMVFPArgs(Attributes, this); + ARMAttributeParser attributes; + ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(&sec)); + attributes.Parse(contents, /*isLittle*/ config->ekind == ELF32LEKind); + updateSupportedARMFeatures(attributes); + updateARMVFPArgs(attributes, this); // FIXME: Retain the first attribute section we see. The eglibc ARM // dynamic loaders require the presence of an attribute section for dlopen // to work. In a full implementation we would merge all attribute sections. - if (In.ARMAttributes == nullptr) { - In.ARMAttributes = make<InputSection>(*this, Sec, Name); - return In.ARMAttributes; + if (in.armAttributes == nullptr) { + in.armAttributes = make<InputSection>(*this, sec, name); + return in.armAttributes; } - return &InputSection::Discarded; + return &InputSection::discarded; + } + case SHT_LLVM_DEPENDENT_LIBRARIES: { + if (config->relocatable) + break; + ArrayRef<char> data = + CHECK(this->getObj().template getSectionContentsAsArray<char>(&sec), this); + if (!data.empty() && data.back() != '\0') { + error(toString(this) + + ": corrupted dependent libraries section (unterminated string): " + + name); + return &InputSection::discarded; + } + for (const char *d = data.begin(), *e = data.end(); d < e;) { + StringRef s(d); + addDependentLibrary(s, this); + d += s.size() + 1; + } + return &InputSection::discarded; } case SHT_RELA: case SHT_REL: { @@ -638,25 +897,25 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { // and the group is discarded, even though it's a violation of the // spec. We handle that situation gracefully by discarding dangling // relocation sections. - InputSectionBase *Target = getRelocTarget(Sec); - if (!Target) + InputSectionBase *target = getRelocTarget(sec); + if (!target) return nullptr; // This section contains relocation information. // If -r is given, we do not interpret or apply relocation // but just copy relocation sections to output. - if (Config->Relocatable) { - InputSection *RelocSec = make<InputSection>(*this, Sec, Name); + if (config->relocatable) { + InputSection *relocSec = make<InputSection>(*this, sec, name); // We want to add a dependency to target, similar like we do for // -emit-relocs below. This is useful for the case when linker script // contains the "/DISCARD/". It is perhaps uncommon to use a script with // -r, but we faced it in the Linux kernel and have to handle such case // and not to crash. - Target->DependentSections.push_back(RelocSec); - return RelocSec; + target->dependentSections.push_back(relocSec); + return relocSec; } - if (Target->FirstRelocation) + if (target->firstRelocation) fatal(toString(this) + ": multiple relocation sections to one section are not supported"); @@ -665,33 +924,33 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { // because applying relocations at end of linking changes section // contents. So, we simply handle such sections as non-mergeable ones. // Degrading like this is acceptable because section merging is optional. - if (auto *MS = dyn_cast<MergeInputSection>(Target)) { - Target = toRegularSection(MS); - this->Sections[Sec.sh_info] = Target; + if (auto *ms = dyn_cast<MergeInputSection>(target)) { + target = toRegularSection(ms); + this->sections[sec.sh_info] = target; } - if (Sec.sh_type == SHT_RELA) { - ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this); - Target->FirstRelocation = Rels.begin(); - Target->NumRelocations = Rels.size(); - Target->AreRelocsRela = true; + if (sec.sh_type == SHT_RELA) { + ArrayRef<Elf_Rela> rels = CHECK(getObj().relas(&sec), this); + target->firstRelocation = rels.begin(); + target->numRelocations = rels.size(); + target->areRelocsRela = true; } else { - ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this); - Target->FirstRelocation = Rels.begin(); - Target->NumRelocations = Rels.size(); - Target->AreRelocsRela = false; + ArrayRef<Elf_Rel> rels = CHECK(getObj().rels(&sec), this); + target->firstRelocation = rels.begin(); + target->numRelocations = rels.size(); + target->areRelocsRela = false; } - assert(isUInt<31>(Target->NumRelocations)); + assert(isUInt<31>(target->numRelocations)); // Relocation sections processed by the linker are usually removed // from the output, so returning `nullptr` for the normal case. // However, if -emit-relocs is given, we need to leave them in the output. // (Some post link analysis tools need this information.) - if (Config->EmitRelocs) { - InputSection *RelocSec = make<InputSection>(*this, Sec, Name); + if (config->emitRelocs) { + InputSection *relocSec = make<InputSection>(*this, sec, name); // We will not emit relocation section if target was discarded. - Target->DependentSections.push_back(RelocSec); - return RelocSec; + target->dependentSections.push_back(relocSec); + return relocSec; } return nullptr; } @@ -710,28 +969,42 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { // explicitly told to do otherwise (by -z execstack). Because the stack // executable-ness is controlled solely by command line options, // .note.GNU-stack sections are simply ignored. - if (Name == ".note.GNU-stack") - return &InputSection::Discarded; + if (name == ".note.GNU-stack") + return &InputSection::discarded; + + // Object files that use processor features such as Intel Control-Flow + // Enforcement (CET) or AArch64 Branch Target Identification BTI, use a + // .note.gnu.property section containing a bitfield of feature bits like the + // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag. + // + // Since we merge bitmaps from multiple object files to create a new + // .note.gnu.property containing a single AND'ed bitmap, we discard an input + // file's .note.gnu.property section. + if (name == ".note.gnu.property") { + ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(&sec)); + this->andFeatures = readAndFeatures(this, contents); + return &InputSection::discarded; + } // Split stacks is a feature to support a discontiguous stack, // commonly used in the programming language Go. For the details, // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled // for split stack will include a .note.GNU-split-stack section. - if (Name == ".note.GNU-split-stack") { - if (Config->Relocatable) { + if (name == ".note.GNU-split-stack") { + if (config->relocatable) { error("cannot mix split-stack and non-split-stack in a relocatable link"); - return &InputSection::Discarded; + return &InputSection::discarded; } - this->SplitStack = true; - return &InputSection::Discarded; + this->splitStack = true; + return &InputSection::discarded; } // An object file cmpiled for split stack, but where some of the // functions were compiled with the no_split_stack_attribute will // include a .note.GNU-no-split-stack section. - if (Name == ".note.GNU-no-split-stack") { - this->SomeNoSplitStack = true; - return &InputSection::Discarded; + if (name == ".note.GNU-no-split-stack") { + this->someNoSplitStack = true; + return &InputSection::discarded; } // The linkonce feature is a sort of proto-comdat. Some glibc i386 object @@ -739,245 +1012,205 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { // sections. Drop those sections to avoid duplicate symbol errors. // FIXME: This is glibc PR20543, we should remove this hack once that has been // fixed for a while. - if (Name.startswith(".gnu.linkonce.")) - return &InputSection::Discarded; + if (name == ".gnu.linkonce.t.__x86.get_pc_thunk.bx" || + name == ".gnu.linkonce.t.__i686.get_pc_thunk.bx") + return &InputSection::discarded; // If we are creating a new .build-id section, strip existing .build-id // sections so that the output won't have more than one .build-id. // This is not usually a problem because input object files normally don't // have .build-id sections, but you can create such files by // "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it. - if (Name == ".note.gnu.build-id" && Config->BuildId != BuildIdKind::None) - return &InputSection::Discarded; + if (name == ".note.gnu.build-id" && config->buildId != BuildIdKind::None) + return &InputSection::discarded; // The linker merges EH (exception handling) frames and creates a // .eh_frame_hdr section for runtime. So we handle them with a special // class. For relocatable outputs, they are just passed through. - if (Name == ".eh_frame" && !Config->Relocatable) - return make<EhInputSection>(*this, Sec, Name); + if (name == ".eh_frame" && !config->relocatable) + return make<EhInputSection>(*this, sec, name); - if (shouldMerge(Sec)) - return make<MergeInputSection>(*this, Sec, Name); - return make<InputSection>(*this, Sec, Name); + if (shouldMerge(sec)) + return make<MergeInputSection>(*this, sec, name); + return make<InputSection>(*this, sec, name); } template <class ELFT> -StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &Sec) { - return CHECK(this->getObj().getSectionName(&Sec, SectionStringTable), this); +StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &sec) { + return CHECK(getObj().getSectionName(&sec, sectionStringTable), this); } +// Initialize this->Symbols. this->Symbols is a parallel array as +// its corresponding ELF symbol table. template <class ELFT> void ObjFile<ELFT>::initializeSymbols() { - this->Symbols.reserve(this->ELFSyms.size()); - for (const Elf_Sym &Sym : this->ELFSyms) - this->Symbols.push_back(createSymbol(&Sym)); -} - -template <class ELFT> Symbol *ObjFile<ELFT>::createSymbol(const Elf_Sym *Sym) { - int Binding = Sym->getBinding(); - - uint32_t SecIdx = this->getSectionIndex(*Sym); - if (SecIdx >= this->Sections.size()) - fatal(toString(this) + ": invalid section index: " + Twine(SecIdx)); - - InputSectionBase *Sec = this->Sections[SecIdx]; - uint8_t StOther = Sym->st_other; - uint8_t Type = Sym->getType(); - uint64_t Value = Sym->st_value; - uint64_t Size = Sym->st_size; - - if (Binding == STB_LOCAL) { - if (Sym->getType() == STT_FILE) - SourceFile = CHECK(Sym->getName(this->StringTable), this); + ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>(); + this->symbols.resize(eSyms.size()); + + // Our symbol table may have already been partially initialized + // because of LazyObjFile. + for (size_t i = 0, end = eSyms.size(); i != end; ++i) + if (!this->symbols[i] && eSyms[i].getBinding() != STB_LOCAL) + this->symbols[i] = + symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this)); + + // Fill this->Symbols. A symbol is either local or global. + for (size_t i = 0, end = eSyms.size(); i != end; ++i) { + const Elf_Sym &eSym = eSyms[i]; + + // Read symbol attributes. + uint32_t secIdx = getSectionIndex(eSym); + if (secIdx >= this->sections.size()) + fatal(toString(this) + ": invalid section index: " + Twine(secIdx)); + + InputSectionBase *sec = this->sections[secIdx]; + uint8_t binding = eSym.getBinding(); + uint8_t stOther = eSym.st_other; + uint8_t type = eSym.getType(); + uint64_t value = eSym.st_value; + uint64_t size = eSym.st_size; + StringRefZ name = this->stringTable.data() + eSym.st_name; + + // Handle local symbols. Local symbols are not added to the symbol + // table because they are not visible from other object files. We + // allocate symbol instances and add their pointers to Symbols. + if (binding == STB_LOCAL) { + if (eSym.getType() == STT_FILE) + sourceFile = CHECK(eSym.getName(this->stringTable), this); + + if (this->stringTable.size() <= eSym.st_name) + fatal(toString(this) + ": invalid symbol name offset"); + + if (eSym.st_shndx == SHN_UNDEF) + this->symbols[i] = make<Undefined>(this, name, binding, stOther, type); + else if (sec == &InputSection::discarded) + this->symbols[i] = make<Undefined>(this, name, binding, stOther, type, + /*DiscardedSecIdx=*/secIdx); + else + this->symbols[i] = + make<Defined>(this, name, binding, stOther, type, value, size, sec); + continue; + } - if (this->StringTable.size() <= Sym->st_name) - fatal(toString(this) + ": invalid symbol name offset"); + // Handle global undefined symbols. + if (eSym.st_shndx == SHN_UNDEF) { + this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type}); + continue; + } - StringRefZ Name = this->StringTable.data() + Sym->st_name; - if (Sym->st_shndx == SHN_UNDEF) - return make<Undefined>(this, Name, Binding, StOther, Type); + // Handle global common symbols. + if (eSym.st_shndx == SHN_COMMON) { + if (value == 0 || value >= UINT32_MAX) + fatal(toString(this) + ": common symbol '" + StringRef(name.data) + + "' has invalid alignment: " + Twine(value)); + this->symbols[i]->resolve( + CommonSymbol{this, name, binding, stOther, type, value, size}); + continue; + } - return make<Defined>(this, Name, Binding, StOther, Type, Value, Size, Sec); - } + // If a defined symbol is in a discarded section, handle it as if it + // were an undefined symbol. Such symbol doesn't comply with the + // standard, but in practice, a .eh_frame often directly refer + // COMDAT member sections, and if a comdat group is discarded, some + // defined symbol in a .eh_frame becomes dangling symbols. + if (sec == &InputSection::discarded) { + this->symbols[i]->resolve( + Undefined{this, name, binding, stOther, type, secIdx}); + continue; + } - StringRef Name = CHECK(Sym->getName(this->StringTable), this); - - switch (Sym->st_shndx) { - case SHN_UNDEF: - return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type, - /*CanOmitFromDynSym=*/false, this); - case SHN_COMMON: - if (Value == 0 || Value >= UINT32_MAX) - fatal(toString(this) + ": common symbol '" + Name + - "' has invalid alignment: " + Twine(Value)); - return Symtab->addCommon(Name, Size, Value, Binding, StOther, Type, *this); - } + // Handle global defined symbols. + if (binding == STB_GLOBAL || binding == STB_WEAK || + binding == STB_GNU_UNIQUE) { + this->symbols[i]->resolve( + Defined{this, name, binding, stOther, type, value, size, sec}); + continue; + } - switch (Binding) { - default: - fatal(toString(this) + ": unexpected binding: " + Twine(Binding)); - case STB_GLOBAL: - case STB_WEAK: - case STB_GNU_UNIQUE: - if (Sec == &InputSection::Discarded) - return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type, - /*CanOmitFromDynSym=*/false, this); - return Symtab->addDefined(Name, StOther, Type, Value, Size, Binding, Sec, - this); + fatal(toString(this) + ": unexpected binding: " + Twine((int)binding)); } } -ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File) - : InputFile(ArchiveKind, File->getMemoryBufferRef()), - File(std::move(File)) {} +ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&file) + : InputFile(ArchiveKind, file->getMemoryBufferRef()), + file(std::move(file)) {} -template <class ELFT> void ArchiveFile::parse() { - for (const Archive::Symbol &Sym : File->symbols()) - Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym); +void ArchiveFile::parse() { + for (const Archive::Symbol &sym : file->symbols()) + symtab->addSymbol(LazyArchive{*this, sym}); } // Returns a buffer pointing to a member file containing a given symbol. -InputFile *ArchiveFile::fetch(const Archive::Symbol &Sym) { - Archive::Child C = - CHECK(Sym.getMember(), toString(this) + +void ArchiveFile::fetch(const Archive::Symbol &sym) { + Archive::Child c = + CHECK(sym.getMember(), toString(this) + ": could not get the member for symbol " + - Sym.getName()); + sym.getName()); - if (!Seen.insert(C.getChildOffset()).second) - return nullptr; + if (!seen.insert(c.getChildOffset()).second) + return; - MemoryBufferRef MB = - CHECK(C.getMemoryBufferRef(), + MemoryBufferRef mb = + CHECK(c.getMemoryBufferRef(), toString(this) + ": could not get the buffer for the member defining symbol " + - Sym.getName()); + sym.getName()); - if (Tar && C.getParent()->isThin()) - Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), MB.getBuffer()); + if (tar && c.getParent()->isThin()) + tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer()); - InputFile *File = createObjectFile( - MB, getName(), C.getParent()->isThin() ? 0 : C.getChildOffset()); - File->GroupId = GroupId; - return File; + InputFile *file = createObjectFile( + mb, getName(), c.getParent()->isThin() ? 0 : c.getChildOffset()); + file->groupId = groupId; + parseFile(file); } -template <class ELFT> -SharedFile<ELFT>::SharedFile(MemoryBufferRef M, StringRef DefaultSoName) - : ELFFileBase<ELFT>(Base::SharedKind, M), SoName(DefaultSoName), - IsNeeded(!Config->AsNeeded) {} - -// Partially parse the shared object file so that we can call -// getSoName on this object. -template <class ELFT> void SharedFile<ELFT>::parseSoName() { - const Elf_Shdr *DynamicSec = nullptr; - const ELFFile<ELFT> Obj = this->getObj(); - ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this); - - // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d. - for (const Elf_Shdr &Sec : Sections) { - switch (Sec.sh_type) { - default: - continue; - case SHT_DYNSYM: - this->initSymtab(Sections, &Sec); - break; - case SHT_DYNAMIC: - DynamicSec = &Sec; - break; - case SHT_SYMTAB_SHNDX: - this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, Sections), this); - break; - case SHT_GNU_versym: - this->VersymSec = &Sec; - break; - case SHT_GNU_verdef: - this->VerdefSec = &Sec; - break; - } - } - - if (this->VersymSec && this->ELFSyms.empty()) - error("SHT_GNU_versym should be associated with symbol table"); - - // Search for a DT_SONAME tag to initialize this->SoName. - if (!DynamicSec) - return; - ArrayRef<Elf_Dyn> Arr = - CHECK(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec), this); - for (const Elf_Dyn &Dyn : Arr) { - if (Dyn.d_tag == DT_SONAME) { - uint64_t Val = Dyn.getVal(); - if (Val >= this->StringTable.size()) - fatal(toString(this) + ": invalid DT_SONAME entry"); - SoName = this->StringTable.data() + Val; - return; - } - } -} - -// Parses ".gnu.version" section which is a parallel array for the symbol table. -// If a given file doesn't have ".gnu.version" section, returns VER_NDX_GLOBAL. -template <class ELFT> std::vector<uint32_t> SharedFile<ELFT>::parseVersyms() { - size_t Size = this->ELFSyms.size() - this->FirstGlobal; - if (!VersymSec) - return std::vector<uint32_t>(Size, VER_NDX_GLOBAL); - - const char *Base = this->MB.getBuffer().data(); - const Elf_Versym *Versym = - reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) + - this->FirstGlobal; - - std::vector<uint32_t> Ret(Size); - for (size_t I = 0; I < Size; ++I) - Ret[I] = Versym[I].vs_index; - return Ret; -} +unsigned SharedFile::vernauxNum; -// Parse the version definitions in the object file if present. Returns a vector -// whose nth element contains a pointer to the Elf_Verdef for version identifier -// n. Version identifiers that are not definitions map to nullptr. -template <class ELFT> -std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() { - if (!VerdefSec) +// Parse the version definitions in the object file if present, and return a +// vector whose nth element contains a pointer to the Elf_Verdef for version +// identifier n. Version identifiers that are not definitions map to nullptr. +template <typename ELFT> +static std::vector<const void *> parseVerdefs(const uint8_t *base, + const typename ELFT::Shdr *sec) { + if (!sec) return {}; // We cannot determine the largest verdef identifier without inspecting // every Elf_Verdef, but both bfd and gold assign verdef identifiers // sequentially starting from 1, so we predict that the largest identifier - // will be VerdefCount. - unsigned VerdefCount = VerdefSec->sh_info; - std::vector<const Elf_Verdef *> Verdefs(VerdefCount + 1); + // will be verdefCount. + unsigned verdefCount = sec->sh_info; + std::vector<const void *> verdefs(verdefCount + 1); // Build the Verdefs array by following the chain of Elf_Verdef objects // from the start of the .gnu.version_d section. - const char *Base = this->MB.getBuffer().data(); - const char *Verdef = Base + VerdefSec->sh_offset; - for (unsigned I = 0; I != VerdefCount; ++I) { - auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef); - Verdef += CurVerdef->vd_next; - unsigned VerdefIndex = CurVerdef->vd_ndx; - Verdefs.resize(VerdefIndex + 1); - Verdefs[VerdefIndex] = CurVerdef; + const uint8_t *verdef = base + sec->sh_offset; + for (unsigned i = 0; i != verdefCount; ++i) { + auto *curVerdef = reinterpret_cast<const typename ELFT::Verdef *>(verdef); + verdef += curVerdef->vd_next; + unsigned verdefIndex = curVerdef->vd_ndx; + verdefs.resize(verdefIndex + 1); + verdefs[verdefIndex] = curVerdef; } - - return Verdefs; + return verdefs; } // We do not usually care about alignments of data in shared object // files because the loader takes care of it. However, if we promote a // DSO symbol to point to .bss due to copy relocation, we need to keep // the original alignment requirements. We infer it in this function. -template <class ELFT> -uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections, - const Elf_Sym &Sym) { - uint64_t Ret = UINT64_MAX; - if (Sym.st_value) - Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value); - if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) - Ret = std::min<uint64_t>(Ret, Sections[Sym.st_shndx].sh_addralign); - return (Ret > UINT32_MAX) ? 0 : Ret; +template <typename ELFT> +static uint64_t getAlignment(ArrayRef<typename ELFT::Shdr> sections, + const typename ELFT::Sym &sym) { + uint64_t ret = UINT64_MAX; + if (sym.st_value) + ret = 1ULL << countTrailingZeros((uint64_t)sym.st_value); + if (0 < sym.st_shndx && sym.st_shndx < sections.size()) + ret = std::min<uint64_t>(ret, sections[sym.st_shndx].sh_addralign); + return (ret > UINT32_MAX) ? 0 : ret; } -// Fully parse the shared object file. This must be called after parseSoName(). +// Fully parse the shared object file. // // This function parses symbol versions. If a DSO has version information, // the file has a ".gnu.version_d" section which contains symbol version @@ -992,80 +1225,163 @@ uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections, // The file format for symbol versioning is perhaps a bit more complicated // than necessary, but you can easily understand the code if you wrap your // head around the data structure described above. -template <class ELFT> void SharedFile<ELFT>::parseRest() { - Verdefs = parseVerdefs(); // parse .gnu.version_d - std::vector<uint32_t> Versyms = parseVersyms(); // parse .gnu.version - ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this); +template <class ELFT> void SharedFile::parse() { + using Elf_Dyn = typename ELFT::Dyn; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; + using Elf_Verdef = typename ELFT::Verdef; + using Elf_Versym = typename ELFT::Versym; + + ArrayRef<Elf_Dyn> dynamicTags; + const ELFFile<ELFT> obj = this->getObj<ELFT>(); + ArrayRef<Elf_Shdr> sections = CHECK(obj.sections(), this); + + const Elf_Shdr *versymSec = nullptr; + const Elf_Shdr *verdefSec = nullptr; + + // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d. + for (const Elf_Shdr &sec : sections) { + switch (sec.sh_type) { + default: + continue; + case SHT_DYNAMIC: + dynamicTags = + CHECK(obj.template getSectionContentsAsArray<Elf_Dyn>(&sec), this); + break; + case SHT_GNU_versym: + versymSec = &sec; + break; + case SHT_GNU_verdef: + verdefSec = &sec; + break; + } + } + + if (versymSec && numELFSyms == 0) { + error("SHT_GNU_versym should be associated with symbol table"); + return; + } + + // Search for a DT_SONAME tag to initialize this->soName. + for (const Elf_Dyn &dyn : dynamicTags) { + if (dyn.d_tag == DT_NEEDED) { + uint64_t val = dyn.getVal(); + if (val >= this->stringTable.size()) + fatal(toString(this) + ": invalid DT_NEEDED entry"); + dtNeeded.push_back(this->stringTable.data() + val); + } else if (dyn.d_tag == DT_SONAME) { + uint64_t val = dyn.getVal(); + if (val >= this->stringTable.size()) + fatal(toString(this) + ": invalid DT_SONAME entry"); + soName = this->stringTable.data() + val; + } + } + + // DSOs are uniquified not by filename but by soname. + DenseMap<StringRef, SharedFile *>::iterator it; + bool wasInserted; + std::tie(it, wasInserted) = symtab->soNames.try_emplace(soName, this); + + // If a DSO appears more than once on the command line with and without + // --as-needed, --no-as-needed takes precedence over --as-needed because a + // user can add an extra DSO with --no-as-needed to force it to be added to + // the dependency list. + it->second->isNeeded |= isNeeded; + if (!wasInserted) + return; + + sharedFiles.push_back(this); + + verdefs = parseVerdefs<ELFT>(obj.base(), verdefSec); + + // Parse ".gnu.version" section which is a parallel array for the symbol + // table. If a given file doesn't have a ".gnu.version" section, we use + // VER_NDX_GLOBAL. + size_t size = numELFSyms - firstGlobal; + std::vector<uint32_t> versyms(size, VER_NDX_GLOBAL); + if (versymSec) { + ArrayRef<Elf_Versym> versym = + CHECK(obj.template getSectionContentsAsArray<Elf_Versym>(versymSec), + this) + .slice(firstGlobal); + for (size_t i = 0; i < size; ++i) + versyms[i] = versym[i].vs_index; + } // System libraries can have a lot of symbols with versions. Using a // fixed buffer for computing the versions name (foo@ver) can save a // lot of allocations. - SmallString<0> VersionedNameBuffer; + SmallString<0> versionedNameBuffer; // Add symbols to the symbol table. - ArrayRef<Elf_Sym> Syms = this->getGlobalELFSyms(); - for (size_t I = 0; I < Syms.size(); ++I) { - const Elf_Sym &Sym = Syms[I]; + ArrayRef<Elf_Sym> syms = this->getGlobalELFSyms<ELFT>(); + for (size_t i = 0; i < syms.size(); ++i) { + const Elf_Sym &sym = syms[i]; // ELF spec requires that all local symbols precede weak or global // symbols in each symbol table, and the index of first non-local symbol // is stored to sh_info. If a local symbol appears after some non-local // symbol, that's a violation of the spec. - StringRef Name = CHECK(Sym.getName(this->StringTable), this); - if (Sym.getBinding() == STB_LOCAL) { - warn("found local symbol '" + Name + + StringRef name = CHECK(sym.getName(this->stringTable), this); + if (sym.getBinding() == STB_LOCAL) { + warn("found local symbol '" + name + "' in global part of symbol table in file " + toString(this)); continue; } - if (Sym.isUndefined()) { - Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(), - Sym.st_other, Sym.getType(), - /*CanOmitFromDynSym=*/false, this); - S->ExportDynamic = true; + if (sym.isUndefined()) { + Symbol *s = symtab->addSymbol( + Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); + s->exportDynamic = true; continue; } // MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly // assigns VER_NDX_LOCAL to this section global symbol. Here is a // workaround for this bug. - uint32_t Idx = Versyms[I] & ~VERSYM_HIDDEN; - if (Config->EMachine == EM_MIPS && Idx == VER_NDX_LOCAL && - Name == "_gp_disp") + uint32_t idx = versyms[i] & ~VERSYM_HIDDEN; + if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL && + name == "_gp_disp") continue; - uint64_t Alignment = getAlignment(Sections, Sym); - if (!(Versyms[I] & VERSYM_HIDDEN)) - Symtab->addShared(Name, *this, Sym, Alignment, Idx); + uint32_t alignment = getAlignment<ELFT>(sections, sym); + if (!(versyms[i] & VERSYM_HIDDEN)) { + symtab->addSymbol(SharedSymbol{*this, name, sym.getBinding(), + sym.st_other, sym.getType(), sym.st_value, + sym.st_size, alignment, idx}); + } // Also add the symbol with the versioned name to handle undefined symbols // with explicit versions. - if (Idx == VER_NDX_GLOBAL) + if (idx == VER_NDX_GLOBAL) continue; - if (Idx >= Verdefs.size() || Idx == VER_NDX_LOCAL) { - error("corrupt input file: version definition index " + Twine(Idx) + - " for symbol " + Name + " is out of bounds\n>>> defined in " + + if (idx >= verdefs.size() || idx == VER_NDX_LOCAL) { + error("corrupt input file: version definition index " + Twine(idx) + + " for symbol " + name + " is out of bounds\n>>> defined in " + toString(this)); continue; } - StringRef VerName = - this->StringTable.data() + Verdefs[Idx]->getAux()->vda_name; - VersionedNameBuffer.clear(); - Name = (Name + "@" + VerName).toStringRef(VersionedNameBuffer); - Symtab->addShared(Saver.save(Name), *this, Sym, Alignment, Idx); + StringRef verName = + this->stringTable.data() + + reinterpret_cast<const Elf_Verdef *>(verdefs[idx])->getAux()->vda_name; + versionedNameBuffer.clear(); + name = (name + "@" + verName).toStringRef(versionedNameBuffer); + symtab->addSymbol(SharedSymbol{*this, saver.save(name), sym.getBinding(), + sym.st_other, sym.getType(), sym.st_value, + sym.st_size, alignment, idx}); } } -static ELFKind getBitcodeELFKind(const Triple &T) { - if (T.isLittleEndian()) - return T.isArch64Bit() ? ELF64LEKind : ELF32LEKind; - return T.isArch64Bit() ? ELF64BEKind : ELF32BEKind; +static ELFKind getBitcodeELFKind(const Triple &t) { + if (t.isLittleEndian()) + return t.isArch64Bit() ? ELF64LEKind : ELF32LEKind; + return t.isArch64Bit() ? ELF64BEKind : ELF32BEKind; } -static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) { - switch (T.getArch()) { +static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) { + switch (t.getArch()) { case Triple::aarch64: return EM_AARCH64; case Triple::amdgcn: @@ -1088,25 +1404,28 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) { case Triple::ppc64: case Triple::ppc64le: return EM_PPC64; + case Triple::riscv32: + case Triple::riscv64: + return EM_RISCV; case Triple::x86: - return T.isOSIAMCU() ? EM_IAMCU : EM_386; + return t.isOSIAMCU() ? EM_IAMCU : EM_386; case Triple::x86_64: return EM_X86_64; default: - error(Path + ": could not infer e_machine from bitcode target triple " + - T.str()); + error(path + ": could not infer e_machine from bitcode target triple " + + t.str()); return EM_NONE; } } -BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName, - uint64_t OffsetInArchive) - : InputFile(BitcodeKind, MB) { - this->ArchiveName = ArchiveName; +BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(BitcodeKind, mb) { + this->archiveName = archiveName; - std::string Path = MB.getBufferIdentifier().str(); - if (Config->ThinLTOIndexOnly) - Path = replaceThinLTOSuffix(MB.getBufferIdentifier()); + std::string path = mb.getBufferIdentifier().str(); + if (config->thinLTOIndexOnly) + path = replaceThinLTOSuffix(mb.getBufferIdentifier()); // ThinLTO assumes that all MemoryBufferRefs given to it have a unique // name. If two archives define two members with the same name, this @@ -1114,20 +1433,21 @@ BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName, // into consideration at LTO time (which very likely causes undefined // symbols later in the link stage). So we append file offset to make // filename unique. - MemoryBufferRef MBRef( - MB.getBuffer(), - Saver.save(ArchiveName + Path + - (ArchiveName.empty() ? "" : utostr(OffsetInArchive)))); + StringRef name = archiveName.empty() + ? saver.save(path) + : saver.save(archiveName + "(" + path + " at " + + utostr(offsetInArchive) + ")"); + MemoryBufferRef mbref(mb.getBuffer(), name); - Obj = CHECK(lto::InputFile::create(MBRef), this); + obj = CHECK(lto::InputFile::create(mbref), this); - Triple T(Obj->getTargetTriple()); - EKind = getBitcodeELFKind(T); - EMachine = getBitcodeMachineKind(MB.getBufferIdentifier(), T); + Triple t(obj->getTargetTriple()); + ekind = getBitcodeELFKind(t); + emachine = getBitcodeMachineKind(mb.getBufferIdentifier(), t); } -static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) { - switch (GvVisibility) { +static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) { + switch (gvVisibility) { case GlobalValue::DefaultVisibility: return STV_DEFAULT; case GlobalValue::HiddenVisibility: @@ -1139,209 +1459,187 @@ static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) { } template <class ELFT> -static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats, - const lto::InputFile::Symbol &ObjSym, - BitcodeFile &F) { - StringRef Name = Saver.save(ObjSym.getName()); - uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL; - - uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE; - uint8_t Visibility = mapVisibility(ObjSym.getVisibility()); - bool CanOmitFromDynSym = ObjSym.canBeOmittedFromSymbolTable(); - - int C = ObjSym.getComdatIndex(); - if (C != -1 && !KeptComdats[C]) - return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type, - CanOmitFromDynSym, &F); - - if (ObjSym.isUndefined()) - return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type, - CanOmitFromDynSym, &F); - - if (ObjSym.isCommon()) - return Symtab->addCommon(Name, ObjSym.getCommonSize(), - ObjSym.getCommonAlignment(), Binding, Visibility, - STT_OBJECT, F); - - return Symtab->addBitcode(Name, Binding, Visibility, Type, CanOmitFromDynSym, - F); -} +static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats, + const lto::InputFile::Symbol &objSym, + BitcodeFile &f) { + StringRef name = saver.save(objSym.getName()); + uint8_t binding = objSym.isWeak() ? STB_WEAK : STB_GLOBAL; + uint8_t type = objSym.isTLS() ? STT_TLS : STT_NOTYPE; + uint8_t visibility = mapVisibility(objSym.getVisibility()); + bool canOmitFromDynSym = objSym.canBeOmittedFromSymbolTable(); + + int c = objSym.getComdatIndex(); + if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) { + Undefined New(&f, name, binding, visibility, type); + if (canOmitFromDynSym) + New.exportDynamic = false; + return symtab->addSymbol(New); + } -template <class ELFT> -void BitcodeFile::parse(DenseSet<CachedHashStringRef> &ComdatGroups) { - std::vector<bool> KeptComdats; - for (StringRef S : Obj->getComdatTable()) - KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(S)).second); + if (objSym.isCommon()) + return symtab->addSymbol( + CommonSymbol{&f, name, binding, visibility, STT_OBJECT, + objSym.getCommonAlignment(), objSym.getCommonSize()}); - for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) - Symbols.push_back(createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, *this)); + Defined New(&f, name, binding, visibility, type, 0, 0, nullptr); + if (canOmitFromDynSym) + New.exportDynamic = false; + return symtab->addSymbol(New); } -static ELFKind getELFKind(MemoryBufferRef MB) { - unsigned char Size; - unsigned char Endian; - std::tie(Size, Endian) = getElfArchType(MB.getBuffer()); - - if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB) - fatal(MB.getBufferIdentifier() + ": invalid data encoding"); - if (Size != ELFCLASS32 && Size != ELFCLASS64) - fatal(MB.getBufferIdentifier() + ": invalid file class"); +template <class ELFT> void BitcodeFile::parse() { + std::vector<bool> keptComdats; + for (StringRef s : obj->getComdatTable()) + keptComdats.push_back( + symtab->comdatGroups.try_emplace(CachedHashStringRef(s), this).second); - size_t BufSize = MB.getBuffer().size(); - if ((Size == ELFCLASS32 && BufSize < sizeof(Elf32_Ehdr)) || - (Size == ELFCLASS64 && BufSize < sizeof(Elf64_Ehdr))) - fatal(MB.getBufferIdentifier() + ": file is too short"); + for (const lto::InputFile::Symbol &objSym : obj->symbols()) + symbols.push_back(createBitcodeSymbol<ELFT>(keptComdats, objSym, *this)); - if (Size == ELFCLASS32) - return (Endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind; - return (Endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind; + for (auto l : obj->getDependentLibraries()) + addDependentLibrary(l, this); } void BinaryFile::parse() { - ArrayRef<uint8_t> Data = arrayRefFromStringRef(MB.getBuffer()); - auto *Section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, - 8, Data, ".data"); - Sections.push_back(Section); + ArrayRef<uint8_t> data = arrayRefFromStringRef(mb.getBuffer()); + auto *section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, + 8, data, ".data"); + sections.push_back(section); // For each input file foo that is embedded to a result as a binary // blob, we define _binary_foo_{start,end,size} symbols, so that // user programs can access blobs by name. Non-alphanumeric // characters in a filename are replaced with underscore. - std::string S = "_binary_" + MB.getBufferIdentifier().str(); - for (size_t I = 0; I < S.size(); ++I) - if (!isAlnum(S[I])) - S[I] = '_'; - - Symtab->addDefined(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0, - STB_GLOBAL, Section, nullptr); - Symtab->addDefined(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT, - Data.size(), 0, STB_GLOBAL, Section, nullptr); - Symtab->addDefined(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT, - Data.size(), 0, STB_GLOBAL, nullptr, nullptr); + std::string s = "_binary_" + mb.getBufferIdentifier().str(); + for (size_t i = 0; i < s.size(); ++i) + if (!isAlnum(s[i])) + s[i] = '_'; + + symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, 0, 0, section}); + symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, data.size(), 0, section}); + symtab->addSymbol(Defined{nullptr, saver.save(s + "_size"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr}); } -InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName, - uint64_t OffsetInArchive) { - if (isBitcode(MB)) - return make<BitcodeFile>(MB, ArchiveName, OffsetInArchive); +InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) { + if (isBitcode(mb)) + return make<BitcodeFile>(mb, archiveName, offsetInArchive); - switch (getELFKind(MB)) { + switch (getELFKind(mb, archiveName)) { case ELF32LEKind: - return make<ObjFile<ELF32LE>>(MB, ArchiveName); + return make<ObjFile<ELF32LE>>(mb, archiveName); case ELF32BEKind: - return make<ObjFile<ELF32BE>>(MB, ArchiveName); + return make<ObjFile<ELF32BE>>(mb, archiveName); case ELF64LEKind: - return make<ObjFile<ELF64LE>>(MB, ArchiveName); + return make<ObjFile<ELF64LE>>(mb, archiveName); case ELF64BEKind: - return make<ObjFile<ELF64BE>>(MB, ArchiveName); + return make<ObjFile<ELF64BE>>(mb, archiveName); default: llvm_unreachable("getELFKind"); } } -InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) { - switch (getELFKind(MB)) { - case ELF32LEKind: - return make<SharedFile<ELF32LE>>(MB, DefaultSoName); - case ELF32BEKind: - return make<SharedFile<ELF32BE>>(MB, DefaultSoName); - case ELF64LEKind: - return make<SharedFile<ELF64LE>>(MB, DefaultSoName); - case ELF64BEKind: - return make<SharedFile<ELF64BE>>(MB, DefaultSoName); - default: - llvm_unreachable("getELFKind"); - } -} +void LazyObjFile::fetch() { + if (mb.getBuffer().empty()) + return; -MemoryBufferRef LazyObjFile::getBuffer() { - if (AddedToLink) - return MemoryBufferRef(); - AddedToLink = true; - return MB; -} + InputFile *file = createObjectFile(mb, archiveName, offsetInArchive); + file->groupId = groupId; -InputFile *LazyObjFile::fetch() { - MemoryBufferRef MBRef = getBuffer(); - if (MBRef.getBuffer().empty()) - return nullptr; + mb = {}; + + // Copy symbol vector so that the new InputFile doesn't have to + // insert the same defined symbols to the symbol table again. + file->symbols = std::move(symbols); - InputFile *File = createObjectFile(MBRef, ArchiveName, OffsetInArchive); - File->GroupId = GroupId; - return File; + parseFile(file); } template <class ELFT> void LazyObjFile::parse() { + using Elf_Sym = typename ELFT::Sym; + // A lazy object file wraps either a bitcode file or an ELF file. - if (isBitcode(this->MB)) { - std::unique_ptr<lto::InputFile> Obj = - CHECK(lto::InputFile::create(this->MB), this); - for (const lto::InputFile::Symbol &Sym : Obj->symbols()) - if (!Sym.isUndefined()) - Symtab->addLazyObject<ELFT>(Saver.save(Sym.getName()), *this); + if (isBitcode(this->mb)) { + std::unique_ptr<lto::InputFile> obj = + CHECK(lto::InputFile::create(this->mb), this); + for (const lto::InputFile::Symbol &sym : obj->symbols()) { + if (sym.isUndefined()) + continue; + symtab->addSymbol(LazyObject{*this, saver.save(sym.getName())}); + } return; } - if (getELFKind(this->MB) != Config->EKind) { - error("incompatible file: " + this->MB.getBufferIdentifier()); + if (getELFKind(this->mb, archiveName) != config->ekind) { + error("incompatible file: " + this->mb.getBufferIdentifier()); return; } - ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer())); - ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this); + // Find a symbol table. + ELFFile<ELFT> obj = check(ELFFile<ELFT>::create(mb.getBuffer())); + ArrayRef<typename ELFT::Shdr> sections = CHECK(obj.sections(), this); - for (const typename ELFT::Shdr &Sec : Sections) { - if (Sec.sh_type != SHT_SYMTAB) + for (const typename ELFT::Shdr &sec : sections) { + if (sec.sh_type != SHT_SYMTAB) continue; - typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this); - uint32_t FirstGlobal = Sec.sh_info; - StringRef StringTable = - CHECK(Obj.getStringTableForSymtab(Sec, Sections), this); + // A symbol table is found. + ArrayRef<Elf_Sym> eSyms = CHECK(obj.symbols(&sec), this); + uint32_t firstGlobal = sec.sh_info; + StringRef strtab = CHECK(obj.getStringTableForSymtab(sec, sections), this); + this->symbols.resize(eSyms.size()); + + // Get existing symbols or insert placeholder symbols. + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) + if (eSyms[i].st_shndx != SHN_UNDEF) + this->symbols[i] = symtab->insert(CHECK(eSyms[i].getName(strtab), this)); + + // Replace existing symbols with LazyObject symbols. + // + // resolve() may trigger this->fetch() if an existing symbol is an + // undefined symbol. If that happens, this LazyObjFile has served + // its purpose, and we can exit from the loop early. + for (Symbol *sym : this->symbols) { + if (!sym) + continue; + sym->resolve(LazyObject{*this, sym->getName()}); - for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal)) - if (Sym.st_shndx != SHN_UNDEF) - Symtab->addLazyObject<ELFT>(CHECK(Sym.getName(StringTable), this), - *this); + // MemoryBuffer is emptied if this file is instantiated as ObjFile. + if (mb.getBuffer().empty()) + return; + } return; } } -std::string elf::replaceThinLTOSuffix(StringRef Path) { - StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first; - StringRef Repl = Config->ThinLTOObjectSuffixReplace.second; +std::string elf::replaceThinLTOSuffix(StringRef path) { + StringRef suffix = config->thinLTOObjectSuffixReplace.first; + StringRef repl = config->thinLTOObjectSuffixReplace.second; - if (Path.consume_back(Suffix)) - return (Path + Repl).str(); - return Path; + if (path.consume_back(suffix)) + return (path + repl).str(); + return path; } -template void ArchiveFile::parse<ELF32LE>(); -template void ArchiveFile::parse<ELF32BE>(); -template void ArchiveFile::parse<ELF64LE>(); -template void ArchiveFile::parse<ELF64BE>(); - -template void BitcodeFile::parse<ELF32LE>(DenseSet<CachedHashStringRef> &); -template void BitcodeFile::parse<ELF32BE>(DenseSet<CachedHashStringRef> &); -template void BitcodeFile::parse<ELF64LE>(DenseSet<CachedHashStringRef> &); -template void BitcodeFile::parse<ELF64BE>(DenseSet<CachedHashStringRef> &); +template void BitcodeFile::parse<ELF32LE>(); +template void BitcodeFile::parse<ELF32BE>(); +template void BitcodeFile::parse<ELF64LE>(); +template void BitcodeFile::parse<ELF64BE>(); template void LazyObjFile::parse<ELF32LE>(); template void LazyObjFile::parse<ELF32BE>(); template void LazyObjFile::parse<ELF64LE>(); template void LazyObjFile::parse<ELF64BE>(); -template class elf::ELFFileBase<ELF32LE>; -template class elf::ELFFileBase<ELF32BE>; -template class elf::ELFFileBase<ELF64LE>; -template class elf::ELFFileBase<ELF64BE>; - template class elf::ObjFile<ELF32LE>; template class elf::ObjFile<ELF32BE>; template class elf::ObjFile<ELF64LE>; template class elf::ObjFile<ELF64BE>; -template class elf::SharedFile<ELF32LE>; -template class elf::SharedFile<ELF32BE>; -template class elf::SharedFile<ELF64LE>; -template class elf::SharedFile<ELF64BE>; +template void SharedFile::parse<ELF32LE>(); +template void SharedFile::parse<ELF32BE>(); +template void SharedFile::parse<ELF64LE>(); +template void SharedFile::parse<ELF64BE>(); diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h index 5094ddd804a5..5ccc3d402b37 100644 --- a/ELF/InputFiles.h +++ b/ELF/InputFiles.h @@ -1,9 +1,8 @@ //===- InputFiles.h ---------------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -40,7 +39,7 @@ class InputSectionBase; } // Returns "<internal>", "foo.a(bar.o)" or "baz.o". -std::string toString(const elf::InputFile *F); +std::string toString(const elf::InputFile *f); namespace elf { @@ -50,10 +49,13 @@ class Symbol; // If -reproduce option is given, all input files are written // to this tar archive. -extern std::unique_ptr<llvm::TarWriter> Tar; +extern std::unique_ptr<llvm::TarWriter> tar; // Opens a given file. -llvm::Optional<MemoryBufferRef> readFile(StringRef Path); +llvm::Optional<MemoryBufferRef> readFile(StringRef path); + +// Add symbols in File to the symbol table. +void parseFile(InputFile *file); // The root class of input files. class InputFile { @@ -67,192 +69,230 @@ public: BinaryKind, }; - Kind kind() const { return FileKind; } + Kind kind() const { return fileKind; } bool isElf() const { - Kind K = kind(); - return K == ObjKind || K == SharedKind; + Kind k = kind(); + return k == ObjKind || k == SharedKind; } - StringRef getName() const { return MB.getBufferIdentifier(); } - MemoryBufferRef MB; + StringRef getName() const { return mb.getBufferIdentifier(); } + MemoryBufferRef mb; // Returns sections. It is a runtime error to call this function // on files that don't have the notion of sections. ArrayRef<InputSectionBase *> getSections() const { - assert(FileKind == ObjKind || FileKind == BinaryKind); - return Sections; + assert(fileKind == ObjKind || fileKind == BinaryKind); + return sections; } // Returns object file symbols. It is a runtime error to call this // function on files of other types. ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); } - std::vector<Symbol *> &getMutableSymbols() { - assert(FileKind == BinaryKind || FileKind == ObjKind || - FileKind == BitcodeKind); - return Symbols; + MutableArrayRef<Symbol *> getMutableSymbols() { + assert(fileKind == BinaryKind || fileKind == ObjKind || + fileKind == BitcodeKind); + return symbols; } // Filename of .a which contained this file. If this file was // not in an archive file, it is the empty string. We use this // string for creating error messages. - std::string ArchiveName; + std::string archiveName; // If this is an architecture-specific file, the following members // have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type. - ELFKind EKind = ELFNoneKind; - uint16_t EMachine = llvm::ELF::EM_NONE; - uint8_t OSABI = 0; + ELFKind ekind = ELFNoneKind; + uint16_t emachine = llvm::ELF::EM_NONE; + uint8_t osabi = 0; + uint8_t abiVersion = 0; // Cache for toString(). Only toString() should use this member. - mutable std::string ToStringCache; + mutable std::string toStringCache; - std::string getSrcMsg(const Symbol &Sym, InputSectionBase &Sec, - uint64_t Offset); + std::string getSrcMsg(const Symbol &sym, InputSectionBase &sec, + uint64_t offset); // True if this is an argument for --just-symbols. Usually false. - bool JustSymbols = false; - - // GroupId is used for --warn-backrefs which is an optional error + bool justSymbols = false; + + // outSecOff of .got2 in the current file. This is used by PPC32 -fPIC/-fPIE + // to compute offsets in PLT call stubs. + uint32_t ppc32Got2OutSecOff = 0; + + // On PPC64 we need to keep track of which files contain small code model + // relocations that access the .toc section. To minimize the chance of a + // relocation overflow, files that do contain said relocations should have + // their .toc sections sorted closer to the .got section than files that do + // not contain any small code model relocations. Thats because the toc-pointer + // is defined to point at .got + 0x8000 and the instructions used with small + // code model relocations support immediates in the range [-0x8000, 0x7FFC], + // making the addressable range relative to the toc pointer + // [.got, .got + 0xFFFC]. + bool ppc64SmallCodeModelTocRelocs = false; + + // groupId is used for --warn-backrefs which is an optional error // checking feature. All files within the same --{start,end}-group or // --{start,end}-lib get the same group ID. Otherwise, each file gets a new // group ID. For more info, see checkDependency() in SymbolTable.cpp. - uint32_t GroupId; - static bool IsInGroup; - static uint32_t NextGroupId; + uint32_t groupId; + static bool isInGroup; + static uint32_t nextGroupId; // Index of MIPS GOT built for this file. - llvm::Optional<size_t> MipsGotIndex; + llvm::Optional<size_t> mipsGotIndex; + + std::vector<Symbol *> symbols; protected: - InputFile(Kind K, MemoryBufferRef M); - std::vector<InputSectionBase *> Sections; - std::vector<Symbol *> Symbols; + InputFile(Kind k, MemoryBufferRef m); + std::vector<InputSectionBase *> sections; private: - const Kind FileKind; + const Kind fileKind; }; -template <typename ELFT> class ELFFileBase : public InputFile { +class ELFFileBase : public InputFile { public: - typedef typename ELFT::Shdr Elf_Shdr; - typedef typename ELFT::Sym Elf_Sym; - typedef typename ELFT::Word Elf_Word; - typedef typename ELFT::SymRange Elf_Sym_Range; + ELFFileBase(Kind k, MemoryBufferRef m); + static bool classof(const InputFile *f) { return f->isElf(); } - ELFFileBase(Kind K, MemoryBufferRef M); - static bool classof(const InputFile *F) { return F->isElf(); } - - llvm::object::ELFFile<ELFT> getObj() const { - return check(llvm::object::ELFFile<ELFT>::create(MB.getBuffer())); + template <typename ELFT> llvm::object::ELFFile<ELFT> getObj() const { + return check(llvm::object::ELFFile<ELFT>::create(mb.getBuffer())); } - StringRef getStringTable() const { return StringTable; } - - uint32_t getSectionIndex(const Elf_Sym &Sym) const; + StringRef getStringTable() const { return stringTable; } - Elf_Sym_Range getGlobalELFSyms(); - Elf_Sym_Range getELFSyms() const { return ELFSyms; } + template <typename ELFT> typename ELFT::SymRange getELFSyms() const { + return typename ELFT::SymRange( + reinterpret_cast<const typename ELFT::Sym *>(elfSyms), numELFSyms); + } + template <typename ELFT> typename ELFT::SymRange getGlobalELFSyms() const { + return getELFSyms<ELFT>().slice(firstGlobal); + } protected: - ArrayRef<Elf_Sym> ELFSyms; - uint32_t FirstGlobal = 0; - ArrayRef<Elf_Word> SymtabSHNDX; - StringRef StringTable; - void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab); + // Initializes this class's member variables. + template <typename ELFT> void init(); + + const void *elfSyms = nullptr; + size_t numELFSyms = 0; + uint32_t firstGlobal = 0; + StringRef stringTable; }; // .o file. -template <class ELFT> class ObjFile : public ELFFileBase<ELFT> { - typedef ELFFileBase<ELFT> Base; - typedef typename ELFT::Rel Elf_Rel; - typedef typename ELFT::Rela Elf_Rela; - typedef typename ELFT::Sym Elf_Sym; - typedef typename ELFT::Shdr Elf_Shdr; - typedef typename ELFT::Word Elf_Word; - typedef typename ELFT::CGProfile Elf_CGProfile; - - StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections, - const Elf_Shdr &Sec); - ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec); +template <class ELFT> class ObjFile : public ELFFileBase { + using Elf_Rel = typename ELFT::Rel; + using Elf_Rela = typename ELFT::Rela; + using Elf_Sym = typename ELFT::Sym; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Word = typename ELFT::Word; + using Elf_CGProfile = typename ELFT::CGProfile; public: - static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; } + static bool classof(const InputFile *f) { return f->kind() == ObjKind; } + + llvm::object::ELFFile<ELFT> getObj() const { + return this->ELFFileBase::getObj<ELFT>(); + } ArrayRef<Symbol *> getLocalSymbols(); ArrayRef<Symbol *> getGlobalSymbols(); - ObjFile(MemoryBufferRef M, StringRef ArchiveName); - void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups); + ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) { + this->archiveName = archiveName; + } + + void parse(bool ignoreComdats = false); - Symbol &getSymbol(uint32_t SymbolIndex) const { - if (SymbolIndex >= this->Symbols.size()) + StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> sections, + const Elf_Shdr &sec); + + Symbol &getSymbol(uint32_t symbolIndex) const { + if (symbolIndex >= this->symbols.size()) fatal(toString(this) + ": invalid symbol index"); - return *this->Symbols[SymbolIndex]; + return *this->symbols[symbolIndex]; } - template <typename RelT> Symbol &getRelocTargetSym(const RelT &Rel) const { - uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); - return getSymbol(SymIndex); + uint32_t getSectionIndex(const Elf_Sym &sym) const; + + template <typename RelT> Symbol &getRelocTargetSym(const RelT &rel) const { + uint32_t symIndex = rel.getSymbol(config->isMips64EL); + return getSymbol(symIndex); } llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t); - llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name); + llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef name); // MIPS GP0 value defined by this file. This value represents the gp value // used to create the relocatable object and required to support // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations. - uint32_t MipsGp0 = 0; + uint32_t mipsGp0 = 0; + + uint32_t andFeatures = 0;< |