From: robert Date: Sat, 11 Nov 2023 18:22:09 +0000 (+0000) Subject: merge lld-16.0.6 X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=05edf1c10c70aea023b49c9190728f24a4673803;p=openbsd merge lld-16.0.6 --- diff --git a/gnu/llvm/lld/CMakeLists.txt b/gnu/llvm/lld/CMakeLists.txt index 3d6225646fe..b680e407a1b 100644 --- a/gnu/llvm/lld/CMakeLists.txt +++ b/gnu/llvm/lld/CMakeLists.txt @@ -161,6 +161,34 @@ list(INSERT CMAKE_MODULE_PATH 0 include(AddLLD) +option(LLD_ENABLE_WASM + "Enable support for WASM." + OFF) +if (LLD_ENABLE_WASM) + add_definitions("-DLLD_ENABLE_WASM=1") +endif() + +option(LLD_ENABLE_MACHO + "Enable support for MachO." + OFF) +if (LLD_ENABLE_MACHO) + add_definitions("-DLLD_ENABLE_MACHO=1") +endif() + +option(LLD_ENABLE_MINGW + "Enable support for MINGW." + OFF) +if (LLD_ENABLE_MINGW) + add_definitions("-DLLD_ENABLE_MINGW=1") +endif() + +option(LLD_ENABLE_COFF + "Enable support for COFF." + OFF) +if (LLD_ENABLE_COFF) + add_definitions("-DLLD_ENABLE_COFF=1") +endif() + option(LLD_USE_VTUNE "Enable VTune user task tracking." OFF) @@ -210,7 +238,9 @@ endif() add_subdirectory(docs) add_subdirectory(COFF) add_subdirectory(ELF) -add_subdirectory(MachO) +if (LLD_ENABLE_MACHO) + add_subdirectory(MachO) +endif() add_subdirectory(MinGW) add_subdirectory(wasm) diff --git a/gnu/llvm/lld/ELF/Arch/AArch64.cpp b/gnu/llvm/lld/ELF/Arch/AArch64.cpp index 7021aa000ab..1ed1227dce6 100644 --- a/gnu/llvm/lld/ELF/Arch/AArch64.cpp +++ b/gnu/llvm/lld/ELF/Arch/AArch64.cpp @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "Thunks.h" #include "lld/Common/ErrorHandler.h" -#include "llvm/Object/ELF.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; @@ -36,6 +36,7 @@ public: 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, const Symbol &sym, uint64_t pltEntryAddr) const override; @@ -48,12 +49,22 @@ public: void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; - void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; + void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; + +private: + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; +}; + +struct AArch64Relaxer { + bool safeToRelaxAdrpLdr = false; + + AArch64Relaxer(ArrayRef relocs); + bool tryRelaxAdrpAdd(const Relocation &adrpRel, const Relocation &addRel, + uint64_t secAddr, uint8_t *buf) const; + bool tryRelaxAdrpLdr(const Relocation &adrpRel, const Relocation &ldrRel, + uint64_t secAddr, uint8_t *buf) const; }; } // namespace @@ -62,7 +73,6 @@ AArch64::AArch64() { 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; @@ -71,7 +81,6 @@ AArch64::AArch64() { pltEntrySize = 16; ipltEntrySize = 16; defaultMaxPageSize = 65536; - gotBaseSymInGotPlt = false; // Align to the 2 MiB page size (known as a superpage or huge page). // FreeBSD automatically promotes 2 MiB-aligned allocations. @@ -199,6 +208,18 @@ int64_t AArch64::getImplicitAddend(const uint8_t *buf, RelType type) const { switch (type) { case R_AARCH64_TLSDESC: return read64(buf + 8); + case R_AARCH64_NONE: + case R_AARCH64_GLOB_DAT: + case R_AARCH64_JUMP_SLOT: + return 0; + case R_AARCH64_PREL32: + return SignExtend64<32>(read32(buf)); + case R_AARCH64_ABS64: + case R_AARCH64_PREL64: + case R_AARCH64_RELATIVE: + case R_AARCH64_IRELATIVE: + case R_AARCH64_TLS_TPREL64: + return read64(buf); default: internalLinkerError(getErrorLocation(buf), "cannot read addend for relocation " + toString(type)); @@ -210,12 +231,17 @@ void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const { write64(buf, in.plt->getVA()); } +void AArch64::writeIgotPlt(uint8_t *buf, const Symbol &s) const { + if (config->writeAddends) + write64(buf, s.getVA()); +} + 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]))] - 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.got.plt[2])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.got.plt[2]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.got.plt[2])) 0x20, 0x02, 0x1f, 0xd6, // br x17 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5, // nop @@ -234,9 +260,9 @@ void AArch64::writePltHeader(uint8_t *buf) const { void AArch64::writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) 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])) + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.got.plt[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.got.plt[n]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.got.plt[n])) 0x20, 0x02, 0x1f, 0xd6 // br x17 }; memcpy(buf, inst, sizeof(inst)); @@ -251,9 +277,11 @@ void AArch64::writePlt(uint8_t *buf, const Symbol &sym, bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s, int64_t a) const { - // If s is an undefined weak symbol and does not have a PLT entry then it - // will be resolved as a branch to the next instruction. - if (s.isUndefWeak() && !s.isInPlt()) + // 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 it is hidden, its + // binding has been converted to local, so we just check isUndefined() here. A + // undefined non-weak symbol will have been errored. + if (s.isUndefined() && !s.isInPlt()) return false; // ELF for the ARM 64-bit architecture, section Call and Jump relocations // only permits range extension thunks for R_AARCH64_CALL26 and @@ -358,7 +386,7 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: checkInt(loc, val, 33, rel); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_AARCH64_ADR_PREL_PG_HI21_NC: write32AArch64Addr(loc, val >> 12); break; @@ -375,7 +403,7 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, // transformation by placing a R_AARCH64_JUMP26 relocation at the offset of // the instruction we want to patch. write32le(loc, 0x14000000); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_AARCH64_CALL26: checkInt(loc, val, 28, rel); or32le(loc, (val & 0x0FFFFFFC) >> 2); @@ -419,19 +447,19 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, break; case R_AARCH64_MOVW_UABS_G0: checkUInt(loc, val, 16, rel); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_AARCH64_MOVW_UABS_G0_NC: or32le(loc, (val & 0xFFFF) << 5); break; case R_AARCH64_MOVW_UABS_G1: checkUInt(loc, val, 32, rel); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_AARCH64_MOVW_UABS_G1_NC: or32le(loc, (val & 0xFFFF0000) >> 11); break; case R_AARCH64_MOVW_UABS_G2: checkUInt(loc, val, 48, rel); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_AARCH64_MOVW_UABS_G2_NC: or32le(loc, (val & 0xFFFF00000000) >> 27); break; @@ -442,7 +470,7 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, case R_AARCH64_MOVW_SABS_G0: case R_AARCH64_TLSLE_MOVW_TPREL_G0: checkInt(loc, val, 17, rel); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_AARCH64_MOVW_PREL_G0_NC: case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC: writeSMovWImm(loc, val); @@ -451,7 +479,7 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, case R_AARCH64_MOVW_SABS_G1: case R_AARCH64_TLSLE_MOVW_TPREL_G1: checkInt(loc, val, 33, rel); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_AARCH64_MOVW_PREL_G1_NC: case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC: writeSMovWImm(loc, val >> 16); @@ -460,7 +488,7 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, case R_AARCH64_MOVW_SABS_G2: case R_AARCH64_TLSLE_MOVW_TPREL_G2: checkInt(loc, val, 49, rel); - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_AARCH64_MOVW_PREL_G2_NC: writeSMovWImm(loc, val >> 32); break; @@ -570,6 +598,194 @@ void AArch64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, llvm_unreachable("invalid relocation for TLS IE to LE relaxation"); } +AArch64Relaxer::AArch64Relaxer(ArrayRef relocs) { + if (!config->relax) + return; + // Check if R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC + // always appear in pairs. + size_t i = 0; + const size_t size = relocs.size(); + for (; i != size; ++i) { + if (relocs[i].type == R_AARCH64_ADR_GOT_PAGE) { + if (i + 1 < size && relocs[i + 1].type == R_AARCH64_LD64_GOT_LO12_NC) { + ++i; + continue; + } + break; + } else if (relocs[i].type == R_AARCH64_LD64_GOT_LO12_NC) { + break; + } + } + safeToRelaxAdrpLdr = i == size; +} + +bool AArch64Relaxer::tryRelaxAdrpAdd(const Relocation &adrpRel, + const Relocation &addRel, uint64_t secAddr, + uint8_t *buf) const { + // When the address of sym is within the range of ADR then + // we may relax + // ADRP xn, sym + // ADD xn, xn, :lo12: sym + // to + // NOP + // ADR xn, sym + if (!config->relax || adrpRel.type != R_AARCH64_ADR_PREL_PG_HI21 || + addRel.type != R_AARCH64_ADD_ABS_LO12_NC) + return false; + // Check if the relocations apply to consecutive instructions. + if (adrpRel.offset + 4 != addRel.offset) + return false; + if (adrpRel.sym != addRel.sym) + return false; + if (adrpRel.addend != 0 || addRel.addend != 0) + return false; + + uint32_t adrpInstr = read32le(buf + adrpRel.offset); + uint32_t addInstr = read32le(buf + addRel.offset); + // Check if the first instruction is ADRP and the second instruction is ADD. + if ((adrpInstr & 0x9f000000) != 0x90000000 || + (addInstr & 0xffc00000) != 0x91000000) + return false; + uint32_t adrpDestReg = adrpInstr & 0x1f; + uint32_t addDestReg = addInstr & 0x1f; + uint32_t addSrcReg = (addInstr >> 5) & 0x1f; + if (adrpDestReg != addDestReg || adrpDestReg != addSrcReg) + return false; + + Symbol &sym = *adrpRel.sym; + // Check if the address difference is within 1MiB range. + int64_t val = sym.getVA() - (secAddr + addRel.offset); + if (val < -1024 * 1024 || val >= 1024 * 1024) + return false; + + Relocation adrRel = {R_ABS, R_AARCH64_ADR_PREL_LO21, addRel.offset, + /*addend=*/0, &sym}; + // nop + write32le(buf + adrpRel.offset, 0xd503201f); + // adr x_ + write32le(buf + adrRel.offset, 0x10000000 | adrpDestReg); + target->relocate(buf + adrRel.offset, adrRel, val); + return true; +} + +bool AArch64Relaxer::tryRelaxAdrpLdr(const Relocation &adrpRel, + const Relocation &ldrRel, uint64_t secAddr, + uint8_t *buf) const { + if (!safeToRelaxAdrpLdr) + return false; + + // When the definition of sym is not preemptible then we may + // be able to relax + // ADRP xn, :got: sym + // LDR xn, [ xn :got_lo12: sym] + // to + // ADRP xn, sym + // ADD xn, xn, :lo_12: sym + + if (adrpRel.type != R_AARCH64_ADR_GOT_PAGE || + ldrRel.type != R_AARCH64_LD64_GOT_LO12_NC) + return false; + // Check if the relocations apply to consecutive instructions. + if (adrpRel.offset + 4 != ldrRel.offset) + return false; + // Check if the relocations reference the same symbol and + // skip undefined, preemptible and STT_GNU_IFUNC symbols. + if (!adrpRel.sym || adrpRel.sym != ldrRel.sym || !adrpRel.sym->isDefined() || + adrpRel.sym->isPreemptible || adrpRel.sym->isGnuIFunc()) + return false; + // Check if the addends of the both relocations are zero. + if (adrpRel.addend != 0 || ldrRel.addend != 0) + return false; + uint32_t adrpInstr = read32le(buf + adrpRel.offset); + uint32_t ldrInstr = read32le(buf + ldrRel.offset); + // Check if the first instruction is ADRP and the second instruction is LDR. + if ((adrpInstr & 0x9f000000) != 0x90000000 || + (ldrInstr & 0x3b000000) != 0x39000000) + return false; + // Check the value of the sf bit. + if (!(ldrInstr >> 31)) + return false; + uint32_t adrpDestReg = adrpInstr & 0x1f; + uint32_t ldrDestReg = ldrInstr & 0x1f; + uint32_t ldrSrcReg = (ldrInstr >> 5) & 0x1f; + // Check if ADPR and LDR use the same register. + if (adrpDestReg != ldrDestReg || adrpDestReg != ldrSrcReg) + return false; + + Symbol &sym = *adrpRel.sym; + // GOT references to absolute symbols can't be relaxed to use ADRP/ADD in + // position-independent code because these instructions produce a relative + // address. + if (config->isPic && !cast(sym).section) + return false; + // Check if the address difference is within 4GB range. + int64_t val = + getAArch64Page(sym.getVA()) - getAArch64Page(secAddr + adrpRel.offset); + if (val != llvm::SignExtend64(val, 33)) + return false; + + Relocation adrpSymRel = {R_AARCH64_PAGE_PC, R_AARCH64_ADR_PREL_PG_HI21, + adrpRel.offset, /*addend=*/0, &sym}; + Relocation addRel = {R_ABS, R_AARCH64_ADD_ABS_LO12_NC, ldrRel.offset, + /*addend=*/0, &sym}; + + // adrp x_ + write32le(buf + adrpSymRel.offset, 0x90000000 | adrpDestReg); + // add x_, x_ + write32le(buf + addRel.offset, 0x91000000 | adrpDestReg | (adrpDestReg << 5)); + + target->relocate(buf + adrpSymRel.offset, adrpSymRel, + SignExtend64(getAArch64Page(sym.getVA()) - + getAArch64Page(secAddr + adrpSymRel.offset), + 64)); + target->relocate(buf + addRel.offset, addRel, SignExtend64(sym.getVA(), 64)); + tryRelaxAdrpAdd(adrpSymRel, addRel, secAddr, buf); + return true; +} + +void AArch64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { + uint64_t secAddr = sec.getOutputSection()->addr; + if (auto *s = dyn_cast(&sec)) + secAddr += s->outSecOff; + AArch64Relaxer relaxer(sec.relocs()); + for (size_t i = 0, size = sec.relocs().size(); i != size; ++i) { + const Relocation &rel = sec.relocs()[i]; + uint8_t *loc = buf + rel.offset; + const uint64_t val = + sec.getRelocTargetVA(sec.file, rel.type, rel.addend, + secAddr + rel.offset, *rel.sym, rel.expr); + switch (rel.expr) { + case R_AARCH64_GOT_PAGE_PC: + if (i + 1 < size && + relaxer.tryRelaxAdrpLdr(rel, sec.relocs()[i + 1], secAddr, buf)) { + ++i; + continue; + } + break; + case R_AARCH64_PAGE_PC: + if (i + 1 < size && + relaxer.tryRelaxAdrpAdd(rel, sec.relocs()[i + 1], secAddr, buf)) { + ++i; + continue; + } + break; + case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: + case R_RELAX_TLS_GD_TO_IE_ABS: + relaxTlsGdToIe(loc, rel, val); + continue; + case R_RELAX_TLS_GD_TO_LE: + relaxTlsGdToLe(loc, rel, val); + continue; + case R_RELAX_TLS_IE_TO_LE: + relaxTlsIeToLe(loc, rel, val); + continue; + default: + break; + } + relocate(loc, rel, val); + } +} + // 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 @@ -614,8 +830,7 @@ public: uint64_t pltEntryAddr) const override; private: - bool btiHeader; // bti instruction needed in PLT Header - bool btiEntry; // bti instruction needed in PLT Entry + bool btiHeader; // bti instruction needed in PLT Header and Entry bool pacEntry; // autia1716 instruction needed in PLT Entry }; } // namespace @@ -630,15 +845,14 @@ AArch64BtiPac::AArch64BtiPac() { // 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. + // the function in an executable being taken by a shared library, or + // non-preemptible ifunc referenced by non-GOT-generating, non-PLT-generating + // relocations. // The PAC PLT entries require dynamic loader support and this isn't known // from properties in the objects, so we use the command line flag. - btiEntry = btiHeader && !config->shared; pacEntry = config->zPacPlt; - if (btiEntry || pacEntry) { + if (btiHeader || pacEntry) { pltEntrySize = 24; ipltEntrySize = 24; } @@ -648,9 +862,9 @@ 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])) + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.got.plt[2])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.got.plt[2]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.got.plt[2])) 0x20, 0x02, 0x1f, 0xd6, // br x17 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5 // nop @@ -684,9 +898,9 @@ void AArch64BtiPac::writePlt(uint8_t *buf, const Symbol &sym, // [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])) + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.got.plt[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.got.plt[n]))] + 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.got.plt[n])) }; const uint8_t pacBr[] = { 0x9f, 0x21, 0x03, 0xd5, // autia1716 @@ -698,7 +912,12 @@ void AArch64BtiPac::writePlt(uint8_t *buf, const Symbol &sym, }; const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop - if (btiEntry) { + // NEEDS_COPY indicates a non-ifunc canonical PLT entry whose address may + // escape to shared objects. isInIplt indicates a non-preemptible ifunc. Its + // address may escape if referenced by a direct relocation. The condition is + // conservative. + bool hasBti = btiHeader && (sym.hasFlag(NEEDS_COPY) || sym.isInIplt); + if (hasBti) { memcpy(buf, btiData, sizeof(btiData)); buf += sizeof(btiData); pltEntryAddr += sizeof(btiData); @@ -715,7 +934,7 @@ void AArch64BtiPac::writePlt(uint8_t *buf, const Symbol &sym, memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr)); else memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr)); - if (!btiEntry) + if (!hasBti) // We didn't add the BTI c instruction so round out size with NOP. memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData)); } @@ -725,8 +944,8 @@ static TargetInfo *getTargetInfo() { static AArch64BtiPac t; return &t; #else - if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | - GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { + if ((config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) || + config->zPacPlt) { static AArch64BtiPac t; return &t; } diff --git a/gnu/llvm/lld/ELF/Arch/PPC.cpp b/gnu/llvm/lld/ELF/Arch/PPC.cpp index aaecef6ee94..87942c1e924 100644 --- a/gnu/llvm/lld/ELF/Arch/PPC.cpp +++ b/gnu/llvm/lld/ELF/Arch/PPC.cpp @@ -20,6 +20,9 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; +// Undefine the macro predefined by GCC powerpc32. +#undef PPC + namespace { class PPC final : public TargetInfo { public: @@ -27,6 +30,7 @@ public: 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 writeGotHeader(uint8_t *buf) const override; void writePltHeader(uint8_t *buf) const override { llvm_unreachable("should call writePPC32GlinkSection() instead"); @@ -47,14 +51,13 @@ public: uint64_t val) const override; RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; int getTlsGdRelaxSkip(RelType type) const override; - void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; + void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; + +private: + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; }; } // namespace @@ -74,7 +77,7 @@ void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { // non-GOT-non-PLT relocations referencing external functions for -fpie/-fPIE. uint32_t glink = in.plt->getVA(); // VA of .glink if (!config->isPic) { - for (const Symbol *sym : cast(in.plt)->canonical_plts) { + for (const Symbol *sym : cast(*in.plt).canonical_plts) { writePPC32PltCallStub(buf, sym->getGotPltVA(), nullptr, 0); buf += 16; glink += 16; @@ -151,12 +154,10 @@ void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { PPC::PPC() { copyRel = R_PPC_COPY; 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 = 0; @@ -191,7 +192,7 @@ void PPC::writeGotHeader(uint8_t *buf) const { void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const { // Address of the symbol resolver stub in .glink . - write32(buf, in.plt->getVA() + in.plt->headerSize + 4 * s.pltIndex); + write32(buf, in.plt->getVA() + in.plt->headerSize + 4 * s.getPltIdx()); } bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file, @@ -274,6 +275,20 @@ RelType PPC::getDynRel(RelType type) const { return R_PPC_NONE; } +int64_t PPC::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + case R_PPC_NONE: + return 0; + case R_PPC_ADDR32: + case R_PPC_REL32: + return SignExtend64<32>(read32(buf)); + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + } +} + static std::pair fromDTPREL(RelType type, uint64_t val) { uint64_t dtpBiasedVal = val - 0x8000; switch (type) { @@ -467,6 +482,36 @@ void PPC::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, } } +void PPC::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { + uint64_t secAddr = sec.getOutputSection()->addr; + if (auto *s = dyn_cast(&sec)) + secAddr += s->outSecOff; + for (const Relocation &rel : sec.relocs()) { + uint8_t *loc = buf + rel.offset; + const uint64_t val = SignExtend64( + sec.getRelocTargetVA(sec.file, rel.type, rel.addend, + secAddr + rel.offset, *rel.sym, rel.expr), + 32); + switch (rel.expr) { + case R_RELAX_TLS_GD_TO_IE_GOT_OFF: + relaxTlsGdToIe(loc, rel, val); + break; + case R_RELAX_TLS_GD_TO_LE: + relaxTlsGdToLe(loc, rel, val); + break; + case R_RELAX_TLS_LD_TO_LE_ABS: + relaxTlsLdToLe(loc, rel, val); + break; + case R_RELAX_TLS_IE_TO_LE: + relaxTlsIeToLe(loc, rel, val); + break; + default: + relocate(loc, rel, val); + break; + } + } +} + TargetInfo *elf::getPPCTargetInfo() { static PPC target; return ⌖ diff --git a/gnu/llvm/lld/ELF/Arch/PPC64.cpp b/gnu/llvm/lld/ELF/Arch/PPC64.cpp index dae2ac140dc..511a99c8b24 100644 --- a/gnu/llvm/lld/ELF/Arch/PPC64.cpp +++ b/gnu/llvm/lld/ELF/Arch/PPC64.cpp @@ -6,13 +6,14 @@ // //===----------------------------------------------------------------------===// +#include "InputFiles.h" +#include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/Support/Endian.h" using namespace llvm; @@ -148,6 +149,46 @@ enum class LegacyToPrefixMask : uint64_t { 0x8000000003e00000, // S/T (6-10) - The [S/T]X bit moves from 28 to 5. }; +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; + RelType getDynRel(RelType type) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const override; + void writeIplt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void writeGotHeader(uint8_t *buf) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s, + int64_t a) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; + RelExpr adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const override; + void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; + + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; + +private: + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; + void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; +}; +} // namespace + uint64_t elf::getPPC64TocBase() { // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The // TOC starts where the first of these sections starts. We always create a @@ -187,11 +228,6 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { 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; -} - void elf::writePrefixedInstruction(uint8_t *loc, uint64_t insn) { insn = config->isLE ? insn << 32 | insn >> 32 : insn; write64(loc, insn); @@ -199,11 +235,11 @@ void elf::writePrefixedInstruction(uint8_t *loc, uint64_t insn) { static bool addOptional(StringRef name, uint64_t value, std::vector &defined) { - Symbol *sym = symtab->find(name); + Symbol *sym = symtab.find(name); if (!sym || sym->isDefined()) return false; - sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL, - STV_HIDDEN, STT_FUNC, value, + sym->resolve(Defined{/*file=*/nullptr, StringRef(), STB_GLOBAL, STV_HIDDEN, + STT_FUNC, value, /*size=*/0, /*section=*/nullptr}); defined.push_back(cast(sym)); return true; @@ -235,10 +271,10 @@ static void writeSequence(MutableArrayRef buf, const char *prefix, // instructions and write [first,end). auto *sec = make( nullptr, SHF_ALLOC, SHT_PROGBITS, 4, - makeArrayRef(reinterpret_cast(buf.data() + first), - 4 * (buf.size() - first)), + ArrayRef(reinterpret_cast(buf.data() + first), + 4 * (buf.size() - first)), ".text"); - inputSections.push_back(sec); + ctx.inputSections.push_back(sec); for (Defined *sym : defined) { sym->section = sec; sym->value -= 4 * first; @@ -279,9 +315,6 @@ void elf::addPPC64SaveRestore() { template static std::pair 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. @@ -291,7 +324,10 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { // 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 relas = tocSec->template relas(); + ArrayRef relas = + tocSec->template relsOrRelas().relas; + if (relas.empty()) + return {}; uint64_t index = std::min(offset / 8, relas.size() - 1); for (;;) { if (relas[index].r_offset == offset) { @@ -325,7 +361,8 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { // ld/lwa 3, 0(3) # load the value from the address // // Returns true if the relaxation is performed. -bool elf::tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc) { +static bool tryRelaxPPC64TocIndirection(const Relocation &rel, + uint8_t *bufLoc) { assert(config->tocOptimize); if (rel.addend < 0) return false; @@ -356,51 +393,11 @@ bool elf::tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc) { return false; // Add PPC64TocOffset that will be subtracted by PPC64::relocate(). - target->relaxGot(bufLoc, rel, tocRelative + ppc64TocOffset); + static_cast(*target).relaxGot(bufLoc, rel, + 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; - RelType getDynRel(RelType type) const override; - void writePltHeader(uint8_t *buf) const override; - void writePlt(uint8_t *buf, const Symbol &sym, - uint64_t pltEntryAddr) const override; - void writeIplt(uint8_t *buf, const Symbol &sym, - uint64_t pltEntryAddr) const override; - void relocate(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void writeGotHeader(uint8_t *buf) const override; - bool needsThunk(RelExpr expr, RelType type, const InputFile *file, - uint64_t branchAddr, const Symbol &s, - int64_t a) const override; - uint32_t getThunkSectionSpacing() const override; - bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; - RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; - RelExpr adjustGotPcExpr(RelType type, int64_t addend, - const uint8_t *loc) const override; - void relaxGot(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - - bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, - uint8_t stOther) const override; -}; -} // namespace - // Relocation masks following the #lo(value), #hi(value), #ha(value), // #higher(value), #highera(value), #highest(value), and #highesta(value) // macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi @@ -568,7 +565,6 @@ static uint64_t readPrefixedInstruction(const uint8_t *loc) { PPC64::PPC64() { copyRel = R_PPC64_COPY; gotRel = R_PPC64_GLOB_DAT; - noneRel = R_PPC64_NONE; pltRel = R_PPC64_JMP_SLOT; relativeRel = R_PPC64_RELATIVE; iRelativeRel = R_PPC64_IRELATIVE; @@ -580,7 +576,6 @@ PPC64::PPC64() { #endif pltEntrySize = 4; ipltEntrySize = 16; // PPC64PltCallStub::size - gotBaseSymInGotPlt = false; gotHeaderEntriesNum = 1; gotPltHeaderEntriesNum = 2; needsThunks = true; @@ -624,7 +619,7 @@ int PPC64::getTlsGdRelaxSkip(RelType type) const { } static uint32_t getEFlags(InputFile *file) { - if (config->ekind == ELF64BEKind) + if (file->ekind == ELF64BEKind) return cast>(file)->getObj().getHeader().e_flags; return cast>(file)->getObj().getHeader().e_flags; } @@ -632,7 +627,7 @@ static uint32_t getEFlags(InputFile *file) { // 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) { + for (InputFile *f : ctx.objectFiles) { uint32_t flag = getEFlags(f); if (flag == 1) error(toString(f) + ": ABI version 1 is not supported"); @@ -1071,6 +1066,29 @@ RelType PPC64::getDynRel(RelType type) const { return R_PPC64_NONE; } +int64_t PPC64::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + case R_PPC64_NONE: + case R_PPC64_GLOB_DAT: + case R_PPC64_JMP_SLOT: + return 0; + case R_PPC64_REL32: + return SignExtend64<32>(read32(buf)); + case R_PPC64_ADDR64: + case R_PPC64_REL64: + case R_PPC64_RELATIVE: + case R_PPC64_IRELATIVE: + case R_PPC64_DTPMOD64: + case R_PPC64_DTPREL64: + case R_PPC64_TPREL64: + return read64(buf); + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + } +} + void PPC64::writeGotHeader(uint8_t *buf) const { write64(buf, getPPC64TocBase()); } @@ -1112,7 +1130,7 @@ void PPC64::writePltHeader(uint8_t *buf) const { void PPC64::writePlt(uint8_t *buf, const Symbol &sym, uint64_t /*pltEntryAddr*/) const { - int32_t offset = pltHeaderSize + sym.pltIndex * pltEntrySize; + int32_t offset = pltHeaderSize + sym.getPltIdx() * pltEntrySize; // bl __glink_PLTresolve write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc)); } @@ -1348,7 +1366,7 @@ void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { // of the TLS block. Therefore, in the case of R_PPC64_DTPREL34 we first // need to subtract that value then fallthrough to the general case. val -= dynamicThreadPointerOffset; - LLVM_FALLTHROUGH; + [[fallthrough]]; case R_PPC64_PCREL34: case R_PPC64_GOT_PCREL34: case R_PPC64_GOT_TLSGD_PCREL34: @@ -1392,9 +1410,10 @@ bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file, if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1) return true; - // If a symbol is a weak undefined and we are compiling an executable - // it doesn't need a range-extending thunk since it can't be called. - if (s.isUndefWeak() && !config->shared) + // An undefined weak symbol not in a PLT does not need a thunk. If it is + // hidden, its binding has been converted to local, so we just check + // isUndefined() here. A undefined non-weak symbol has been errored. + if (s.isUndefined()) return false; // If the offset exceeds the range of the branch type then it will need @@ -1510,6 +1529,87 @@ void PPC64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, } } +void PPC64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { + uint64_t secAddr = sec.getOutputSection()->addr; + if (auto *s = dyn_cast(&sec)) + secAddr += s->outSecOff; + uint64_t lastPPCRelaxedRelocOff = -1; + for (const Relocation &rel : sec.relocs()) { + uint8_t *loc = buf + rel.offset; + const uint64_t val = + sec.getRelocTargetVA(sec.file, rel.type, rel.addend, + secAddr + rel.offset, *rel.sym, rel.expr); + switch (rel.expr) { + case R_PPC64_RELAX_GOT_PC: { + // The R_PPC64_PCREL_OPT relocation must appear immediately after + // R_PPC64_GOT_PCREL34 in the relocations table at the same offset. + // We can only relax R_PPC64_PCREL_OPT if we have also relaxed + // the associated R_PPC64_GOT_PCREL34 since only the latter has an + // associated symbol. So save the offset when relaxing R_PPC64_GOT_PCREL34 + // and only relax the other if the saved offset matches. + if (rel.type == R_PPC64_GOT_PCREL34) + lastPPCRelaxedRelocOff = rel.offset; + if (rel.type == R_PPC64_PCREL_OPT && rel.offset != lastPPCRelaxedRelocOff) + break; + relaxGot(loc, rel, val); + break; + } + case R_PPC64_RELAX_TOC: + // rel.sym refers to the STT_SECTION symbol associated to the .toc input + // section. If an R_PPC64_TOC16_LO (.toc + addend) references the TOC + // entry, there may be R_PPC64_TOC16_HA not paired with + // R_PPC64_TOC16_LO_DS. Don't relax. This loses some relaxation + // opportunities but is safe. + if (ppc64noTocRelax.count({rel.sym, rel.addend}) || + !tryRelaxPPC64TocIndirection(rel, loc)) + relocate(loc, rel, val); + break; + case R_PPC64_CALL: + // If this is a call to __tls_get_addr, it may be part of a TLS + // sequence that has been relaxed and turned into a nop. In this + // case, we don't want to handle it as a call. + if (read32(loc) == 0x60000000) // nop + break; + + // Patch a nop (0x60000000) to a ld. + if (rel.sym->needsTocRestore) { + // gcc/gfortran 5.4, 6.3 and earlier versions do not add nop for + // recursive calls even if the function is preemptible. This is not + // wrong in the common case where the function is not preempted at + // runtime. Just ignore. + if ((rel.offset + 8 > sec.content().size() || + read32(loc + 4) != 0x60000000) && + rel.sym->file != sec.file) { + // Use substr(6) to remove the "__plt_" prefix. + errorOrWarn(getErrorLocation(loc) + "call to " + + lld::toString(*rel.sym).substr(6) + + " lacks nop, can't restore toc"); + break; + } + write32(loc + 4, 0xe8410018); // ld %r2, 24(%r1) + } + relocate(loc, rel, val); + break; + case R_RELAX_TLS_GD_TO_IE: + case R_RELAX_TLS_GD_TO_IE_GOT_OFF: + relaxTlsGdToIe(loc, rel, val); + break; + case R_RELAX_TLS_GD_TO_LE: + relaxTlsGdToLe(loc, rel, val); + break; + case R_RELAX_TLS_LD_TO_LE_ABS: + relaxTlsLdToLe(loc, rel, val); + break; + case R_RELAX_TLS_IE_TO_LE: + relaxTlsIeToLe(loc, rel, val); + break; + default: + relocate(loc, rel, val); + break; + } + } +} + // The prologue for a split-stack function is expected to look roughly // like this: // .Lglobal_entry_point: diff --git a/gnu/llvm/lld/ELF/Arch/RISCV.cpp b/gnu/llvm/lld/ELF/Arch/RISCV.cpp index baf0173bf84..87887b314a5 100644 --- a/gnu/llvm/lld/ELF/Arch/RISCV.cpp +++ b/gnu/llvm/lld/ELF/Arch/RISCV.cpp @@ -7,9 +7,16 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" +#include "llvm/Support/ELFAttributes.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/RISCVAttributeParser.h" +#include "llvm/Support/RISCVAttributes.h" +#include "llvm/Support/RISCVISAInfo.h" +#include "llvm/Support/TimeProfiler.h" using namespace llvm; using namespace llvm::object; @@ -36,6 +43,7 @@ public: const uint8_t *loc) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + bool relaxOnce(int pass) const override; }; } // end anonymous namespace @@ -54,6 +62,7 @@ enum Op { enum Reg { X_RA = 1, + X_TP = 4, X_T0 = 5, X_T1 = 6, X_T2 = 7, @@ -73,9 +82,21 @@ static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) { return op | (rd << 7) | (imm << 12); } +// 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 setLO12_I(uint32_t insn, uint32_t imm) { + return (insn & 0xfffff) | (imm << 20); +} +static uint32_t setLO12_S(uint32_t insn, uint32_t imm) { + return (insn & 0x1fff07f) | (extractBits(imm, 11, 5) << 25) | + (extractBits(imm, 4, 0) << 7); +} + RISCV::RISCV() { copyRel = R_RISCV_COPY; - noneRel = R_RISCV_NONE; pltRel = R_RISCV_JUMP_SLOT; relativeRel = R_RISCV_RELATIVE; iRelativeRel = R_RISCV_IRELATIVE; @@ -93,7 +114,6 @@ RISCV::RISCV() { gotRel = symbolicRel; // .got[0] = _DYNAMIC - gotBaseSymInGotPlt = false; gotHeaderEntriesNum = 1; // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map @@ -113,19 +133,21 @@ static uint32_t getEFlags(InputFile *f) { uint32_t RISCV::calcEFlags() const { // If there are only binary input files (from -b binary), use a // value of 0 for the ELF header flags. - if (objectFiles.empty()) + if (ctx.objectFiles.empty()) return 0; - uint32_t target = getEFlags(objectFiles.front()); + uint32_t target = getEFlags(ctx.objectFiles.front()); - for (InputFile *f : objectFiles) { + for (InputFile *f : ctx.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)) - warn(toString(f) + - ": linking object files with different floating-point ABI"); + error( + toString(f) + + ": cannot link object files with different floating-point ABI from " + + toString(ctx.objectFiles[0])); if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE)) error(toString(f) + @@ -144,8 +166,12 @@ int64_t RISCV::getImplicitAddend(const uint8_t *buf, RelType type) const { case R_RISCV_32: case R_RISCV_TLS_DTPMOD32: case R_RISCV_TLS_DTPREL32: + case R_RISCV_TLS_TPREL32: return SignExtend64<32>(read32le(buf)); case R_RISCV_64: + case R_RISCV_TLS_DTPMOD64: + case R_RISCV_TLS_DTPREL64: + case R_RISCV_TLS_TPREL64: return read64le(buf); case R_RISCV_RELATIVE: case R_RISCV_IRELATIVE: @@ -263,22 +289,16 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, 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_TPREL; - case R_RISCV_RELAX: - case R_RISCV_TPREL_ADD: - return R_NONE; case R_RISCV_ALIGN: - // Not just a hint; always padded to the worst-case number of NOPs, so may - // not currently be aligned, and without linker relaxation support we can't - // delete NOPs to realign. - errorOrWarn(getErrorLocation(loc) + "relocation R_RISCV_ALIGN requires " - "unimplemented linker relaxation; recompile with -mno-relax"); - return R_NONE; + return R_RELAX_HINT; + case R_RISCV_TPREL_ADD: + case R_RISCV_RELAX: + return config->relax ? R_RELAX_HINT : R_NONE; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); @@ -286,11 +306,6 @@ 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; -} - void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { const unsigned bits = config->wordsize * 8; @@ -303,7 +318,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { return; case R_RISCV_RVC_BRANCH: { - checkInt(loc, static_cast(val) >> 1, 8, rel); + checkInt(loc, val, 9, rel); checkAlignment(loc, val, 2, rel); uint16_t insn = read16le(loc) & 0xE383; uint16_t imm8 = extractBits(val, 8, 8) << 12; @@ -318,7 +333,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { } case R_RISCV_RVC_JUMP: { - checkInt(loc, static_cast(val) >> 1, 11, rel); + checkInt(loc, val, 12, rel); checkAlignment(loc, val, 2, rel); uint16_t insn = read16le(loc) & 0xE003; uint16_t imm11 = extractBits(val, 11, 11) << 12; @@ -349,7 +364,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { } case R_RISCV_JAL: { - checkInt(loc, static_cast(val) >> 1, 20, rel); + checkInt(loc, val, 21, rel); checkAlignment(loc, val, 2, rel); uint32_t insn = read32le(loc) & 0xFFF; @@ -364,7 +379,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { } case R_RISCV_BRANCH: { - checkInt(loc, static_cast(val) >> 1, 12, rel); + checkInt(loc, val, 13, rel); checkAlignment(loc, val, 2, rel); uint32_t insn = read32le(loc) & 0x1FFF07F; @@ -407,7 +422,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { case R_RISCV_LO12_I: { uint64_t hi = (val + 0x800) >> 12; uint64_t lo = val - (hi << 12); - write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20)); + write32le(loc, setLO12_I(read32le(loc), lo & 0xfff)); return; } @@ -416,9 +431,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { case R_RISCV_LO12_S: { 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); + write32le(loc, setLO12_S(read32le(loc), lo)); return; } @@ -478,6 +491,531 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { } } +namespace { +struct SymbolAnchor { + uint64_t offset; + Defined *d; + bool end; // true for the anchor of st_value+st_size +}; +} // namespace + +struct elf::RISCVRelaxAux { + // This records symbol start and end offsets which will be adjusted according + // to the nearest relocDeltas element. + SmallVector anchors; + // For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] : + // 0). + std::unique_ptr relocDeltas; + // For relocations[i], the actual type is relocTypes[i]. + std::unique_ptr relocTypes; + SmallVector writes; +}; + +static void initSymbolAnchors() { + SmallVector storage; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + sec->relaxAux = make(); + if (sec->relocs().size()) { + sec->relaxAux->relocDeltas = + std::make_unique(sec->relocs().size()); + sec->relaxAux->relocTypes = + std::make_unique(sec->relocs().size()); + } + } + } + // Store anchors (st_value and st_value+st_size) for symbols relative to text + // sections. + for (InputFile *file : ctx.objectFiles) + for (Symbol *sym : file->getSymbols()) { + auto *d = dyn_cast(sym); + if (!d || d->file != file) + continue; + if (auto *sec = dyn_cast_or_null(d->section)) + if (sec->flags & SHF_EXECINSTR && sec->relaxAux) { + // If sec is discarded, relaxAux will be nullptr. + sec->relaxAux->anchors.push_back({d->value, d, false}); + sec->relaxAux->anchors.push_back({d->value + d->size, d, true}); + } + } + // Sort anchors by offset so that we can find the closest relocation + // efficiently. For a zero size symbol, ensure that its start anchor precedes + // its end anchor. For two symbols with anchors at the same offset, their + // order does not matter. + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + llvm::sort(sec->relaxAux->anchors, [](auto &a, auto &b) { + return std::make_pair(a.offset, a.end) < + std::make_pair(b.offset, b.end); + }); + } + } +} + +// Relax R_RISCV_CALL/R_RISCV_CALL_PLT auipc+jalr to c.j, c.jal, or jal. +static void relaxCall(const InputSection &sec, size_t i, uint64_t loc, + Relocation &r, uint32_t &remove) { + const bool rvc = config->eflags & EF_RISCV_RVC; + const Symbol &sym = *r.sym; + const uint64_t insnPair = read64le(sec.content().data() + r.offset); + const uint32_t rd = extractBits(insnPair, 32 + 11, 32 + 7); + const uint64_t dest = + (r.expr == R_PLT_PC ? sym.getPltVA() : sym.getVA()) + r.addend; + const int64_t displace = dest - loc; + + if (rvc && isInt<12>(displace) && rd == 0) { + sec.relaxAux->relocTypes[i] = R_RISCV_RVC_JUMP; + sec.relaxAux->writes.push_back(0xa001); // c.j + remove = 6; + } else if (rvc && isInt<12>(displace) && rd == X_RA && + !config->is64) { // RV32C only + sec.relaxAux->relocTypes[i] = R_RISCV_RVC_JUMP; + sec.relaxAux->writes.push_back(0x2001); // c.jal + remove = 6; + } else if (isInt<21>(displace)) { + sec.relaxAux->relocTypes[i] = R_RISCV_JAL; + sec.relaxAux->writes.push_back(0x6f | rd << 7); // jal + remove = 4; + } +} + +// Relax local-exec TLS when hi20 is zero. +static void relaxTlsLe(const InputSection &sec, size_t i, uint64_t loc, + Relocation &r, uint32_t &remove) { + uint64_t val = r.sym->getVA(r.addend); + if (hi20(val) != 0) + return; + uint32_t insn = read32le(sec.content().data() + r.offset); + switch (r.type) { + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_ADD: + // Remove lui rd, %tprel_hi(x) and add rd, rd, tp, %tprel_add(x). + sec.relaxAux->relocTypes[i] = R_RISCV_RELAX; + remove = 4; + break; + case R_RISCV_TPREL_LO12_I: + // addi rd, rd, %tprel_lo(x) => addi rd, tp, st_value(x) + sec.relaxAux->relocTypes[i] = R_RISCV_32; + insn = (insn & ~(31 << 15)) | (X_TP << 15); + sec.relaxAux->writes.push_back(setLO12_I(insn, val)); + break; + case R_RISCV_TPREL_LO12_S: + // sw rs, %tprel_lo(x)(rd) => sw rs, st_value(x)(rd) + sec.relaxAux->relocTypes[i] = R_RISCV_32; + insn = (insn & ~(31 << 15)) | (X_TP << 15); + sec.relaxAux->writes.push_back(setLO12_S(insn, val)); + break; + } +} + +static bool relax(InputSection &sec) { + const uint64_t secAddr = sec.getVA(); + auto &aux = *sec.relaxAux; + bool changed = false; + + // Get st_value delta for symbols relative to this section from the previous + // iteration. + DenseMap valueDelta; + ArrayRef sa = ArrayRef(aux.anchors); + uint32_t delta = 0; + for (auto [i, r] : llvm::enumerate(sec.relocs())) { + for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) + if (!sa[0].end) + valueDelta[sa[0].d] = delta; + delta = aux.relocDeltas[i]; + } + for (const SymbolAnchor &sa : sa) + if (!sa.end) + valueDelta[sa.d] = delta; + sa = ArrayRef(aux.anchors); + delta = 0; + + std::fill_n(aux.relocTypes.get(), sec.relocs().size(), R_RISCV_NONE); + aux.writes.clear(); + for (auto [i, r] : llvm::enumerate(sec.relocs())) { + const uint64_t loc = secAddr + r.offset - delta; + uint32_t &cur = aux.relocDeltas[i], remove = 0; + switch (r.type) { + case R_RISCV_ALIGN: { + const uint64_t nextLoc = loc + r.addend; + const uint64_t align = PowerOf2Ceil(r.addend + 2); + // All bytes beyond the alignment boundary should be removed. + remove = nextLoc - ((loc + align - 1) & -align); + assert(static_cast(remove) >= 0 && + "R_RISCV_ALIGN needs expanding the content"); + break; + } + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + if (i + 1 != sec.relocs().size() && + sec.relocs()[i + 1].type == R_RISCV_RELAX) + relaxCall(sec, i, loc, r, remove); + break; + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_ADD: + case R_RISCV_TPREL_LO12_I: + case R_RISCV_TPREL_LO12_S: + if (i + 1 != sec.relocs().size() && + sec.relocs()[i + 1].type == R_RISCV_RELAX) + relaxTlsLe(sec, i, loc, r, remove); + break; + } + + // For all anchors whose offsets are <= r.offset, they are preceded by + // the previous relocation whose `relocDeltas` value equals `delta`. + // Decrease their st_value and update their st_size. + for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) { + if (sa[0].end) + sa[0].d->size = sa[0].offset - delta - sa[0].d->value; + else + sa[0].d->value -= delta - valueDelta.find(sa[0].d)->second; + } + delta += remove; + if (delta != cur) { + cur = delta; + changed = true; + } + } + + for (const SymbolAnchor &a : sa) { + if (a.end) + a.d->size = a.offset - delta - a.d->value; + else + a.d->value -= delta - valueDelta.find(a.d)->second; + } + // Inform assignAddresses that the size has changed. + if (!isUInt<16>(delta)) + fatal("section size decrease is too large"); + sec.bytesDropped = delta; + return changed; +} + +// When relaxing just R_RISCV_ALIGN, relocDeltas is usually changed only once in +// the absence of a linker script. For call and load/store R_RISCV_RELAX, code +// shrinkage may reduce displacement and make more relocations eligible for +// relaxation. Code shrinkage may increase displacement to a call/load/store +// target at a higher fixed address, invalidating an earlier relaxation. Any +// change in section sizes can have cascading effect and require another +// relaxation pass. +bool RISCV::relaxOnce(int pass) const { + llvm::TimeTraceScope timeScope("RISC-V relaxOnce"); + if (config->relocatable) + return false; + + if (pass == 0) + initSymbolAnchors(); + + SmallVector storage; + bool changed = false; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) + changed |= relax(*sec); + } + return changed; +} + +void elf::riscvFinalizeRelax(int passes) { + llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation"); + log("relaxation passes: " + Twine(passes)); + SmallVector storage; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + RISCVRelaxAux &aux = *sec->relaxAux; + if (!aux.relocDeltas) + continue; + + MutableArrayRef rels = sec->relocs(); + ArrayRef old = sec->content(); + size_t newSize = old.size() - aux.relocDeltas[rels.size() - 1]; + size_t writesIdx = 0; + uint8_t *p = context().bAlloc.Allocate(newSize); + uint64_t offset = 0; + int64_t delta = 0; + sec->content_ = p; + sec->size = newSize; + sec->bytesDropped = 0; + + // Update section content: remove NOPs for R_RISCV_ALIGN and rewrite + // instructions for relaxed relocations. + for (size_t i = 0, e = rels.size(); i != e; ++i) { + uint32_t remove = aux.relocDeltas[i] - delta; + delta = aux.relocDeltas[i]; + if (remove == 0 && aux.relocTypes[i] == R_RISCV_NONE) + continue; + + // Copy from last location to the current relocated location. + const Relocation &r = rels[i]; + uint64_t size = r.offset - offset; + memcpy(p, old.data() + offset, size); + p += size; + + // For R_RISCV_ALIGN, we will place `offset` in a location (among NOPs) + // to satisfy the alignment requirement. If both `remove` and r.addend + // are multiples of 4, it is as if we have skipped some NOPs. Otherwise + // we are in the middle of a 4-byte NOP, and we need to rewrite the NOP + // sequence. + int64_t skip = 0; + if (r.type == R_RISCV_ALIGN) { + if (remove % 4 || r.addend % 4) { + skip = r.addend - remove; + int64_t j = 0; + for (; j + 4 <= skip; j += 4) + write32le(p + j, 0x00000013); // nop + if (j != skip) { + assert(j + 2 == skip); + write16le(p + j, 0x0001); // c.nop + } + } + } else if (RelType newType = aux.relocTypes[i]) { + switch (newType) { + case R_RISCV_RELAX: + // Used by relaxTlsLe to indicate the relocation is ignored. + break; + case R_RISCV_RVC_JUMP: + skip = 2; + write16le(p, aux.writes[writesIdx++]); + break; + case R_RISCV_JAL: + skip = 4; + write32le(p, aux.writes[writesIdx++]); + break; + case R_RISCV_32: + // Used by relaxTlsLe to write a uint32_t then suppress the handling + // in relocateAlloc. + skip = 4; + write32le(p, aux.writes[writesIdx++]); + aux.relocTypes[i] = R_RISCV_NONE; + break; + default: + llvm_unreachable("unsupported type"); + } + } + + p += skip; + offset = r.offset + skip + remove; + } + memcpy(p, old.data() + offset, old.size() - offset); + + // Subtract the previous relocDeltas value from the relocation offset. + // For a pair of R_RISCV_CALL/R_RISCV_RELAX with the same offset, decrease + // their r_offset by the same delta. + delta = 0; + for (size_t i = 0, e = rels.size(); i != e;) { + uint64_t cur = rels[i].offset; + do { + rels[i].offset -= delta; + if (aux.relocTypes[i] != R_RISCV_NONE) + rels[i].type = aux.relocTypes[i]; + } while (++i != e && rels[i].offset == cur); + delta = aux.relocDeltas[i - 1]; + } + } + } +} + +namespace { +// Representation of the merged .riscv.attributes input sections. The psABI +// specifies merge policy for attributes. E.g. if we link an object without an +// extension with an object with the extension, the output Tag_RISCV_arch shall +// contain the extension. Some tools like objdump parse .riscv.attributes and +// disabling some instructions if the first Tag_RISCV_arch does not contain an +// extension. +class RISCVAttributesSection final : public SyntheticSection { +public: + RISCVAttributesSection() + : SyntheticSection(0, SHT_RISCV_ATTRIBUTES, 1, ".riscv.attributes") {} + + size_t getSize() const override { return size; } + void writeTo(uint8_t *buf) override; + + static constexpr StringRef vendor = "riscv"; + DenseMap intAttr; + DenseMap strAttr; + size_t size = 0; +}; +} // namespace + +static void mergeArch(RISCVISAInfo::OrderedExtensionMap &mergedExts, + unsigned &mergedXlen, const InputSectionBase *sec, + StringRef s) { + auto maybeInfo = RISCVISAInfo::parseNormalizedArchString(s); + if (!maybeInfo) { + errorOrWarn(toString(sec) + ": " + s + ": " + + llvm::toString(maybeInfo.takeError())); + return; + } + + // Merge extensions. + RISCVISAInfo &info = **maybeInfo; + if (mergedExts.empty()) { + mergedExts = info.getExtensions(); + mergedXlen = info.getXLen(); + } else { + for (const auto &ext : info.getExtensions()) { + if (auto it = mergedExts.find(ext.first); it != mergedExts.end()) { + if (std::tie(it->second.MajorVersion, it->second.MinorVersion) >= + std::tie(ext.second.MajorVersion, ext.second.MinorVersion)) + continue; + } + mergedExts[ext.first] = ext.second; + } + } +} + +static RISCVAttributesSection * +mergeAttributesSection(const SmallVector §ions) { + RISCVISAInfo::OrderedExtensionMap exts; + const InputSectionBase *firstStackAlign = nullptr; + unsigned firstStackAlignValue = 0, xlen = 0; + bool hasArch = false; + + in.riscvAttributes = std::make_unique(); + auto &merged = static_cast(*in.riscvAttributes); + + // Collect all tags values from attributes section. + const auto &attributesTags = RISCVAttrs::getRISCVAttributeTags(); + for (const InputSectionBase *sec : sections) { + RISCVAttributeParser parser; + if (Error e = parser.parse(sec->content(), support::little)) + warn(toString(sec) + ": " + llvm::toString(std::move(e))); + for (const auto &tag : attributesTags) { + switch (RISCVAttrs::AttrType(tag.attr)) { + // Integer attributes. + case RISCVAttrs::STACK_ALIGN: + if (auto i = parser.getAttributeValue(tag.attr)) { + auto r = merged.intAttr.try_emplace(tag.attr, *i); + if (r.second) { + firstStackAlign = sec; + firstStackAlignValue = *i; + } else if (r.first->second != *i) { + errorOrWarn(toString(sec) + " has stack_align=" + Twine(*i) + + " but " + toString(firstStackAlign) + + " has stack_align=" + Twine(firstStackAlignValue)); + } + } + continue; + case RISCVAttrs::UNALIGNED_ACCESS: + if (auto i = parser.getAttributeValue(tag.attr)) + merged.intAttr[tag.attr] |= *i; + continue; + + // String attributes. + case RISCVAttrs::ARCH: + if (auto s = parser.getAttributeString(tag.attr)) { + hasArch = true; + mergeArch(exts, xlen, sec, *s); + } + continue; + + // Attributes which use the default handling. + case RISCVAttrs::PRIV_SPEC: + case RISCVAttrs::PRIV_SPEC_MINOR: + case RISCVAttrs::PRIV_SPEC_REVISION: + break; + } + + // Fallback for deprecated priv_spec* and other unknown attributes: retain + // the attribute if all input sections agree on the value. GNU ld uses 0 + // and empty strings as default values which are not dumped to the output. + // TODO Adjust after resolution to + // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/352 + if (tag.attr % 2 == 0) { + if (auto i = parser.getAttributeValue(tag.attr)) { + auto r = merged.intAttr.try_emplace(tag.attr, *i); + if (!r.second && r.first->second != *i) + r.first->second = 0; + } + } else if (auto s = parser.getAttributeString(tag.attr)) { + auto r = merged.strAttr.try_emplace(tag.attr, *s); + if (!r.second && r.first->second != *s) + r.first->second = {}; + } + } + } + + if (hasArch) { + if (auto result = RISCVISAInfo::postProcessAndChecking( + std::make_unique(xlen, exts))) { + merged.strAttr.try_emplace(RISCVAttrs::ARCH, + saver().save((*result)->toString())); + } else { + errorOrWarn(llvm::toString(result.takeError())); + } + } + + // The total size of headers: format-version [ "vendor-name" + // [ . + size_t size = 5 + merged.vendor.size() + 1 + 5; + for (auto &attr : merged.intAttr) + if (attr.second != 0) + size += getULEB128Size(attr.first) + getULEB128Size(attr.second); + for (auto &attr : merged.strAttr) + if (!attr.second.empty()) + size += getULEB128Size(attr.first) + attr.second.size() + 1; + merged.size = size; + return &merged; +} + +void RISCVAttributesSection::writeTo(uint8_t *buf) { + const size_t size = getSize(); + uint8_t *const end = buf + size; + *buf = ELFAttrs::Format_Version; + write32(buf + 1, size - 1); + buf += 5; + + memcpy(buf, vendor.data(), vendor.size()); + buf += vendor.size() + 1; + + *buf = ELFAttrs::File; + write32(buf + 1, end - buf); + buf += 5; + + for (auto &attr : intAttr) { + if (attr.second == 0) + continue; + buf += encodeULEB128(attr.first, buf); + buf += encodeULEB128(attr.second, buf); + } + for (auto &attr : strAttr) { + if (attr.second.empty()) + continue; + buf += encodeULEB128(attr.first, buf); + memcpy(buf, attr.second.data(), attr.second.size()); + buf += attr.second.size() + 1; + } +} + +void elf::mergeRISCVAttributesSections() { + // Find the first input SHT_RISCV_ATTRIBUTES; return if not found. + size_t place = + llvm::find_if(ctx.inputSections, + [](auto *s) { return s->type == SHT_RISCV_ATTRIBUTES; }) - + ctx.inputSections.begin(); + if (place == ctx.inputSections.size()) + return; + + // Extract all SHT_RISCV_ATTRIBUTES sections into `sections`. + SmallVector sections; + llvm::erase_if(ctx.inputSections, [&](InputSectionBase *s) { + if (s->type != SHT_RISCV_ATTRIBUTES) + return false; + sections.push_back(s); + return true; + }); + + // Add the merged section. + ctx.inputSections.insert(ctx.inputSections.begin() + place, + mergeAttributesSection(sections)); +} + TargetInfo *elf::getRISCVTargetInfo() { static RISCV target; return ⌖ diff --git a/gnu/llvm/lld/ELF/Arch/X86_64.cpp b/gnu/llvm/lld/ELF/Arch/X86_64.cpp index 0ecdfe5f1a5..a44c2fe0b53 100644 --- a/gnu/llvm/lld/ELF/Arch/X86_64.cpp +++ b/gnu/llvm/lld/ELF/Arch/X86_64.cpp @@ -6,13 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "InputFiles.h" #include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" -#include "llvm/Object/ELF.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; @@ -41,19 +40,9 @@ public: int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void applyJumpInstrMod(uint8_t *loc, JumpModType type, unsigned size) const override; - RelExpr adjustGotPcExpr(RelType type, int64_t addend, const uint8_t *loc) const override; - void relaxGot(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; - void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const override; + void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const override; bool deleteFallThruJmpInsn(InputSection &is, InputFile *file, @@ -78,7 +67,6 @@ static const std::vector> nopInstructions = { 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; @@ -87,6 +75,7 @@ X86_64::X86_64() { tlsGotRel = R_X86_64_TPOFF64; tlsModuleIndexRel = R_X86_64_DTPMOD64; tlsOffsetRel = R_X86_64_DTPOFF64; + gotBaseSymInGotPlt = true; gotEntrySize = 8; pltHeaderSize = 16; pltEntrySize = 16; @@ -99,7 +88,11 @@ X86_64::X86_64() { defaultImageBase = 0x200000; } -int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; } +int X86_64::getTlsGdRelaxSkip(RelType type) const { + // TLSDESC relocations are processed separately. See relaxTlsGdToLe below. + return type == R_X86_64_GOTPC32_TLSDESC || type == R_X86_64_TLSDESC_CALL ? 1 + : 2; +} // Opcodes for the different X86_64 jmp instructions. enum JmpInsnOpcode : uint32_t { @@ -158,9 +151,9 @@ static JmpInsnOpcode getJmpInsnType(const uint8_t *first, // Returns the maximum size of the vector if no such relocation is found. static unsigned getRelocationWithOffset(const InputSection &is, uint64_t offset) { - unsigned size = is.relocations.size(); + unsigned size = is.relocs().size(); for (unsigned i = size - 1; i + 1 > 0; --i) { - if (is.relocations[i].offset == offset && is.relocations[i].expr != R_NONE) + if (is.relocs()[i].offset == offset && is.relocs()[i].expr != R_NONE) return i; } return size; @@ -254,13 +247,13 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file, // If this jmp insn can be removed, it is the last insn and the // relocation is 4 bytes before the end. unsigned rIndex = getRelocationWithOffset(is, is.getSize() - 4); - if (rIndex == is.relocations.size()) + if (rIndex == is.relocs().size()) return false; - Relocation &r = is.relocations[rIndex]; + Relocation &r = is.relocs()[rIndex]; // Check if the relocation corresponds to a direct jmp. - const uint8_t *secContents = is.data().data(); + const uint8_t *secContents = is.content().data(); // If it is not a direct jmp instruction, there is nothing to do here. if (*(secContents + r.offset - 1) != 0xe9) return false; @@ -276,16 +269,16 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file, // Now, check if flip and delete is possible. const unsigned sizeOfJmpCCInsn = 6; - // To flip, there must be atleast one JmpCC and one direct jmp. + // To flip, there must be at least one JmpCC and one direct jmp. if (is.getSize() < sizeOfDirectJmpInsn + sizeOfJmpCCInsn) - return 0; + return false; unsigned rbIndex = getRelocationWithOffset(is, (is.getSize() - sizeOfDirectJmpInsn - 4)); - if (rbIndex == is.relocations.size()) - return 0; + if (rbIndex == is.relocs().size()) + return false; - Relocation &rB = is.relocations[rbIndex]; + Relocation &rB = is.relocs()[rbIndex]; const uint8_t *jmpInsnB = secContents + rB.offset - 1; JmpInsnOpcode jmpOpcodeB = getJmpInsnType(jmpInsnB - 1, jmpInsnB); @@ -300,7 +293,8 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file, JmpInsnOpcode jInvert = invertJmpOpcode(jmpOpcodeB); if (jInvert == J_UNKNOWN) return false; - is.jumpInstrMods.push_back({jInvert, (rB.offset - 1), 4}); + is.jumpInstrMod = make(); + *is.jumpInstrMod = {rB.offset - 1, jInvert, 4}; // Move R's values to rB except the offset. rB = {r.expr, r.type, rB.offset, r.addend, r.sym}; // Cancel R @@ -313,9 +307,6 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file, 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: @@ -356,6 +347,8 @@ RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, return R_GOT_PC; case R_X86_64_GOTOFF64: return R_GOTPLTREL; + case R_X86_64_PLTOFF64: + return R_PLT_GOTPLT; case R_X86_64_GOTPC32: case R_X86_64_GOTPC64: return R_GOTPLTONLY_PC; @@ -410,7 +403,7 @@ void X86_64::writePlt(uint8_t *buf, const Symbol &sym, memcpy(buf, inst, sizeof(inst)); write32le(buf + 2, sym.getGotPltVA() - pltEntryAddr - 6); - write32le(buf + 7, sym.pltIndex); + write32le(buf + 7, sym.getPltIdx()); write32le(buf + 12, in.plt->getVA() - pltEntryAddr - 16); } @@ -421,8 +414,7 @@ RelType X86_64::getDynRel(RelType type) const { return R_X86_64_NONE; } -void X86_64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const { +static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) { if (rel.type == R_X86_64_TLSGD) { // Convert // .byte 0x66 @@ -441,29 +433,28 @@ void X86_64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, // 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(rel.type == R_X86_64_GOTPC32_TLSDESC); - if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { - error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " - "in callq *x@tlsdesc(%rip), %rax"); + } else if (rel.type == R_X86_64_GOTPC32_TLSDESC) { + // Convert leaq x@tlsdesc(%rip), %REG to movq $x@tpoff, %REG. + if ((loc[-3] & 0xfb) != 0x48 || loc[-2] != 0x8d || + (loc[-1] & 0xc7) != 0x05) { + errorOrWarn(getErrorLocation(loc - 3) + + "R_X86_64_GOTPC32_TLSDESC must be used " + "in leaq x@tlsdesc(%rip), %REG"); return; } - // movq $x@tpoff(%rip),%rax + loc[-3] = 0x48 | ((loc[-3] >> 2) & 1); loc[-2] = 0xc7; - loc[-1] = 0xc0; + loc[-1] = 0xc0 | ((loc[-1] >> 3) & 7); write32le(loc, val + 4); - // xchg ax,ax - loc[4] = 0x66; - loc[5] = 0x90; + } else { + // Convert call *x@tlsdesc(%REG) to xchg ax, ax. + assert(rel.type == R_X86_64_TLSDESC_CALL); + loc[0] = 0x66; + loc[1] = 0x90; } } -void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, - uint64_t val) const { +static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) { if (rel.type == R_X86_64_TLSGD) { // Convert // .byte 0x66 @@ -482,30 +473,29 @@ void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, // 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. + } else if (rel.type == R_X86_64_GOTPC32_TLSDESC) { + // Convert leaq x@tlsdesc(%rip), %REG to movq x@gottpoff(%rip), %REG. assert(rel.type == R_X86_64_GOTPC32_TLSDESC); - if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { - error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " - "in callq *x@tlsdesc(%rip), %rax"); + if ((loc[-3] & 0xfb) != 0x48 || loc[-2] != 0x8d || + (loc[-1] & 0xc7) != 0x05) { + errorOrWarn(getErrorLocation(loc - 3) + + "R_X86_64_GOTPC32_TLSDESC must be used " + "in leaq x@tlsdesc(%rip), %REG"); return; } - // movq x@gottpoff(%rip),%rax loc[-2] = 0x8b; write32le(loc, val); - // xchg ax,ax - loc[4] = 0x66; - loc[5] = 0x90; + } else { + // Convert call *x@tlsdesc(%rax) to xchg ax, ax. + assert(rel.type == R_X86_64_TLSDESC_CALL); + loc[0] = 0x66; + loc[1] = 0x90; } } // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to // R_X86_64_TPOFF32 so that it does not use GOT. -void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &, - uint64_t val) const { +static void relaxTlsIeToLe(uint8_t *loc, const Relocation &, uint64_t val) { uint8_t *inst = loc - 3; uint8_t reg = loc[-1] >> 3; uint8_t *regSlot = loc - 1; @@ -546,17 +536,7 @@ void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &, write32le(loc, val + 4); } -void X86_64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, - uint64_t val) const { - if (rel.type == R_X86_64_DTPOFF64) { - write64le(loc, val); - return; - } - if (rel.type == R_X86_64_DTPOFF32) { - write32le(loc, val); - return; - } - +static void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) { const uint8_t inst[] = { 0x66, 0x66, // .word 0x6666 0x66, // .byte 0x66 @@ -718,9 +698,12 @@ int64_t X86_64::getImplicitAddend(const uint8_t *buf, RelType type) const { case R_X86_64_GOT64: case R_X86_64_GOTOFF64: case R_X86_64_GOTPC64: + case R_X86_64_PLTOFF64: case R_X86_64_IRELATIVE: case R_X86_64_RELATIVE: return read64le(buf); + case R_X86_64_TLSDESC: + return read64le(buf + 8); case R_X86_64_JUMP_SLOT: case R_X86_64_NONE: // These relocations are defined as not having an implicit addend. @@ -732,6 +715,8 @@ int64_t X86_64::getImplicitAddend(const uint8_t *buf, RelType type) const { } } +static void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val); + void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { switch (rel.type) { case R_X86_64_8: @@ -755,18 +740,11 @@ void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { 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: case R_X86_64_PC32: - case R_X86_64_GOTTPOFF: case R_X86_64_PLT32: - case R_X86_64_TLSGD: - case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: checkInt(loc, val, 32, rel); @@ -779,8 +757,55 @@ void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { case R_X86_64_GOT64: case R_X86_64_GOTOFF64: case R_X86_64_GOTPC64: + case R_X86_64_PLTOFF64: write64le(loc, val); break; + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + if (rel.expr != R_GOT_PC) { + relaxGot(loc, rel, val); + } else { + checkInt(loc, val, 32, rel); + write32le(loc, val); + } + break; + case R_X86_64_GOTPC32_TLSDESC: + case R_X86_64_TLSDESC_CALL: + case R_X86_64_TLSGD: + if (rel.expr == R_RELAX_TLS_GD_TO_LE) { + relaxTlsGdToLe(loc, rel, val); + } else if (rel.expr == R_RELAX_TLS_GD_TO_IE) { + relaxTlsGdToIe(loc, rel, val); + } else { + checkInt(loc, val, 32, rel); + write32le(loc, val); + } + break; + case R_X86_64_TLSLD: + if (rel.expr == R_RELAX_TLS_LD_TO_LE) { + relaxTlsLdToLe(loc, rel, val); + } else { + checkInt(loc, val, 32, rel); + write32le(loc, val); + } + break; + case R_X86_64_GOTTPOFF: + if (rel.expr == R_RELAX_TLS_IE_TO_LE) { + relaxTlsIeToLe(loc, rel, val); + } else { + checkInt(loc, val, 32, rel); + write32le(loc, val); + } + break; + case R_X86_64_TPOFF32: + checkInt(loc, val, 32, rel); + write32le(loc, val); + break; + + case R_X86_64_TLSDESC: + // The addend is stored in the second 64-bit word. + write64le(loc + 8, val); + break; default: llvm_unreachable("unknown relocation"); } @@ -792,8 +817,8 @@ RelExpr X86_64::adjustGotPcExpr(RelType type, int64_t addend, // with addend != -4. Such an instruction does not load the full GOT entry, so // we cannot relax the relocation. E.g. movl x@GOTPCREL+4(%rip), %rax // (addend=0) loads the high 32 bits of the GOT entry. - if ((type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) || - addend != -4) + if (!config->relax || addend != -4 || + (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX)) return R_GOT_PC; const uint8_t op = loc[-2]; const uint8_t modRm = loc[-1]; @@ -886,7 +911,7 @@ static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, write32le(loc, val); } -void X86_64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const { +static void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) { checkInt(loc, val, 32, rel); const uint8_t op = loc[-2]; const uint8_t modRm = loc[-1]; @@ -932,7 +957,7 @@ void X86_64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const { bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const { if (!config->is64) { - error("Target doesn't support split stacks."); + error("target doesn't support split stacks"); return false; } @@ -960,6 +985,25 @@ bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, return false; } +void X86_64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { + uint64_t secAddr = sec.getOutputSection()->addr; + if (auto *s = dyn_cast(&sec)) + secAddr += s->outSecOff; + for (const Relocation &rel : sec.relocs()) { + if (rel.expr == R_NONE) // See deleteFallThruJmpInsn + continue; + uint8_t *loc = buf + rel.offset; + const uint64_t val = + sec.getRelocTargetVA(sec.file, rel.type, rel.addend, + secAddr + rel.offset, *rel.sym, rel.expr); + relocate(loc, rel, val); + } + if (sec.jumpInstrMod) { + applyJumpInstrMod(buf + sec.jumpInstrMod->offset, + sec.jumpInstrMod->original, sec.jumpInstrMod->size); + } +} + // If Intel Indirect Branch Tracking is enabled, we have to emit special PLT // entries containing endbr64 instructions. A PLT entry will be split into two // parts, one in .plt.sec (writePlt), and the other in .plt (writeIBTPlt). @@ -980,7 +1024,7 @@ IntelIBT::IntelIBT() { pltHeaderSize = 0; } void IntelIBT::writeGotPlt(uint8_t *buf, const Symbol &s) const { uint64_t va = - in.ibtPlt->getVA() + IBTPltHeaderSize + s.pltIndex * pltEntrySize; + in.ibtPlt->getVA() + IBTPltHeaderSize + s.getPltIdx() * pltEntrySize; write64le(buf, va); } @@ -1093,7 +1137,7 @@ void Retpoline::writePlt(uint8_t *buf, const Symbol &sym, write32le(buf + 7, sym.getGotPltVA() - pltEntryAddr - 11); write32le(buf + 12, -off - 16 + 32); write32le(buf + 17, -off - 21 + 18); - write32le(buf + 22, sym.pltIndex); + write32le(buf + 22, sym.getPltIdx()); write32le(buf + 27, -off - 31); } diff --git a/gnu/llvm/lld/ELF/Config.h b/gnu/llvm/lld/ELF/Config.h index 73d4c8bc156..64734b5b28a 100644 --- a/gnu/llvm/lld/ELF/Config.h +++ b/gnu/llvm/lld/ELF/Config.h @@ -11,26 +11,38 @@ #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/Option/ArgList.h" #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/PrettyStackTrace.h" #include +#include +#include #include -namespace lld { -namespace elf { +namespace lld::elf { class InputFile; +class BinaryFile; +class BitcodeFile; +class ELFFileBase; +class SharedFile; class InputSectionBase; +class EhInputSection; +class Symbol; +class BitcodeCompiler; -enum ELFKind { +enum ELFKind : uint8_t { ELFNoneKind, ELF32LEKind, ELF32BEKind, @@ -86,15 +98,40 @@ struct SymbolVersion { struct VersionDefinition { llvm::StringRef name; uint16_t id; - std::vector nonLocalPatterns; - std::vector localPatterns; + SmallVector nonLocalPatterns; + SmallVector localPatterns; +}; + +class LinkerDriver { +public: + void linkerMain(ArrayRef args); + void addFile(StringRef path, bool withLOption); + void addLibrary(StringRef name); + +private: + void createFiles(llvm::opt::InputArgList &args); + void inferMachineType(); + void link(llvm::opt::InputArgList &args); + template void compileBitcodeFiles(bool skipLinkedOutput); + + // True if we are in --whole-archive and --no-whole-archive. + bool inWholeArchive = false; + + // True if we are in --start-lib and --end-lib. + bool inLib = false; + + std::unique_ptr lto; + std::vector files; + +public: + SmallVector, 0> archiveFiles; }; // This struct contains the global configuration for the linker. // Most fields are direct mapping from the command line options // and such fields have the same name as the corresponding options. -// Most fields are initialized by the driver. -struct Configuration { +// Most fields are initialized by the ctx.driver. +struct Config { uint8_t osabi = 0; uint32_t andFeatures = 0; llvm::CachePruningPolicy thinLTOCachePolicy; @@ -117,9 +154,10 @@ struct Configuration { llvm::StringRef mapFile; llvm::StringRef outputFile; llvm::StringRef optRemarksFilename; - llvm::Optional optRemarksHotnessThreshold = 0; + std::optional optRemarksHotnessThreshold = 0; llvm::StringRef optRemarksPasses; llvm::StringRef optRemarksFormat; + llvm::StringRef optStatsFilename; llvm::StringRef progName; llvm::StringRef printArchiveStats; llvm::StringRef printSymbolOrder; @@ -127,24 +165,29 @@ struct Configuration { llvm::StringRef sysroot; llvm::StringRef thinLTOCacheDir; llvm::StringRef thinLTOIndexOnlyArg; + llvm::StringRef whyExtract; + StringRef zBtiReport = "none"; + StringRef zCetReport = "none"; llvm::StringRef ltoBasicBlockSections; std::pair thinLTOObjectSuffixReplace; std::pair thinLTOPrefixReplace; std::string rpath; - std::vector versionDefinitions; - std::vector auxiliaryList; - std::vector filterList; - std::vector searchPaths; - std::vector symbolOrderingFile; - std::vector thinLTOModulesToCompile; - std::vector undefined; - std::vector dynamicList; - std::vector buildIdVector; + llvm::SmallVector versionDefinitions; + llvm::SmallVector auxiliaryList; + llvm::SmallVector filterList; + llvm::SmallVector passPlugins; + llvm::SmallVector searchPaths; + llvm::SmallVector symbolOrderingFile; + llvm::SmallVector thinLTOModulesToCompile; + llvm::SmallVector undefined; + llvm::SmallVector dynamicList; + llvm::SmallVector buildIdVector; + llvm::SmallVector mllvmOpts; llvm::MapVector, uint64_t> callGraphProfile; bool allowMultipleDefinition; - bool androidPackDynRelocs; + bool androidPackDynRelocs = false; bool armHasBlx = false; bool armHasMovtMovw = false; bool armJ1J2BranchEncoding = false; @@ -153,10 +196,10 @@ struct Configuration { bool callGraphProfileSort; bool checkSections; bool checkDynamicRelocs; - bool compressDebugSections; + llvm::DebugCompressionType compressDebugSections; bool cref; - std::vector> deadRelocInNonAlloc; - bool defineCommon; + llvm::SmallVector, 0> + deadRelocInNonAlloc; bool demangle = true; bool dependentLibraries; bool disableVerify; @@ -178,10 +221,9 @@ struct Configuration { bool ignoreDataAddressEquality; bool ignoreFunctionAddressEquality; bool ltoCSProfileGenerate; + bool ltoPGOWarnMismatch; bool ltoDebugPassManager; bool ltoEmitAsm; - bool ltoNewPassManager; - bool ltoPseudoProbeForProfiling; bool ltoUniqueBasicBlockSectionNames; bool ltoWholeProgramVisibility; bool mergeArmExidx; @@ -193,6 +235,7 @@ struct Configuration { bool nostdlib; bool oFormatBinary; bool omagic; + bool opaquePointers; bool optEB = false; bool optEL = false; bool optimizeBBJumps; @@ -201,10 +244,12 @@ struct Configuration { bool pie; bool printGcSections; bool printIcfSections; + bool relax; bool relocatable; - bool relrPackDynRelocs; - bool saveTemps; - std::vector> shuffleSections; + bool relrGlibc = false; + bool relrPackDynRelocs = false; + llvm::DenseSet saveTempsArgs; + llvm::SmallVector, 0> shuffleSections; bool singleRoRx; bool shared; bool symbolic; @@ -213,6 +258,7 @@ struct Configuration { bool target1Rel; bool trace; bool thinLTOEmitImportsFiles; + bool thinLTOEmitIndexFiles; bool thinLTOIndexOnly; bool timeTraceEnabled; bool tocOptimize; @@ -221,7 +267,7 @@ struct Configuration { bool unique; bool useAndroidRelrTags = false; bool warnBackrefs; - std::vector warnBackrefsExclude; + llvm::SmallVector warnBackrefsExclude; bool warnCommon; bool warnMissingEntry; bool warnSymbolOrdering; @@ -260,13 +306,13 @@ struct Configuration { UnresolvedPolicy unresolvedSymbols; UnresolvedPolicy unresolvedSymbolsInShlib; Target2Policy target2; - bool Power10Stub; + bool power10Stubs; ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default; BuildIdKind buildId = BuildIdKind::None; SeparateSegmentKind zSeparate; ELFKind ekind = ELFNoneKind; uint16_t emachine = llvm::ELF::EM_NONE; - llvm::Optional imageBase; + std::optional imageBase; // commonPageSize and maxPageSize are influenced by nmagic or omagic // so may be set to 1 if either of those options is given. uint64_t commonPageSize; @@ -282,6 +328,7 @@ struct Configuration { StringRef thinLTOJobs; unsigned timeTraceGranularity; int32_t splitStackAdjustSize; + StringRef packageMetadata; // The following config options do not directly correspond to any // particular command line options. @@ -313,19 +360,10 @@ struct Configuration { // if that's true.) 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 hasStaticTlsModel{false}; + // 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 shared object contains code + // compiled with the initial-exec TLS model. + bool hasTlsIe = false; // Holds set of ELF header flags for the target. uint32_t eflags = 0; @@ -351,30 +389,83 @@ struct Configuration { // 4 for ELF32, 8 for ELF64. int wordsize; + + // Mode of MTE to write to the ELF note. Should be one of NT_MEMTAG_ASYNC (for + // async), NT_MEMTAG_SYNC (for sync), or NT_MEMTAG_LEVEL_NONE (for none). If + // async or sync is enabled, write the ELF note specifying the default MTE + // mode. + int androidMemtagMode; + // Signal to the dynamic loader to enable heap MTE. + bool androidMemtagHeap; + // Signal to the dynamic loader that this binary expects stack MTE. Generally, + // this means to map the primary and thread stacks as PROT_MTE. Note: This is + // not supported on Android 11 & 12. + bool androidMemtagStack; + + unsigned threadCount; +}; +struct ConfigWrapper { + Config c; + Config *operator->() { return &c; } +}; + +LLVM_LIBRARY_VISIBILITY extern ConfigWrapper config; + +struct DuplicateSymbol { + const Symbol *sym; + const InputFile *file; + InputSectionBase *section; + uint64_t value; +}; + +struct Ctx { + LinkerDriver driver; + SmallVector> memoryBuffers; + SmallVector objectFiles; + SmallVector sharedFiles; + SmallVector binaryFiles; + SmallVector bitcodeFiles; + SmallVector lazyBitcodeFiles; + SmallVector inputSections; + SmallVector ehInputSections; + // Duplicate symbol candidates. + SmallVector duplicates; + // Symbols in a non-prevailing COMDAT group which should be changed to an + // Undefined. + SmallVector, 0> nonPrevailingSyms; + // A tuple of (reference, extractedFile, sym). Used by --why-extract=. + SmallVector, 0> + whyExtractRecords; + // A mapping from a symbol to an InputFile referencing it backward. Used by + // --warn-backrefs. + llvm::DenseMap> + backwardReferences; + // True if SHT_LLVM_SYMPART is used. + std::atomic hasSympart{false}; + // True if there are TLS IE relocations. Set DF_STATIC_TLS if -shared. + std::atomic hasTlsIe{false}; + // True if we need to reserve two .got entries for local-dynamic TLS model. + std::atomic needsTlsLd{false}; + + void reset(); }; -// The only instance of Configuration struct. -extern Configuration *config; +LLVM_LIBRARY_VISIBILITY extern Ctx ctx; // The first two elements of versionDefinitions represent VER_NDX_LOCAL and // VER_NDX_GLOBAL. This helper returns other elements. static inline ArrayRef namedVersionDefs() { - return llvm::makeArrayRef(config->versionDefinitions).slice(2); + return llvm::ArrayRef(config->versionDefinitions).slice(2); } -static inline void errorOrWarn(const Twine &msg) { - if (!config->noinhibitExec) - error(msg); - else - warn(msg); -} +void errorOrWarn(const Twine &msg); static inline void internalLinkerError(StringRef loc, const Twine &msg) { errorOrWarn(loc + "internal linker error: " + msg + "\n" + llvm::getBugReportMsg()); } -} // namespace elf -} // namespace lld +} // namespace lld::elf #endif diff --git a/gnu/llvm/lld/ELF/Driver.cpp b/gnu/llvm/lld/ELF/Driver.cpp index ee9867293cf..3eca6aad5e6 100644 --- a/gnu/llvm/lld/ELF/Driver.cpp +++ b/gnu/llvm/lld/ELF/Driver.cpp @@ -15,7 +15,7 @@ // linker path or library paths) for each host OS. // // I don't think implicit default values are useful because they are -// usually explicitly specified by the compiler driver. They can even +// usually explicitly specified by the compiler ctx.driver. They can even // be harmful when you are doing cross-linking. Therefore, in LLD, we // simply trust the compiler driver to pass all required options and // don't try to make effort on our side. @@ -27,6 +27,7 @@ #include "ICF.h" #include "InputFiles.h" #include "InputSection.h" +#include "LTO.h" #include "LinkerScript.h" #include "MarkLive.h" #include "OutputSections.h" @@ -37,6 +38,7 @@ #include "Target.h" #include "Writer.h" #include "lld/Common/Args.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Filesystem.h" @@ -49,9 +51,11 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Config/llvm-config.h" #include "llvm/LTO/LTO.h" +#include "llvm/Object/Archive.h" #include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Parallel.h" @@ -71,66 +75,76 @@ using namespace llvm::support; using namespace lld; using namespace lld::elf; -Configuration *elf::config; -LinkerDriver *elf::driver; +ConfigWrapper elf::config; +Ctx elf::ctx; static void setConfigs(opt::InputArgList &args); static void readConfigs(opt::InputArgList &args); -bool elf::link(ArrayRef args, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - - errorHandler().cleanupCallback = []() { - freeArena(); +void elf::errorOrWarn(const Twine &msg) { + if (config->noinhibitExec) + warn(msg); + else + error(msg); +} + +void Ctx::reset() { + driver = LinkerDriver(); + memoryBuffers.clear(); + objectFiles.clear(); + sharedFiles.clear(); + binaryFiles.clear(); + bitcodeFiles.clear(); + lazyBitcodeFiles.clear(); + inputSections.clear(); + ehInputSections.clear(); + duplicates.clear(); + nonPrevailingSyms.clear(); + whyExtractRecords.clear(); + backwardReferences.clear(); + hasSympart.store(false, std::memory_order_relaxed); + needsTlsLd.store(false, std::memory_order_relaxed); +} + +bool elf::link(ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, + bool disableOutput) { + // This driver-specific context will be freed later by lldMain(). + auto *ctx = new CommonLinkerContext; + + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.cleanupCallback = []() { + elf::ctx.reset(); + symtab = SymbolTable(); - inputSections.clear(); outputSections.clear(); - archiveFiles.clear(); - binaryFiles.clear(); - bitcodeFiles.clear(); - lazyObjFiles.clear(); - objectFiles.clear(); - sharedFiles.clear(); - backwardReferences.clear(); + symAux.clear(); tar = nullptr; - memset(&in, 0, sizeof(in)); + in.reset(); - partitions = {Partition()}; + partitions.clear(); + partitions.emplace_back(); SharedFile::vernauxNum = 0; }; + ctx->e.logName = args::getFilenameWithoutExe(args[0]); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use " + "--error-limit=0 to see all errors)"; - errorHandler().logName = args::getFilenameWithoutExe(args[0]); - errorHandler().errorLimitExceededMsg = - "too many errors emitted, stopping now (use " - "-error-limit=0 to see all errors)"; - errorHandler().exitEarly = canExitEarly; - stderrOS.enable_colors(stderrOS.has_colors()); + config = ConfigWrapper(); + script = std::make_unique(); - config = make(); - driver = make(); - script = make(); - symtab = make(); + symAux.emplace_back(); - partitions = {Partition()}; + partitions.clear(); + partitions.emplace_back(); config->progName = args[0]; - driver->linkerMain(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) - exitLld(errorCount() ? 1 : 0); + elf::ctx.driver.linkerMain(args); - bool ret = errorCount() == 0; - if (!canExitEarly) - errorHandler().reset(); - return ret; + return errorCount() == 0; } // Parses a linker -m option. @@ -163,12 +177,15 @@ static std::tuple parseEmulation(StringRef emul) { .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) .Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9}) .Case("msp430elf", {ELF32LEKind, EM_MSP430}) + .Case("elf64_amdgpu", {ELF64LEKind, EM_AMDGPU}) .Default({ELFNoneKind, EM_NONE}); if (ret.first == ELFNoneKind) error("unknown emulation: " + emul); if (ret.second == EM_MSP430) osabi = ELFOSABI_STANDALONE; + else if (ret.second == EM_AMDGPU) + osabi = ELFOSABI_AMDGPU_HSA; return std::make_tuple(ret.first, ret.second, osabi); } @@ -197,18 +214,22 @@ std::vector> static getArchiveMembers( toString(std::move(err))); // Take ownership of memory buffers created for members of thin archives. - for (std::unique_ptr &mb : file->takeThinBuffers()) - make>(std::move(mb)); + std::vector> mbs = file->takeThinBuffers(); + std::move(mbs.begin(), mbs.end(), std::back_inserter(ctx.memoryBuffers)); return v; } +static bool isBitcode(MemoryBufferRef mb) { + return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; +} + // Opens a file and create a file object. Path has to be resolved already. void LinkerDriver::addFile(StringRef path, bool withLOption) { using namespace sys::fs; - Optional buffer = readFile(path); - if (!buffer.hasValue()) + std::optional buffer = readFile(path); + if (!buffer) return; MemoryBufferRef mbref = *buffer; @@ -222,65 +243,70 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) { readLinkerScript(mbref); return; case file_magic::archive: { - // Handle -whole-archive. + auto members = getArchiveMembers(mbref); if (inWholeArchive) { - for (const auto &p : getArchiveMembers(mbref)) - files.push_back(createObjectFile(p.first, path, p.second)); + for (const std::pair &p : members) { + if (isBitcode(p.first)) + files.push_back(make(p.first, path, p.second, false)); + else + files.push_back(createObjFile(p.first, path)); + } return; } - std::unique_ptr 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()) { - // 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 &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 &p : - getArchiveMembers(mbref)) - files.push_back(make(p.first, path, p.second)); - return; + archiveFiles.emplace_back(path, members.size()); + + // Handle archives and --start-lib/--end-lib using the same code path. This + // scans all the ELF relocatable object files and bitcode files in the + // archive rather than just the index file, with the benefit that the + // symbols are only loaded once. For many projects archives see high + // utilization rates and it is a net performance win. --start-lib scans + // symbols in the same order that llvm-ar adds them to the index, so in the + // common case the semantics are identical. If the archive symbol table was + // created in a different order, or is incomplete, this strategy has + // different semantics. Such output differences are considered user error. + // + // All files within the archive get the same group ID to allow mutual + // references for --warn-backrefs. + bool saved = InputFile::isInGroup; + InputFile::isInGroup = true; + for (const std::pair &p : members) { + auto magic = identify_magic(p.first.getBuffer()); + if (magic == file_magic::elf_relocatable) + files.push_back(createObjFile(p.first, path, true)); + else if (magic == file_magic::bitcode) + files.push_back(make(p.first, path, p.second, true)); + else + warn(path + ": archive member '" + p.first.getBufferIdentifier() + + "' is neither ET_REL nor LLVM bitcode"); } - - // Handle the regular case. - files.push_back(make(std::move(file))); + InputFile::isInGroup = saved; + if (!saved) + ++InputFile::nextGroupId; return; } - case file_magic::elf_shared_object: + case file_magic::elf_shared_object: { if (config->isStatic || config->relocatable) { error("attempted static link of dynamic object " + path); return; } - // DSOs usually have DT_SONAME tags in their ELF headers, and the - // sonames are used to identify DSOs. But if they are missing, - // they are identified by filenames. We don't know whether the new - // file has a DT_SONAME or not because we haven't parsed it yet. - // Here, we set the default soname for the file because we might - // need it later. - // - // 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( - make(mbref, withLOption ? path::filename(path) : path)); + // Shared objects are identified by soname. soname is (if specified) + // DT_SONAME and falls back to filename. If a file was specified by -lfoo, + // the directory part is ignored. Note that path may be a temporary and + // cannot be stored into SharedFile::soName. + path = mbref.getBufferIdentifier(); + auto *f = + make(mbref, withLOption ? path::filename(path) : path); + f->init(); + files.push_back(f); return; + } case file_magic::bitcode: + files.push_back(make(mbref, "", 0, inLib)); + break; case file_magic::elf_relocatable: - if (inLib) - files.push_back(make(mbref, "", 0)); - else - files.push_back(createObjectFile(mbref)); + files.push_back(createObjFile(mbref, "", inLib)); break; default: error(path + ": unknown file type"); @@ -289,8 +315,8 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) { // Add a given library by searching it from input search paths. void LinkerDriver::addLibrary(StringRef name) { - if (Optional path = searchLibrary(name)) - addFile(*path, /*withLOption=*/true); + if (std::optional path = searchLibrary(name)) + addFile(saver().save(*path), /*withLOption=*/true); else error("unable to find library -l" + name, ErrorTag::LibNotFound, {name}); } @@ -335,9 +361,6 @@ static void checkOptions() { if (!config->shared && !config->auxiliaryList.empty()) error("-f may not be used without -shared"); - if (!config->relocatable && !config->defineCommon) - error("-no-define-common not supported in non relocatable output"); - if (config->strip == StripPolicy::All && config->emitRelocs) error("--strip-all and --emit-relocs may not be used together"); @@ -373,7 +396,7 @@ static void checkOptions() { } if (config->singleRoRx && !script->hasSectionsCommand) - error("-execute-only and -no-rosegment cannot be used together"); + error("--execute-only and --no-rosegment cannot be used together"); } if (config->zRetpolineplt && config->zForceIbt) @@ -384,7 +407,13 @@ static void checkOptions() { error("-z pac-plt only supported on AArch64"); if (config->zForceBti) error("-z force-bti only supported on AArch64"); + if (config->zBtiReport != "none") + error("-z bti-report only supported on AArch64"); } + + if (config->emachine != EM_386 && config->emachine != EM_X86_64 && + config->zCetReport != "none") + error("-z cet-report only supported on X86 and X86_64"); } static const char *getReproduceOption(opt::InputArgList &args) { @@ -455,42 +484,84 @@ static uint8_t getZStartStopVisibility(opt::InputArgList &args) { return STV_PROTECTED; } +constexpr const char *knownZFlags[] = { + "combreloc", + "copyreloc", + "defs", + "execstack", + "force-bti", + "force-ibt", + "global", + "hazardplt", + "ifunc-noplt", + "initfirst", + "interpose", + "keep-text-section-prefix", + "lazy", + "muldefs", + "nobtcfi", + "nocombreloc", + "nocopyreloc", + "nodefaultlib", + "nodelete", + "nodlopen", + "noexecstack", + "nognustack", + "nokeep-text-section-prefix", + "nopack-relative-relocs", + "norelro", + "noretpolineplt", + "noseparate-code", + "nostart-stop-gc", + "notext", + "now", + "origin", + "pac-plt", + "pack-relative-relocs", + "rel", + "rela", + "relro", + "retpolineplt", + "rodynamic", + "separate-code", + "separate-loadable-segments", + "shstk", + "start-stop-gc", + "text", + "undefs", + "wxneeded", +}; + static bool isKnownZFlag(StringRef s) { - return s == "combreloc" || s == "copyreloc" || s == "defs" || - s == "execstack" || s == "force-bti" || s == "force-ibt" || - s == "global" || s == "hazardplt" || s == "ifunc-noplt" || - s == "initfirst" || s == "interpose" || - s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" || - s == "separate-code" || s == "separate-loadable-segments" || - s == "start-stop-gc" || s == "nobtcfi" || - s == "nocombreloc" || s == "nocopyreloc" || - s == "nodefaultlib" || s == "nodelete" || s == "nodlopen" || - s == "noexecstack" || s == "nognustack" || - s == "nokeep-text-section-prefix" || s == "norelro" || - s == "noretpolineplt" || - s == "noseparate-code" || s == "nostart-stop-gc" || s == "notext" || - s == "now" || s == "origin" || s == "pac-plt" || s == "rel" || - s == "rela" || s == "relro" || s == "retpolineplt" || - s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" || - s == "wxneeded" || s.startswith("common-page-size=") || + return llvm::is_contained(knownZFlags, s) || + s.startswith("common-page-size=") || s.startswith("bti-report=") || + s.startswith("cet-report=") || s.startswith("dead-reloc-in-nonalloc=") || s.startswith("max-page-size=") || s.startswith("stack-size=") || s.startswith("start-stop-visibility="); } -// Report an error for an unknown -z option. +// Report a warning 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())); + warn("unknown -z value: " + StringRef(arg->getValue())); } +constexpr const char *saveTempsValues[] = { + "resolution", "preopt", "promote", "internalize", "import", + "opt", "precodegen", "prelink", "combinedindex"}; + void LinkerDriver::linkerMain(ArrayRef argsArr) { ELFOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); - // Interpret this flag early because error() depends on them. + // Interpret these flags early because error()/warn() depend on them. errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20); + errorHandler().fatalWarnings = + args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false) && + !args.hasArg(OPT_no_warnings); + errorHandler().suppressWarnings = args.hasArg(OPT_no_warnings); checkZOptions(args); // Handle -help @@ -502,10 +573,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Handle -v or -version. // // A note about "compatible with GNU linkers" message: this is a hack for - // scripts generated by GNU Libtool 2.4.6 (released in February 2014 and - // still the newest version in March 2017) or earlier to recognize LLD as - // a GNU compatible linker. As long as an output for the -v option - // contains "GNU" or "with BFD", they recognize us as GNU-compatible. + // scripts generated by GNU Libtool up to 2021-10 to recognize LLD as + // a GNU compatible linker. See + // . // // This is somewhat ugly hack, but in reality, we had no choice other // than doing this. Considering the very long release cycle of Libtool, @@ -561,44 +631,18 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (errorCount()) return; - // The Target instance handles target-specific stuff, such as applying - // relocations or writing a PLT section. It also contains target-dependent - // values such as a default image base address. - target = getTarget(); - - switch (config->ekind) { - case ELF32LEKind: - link(args); - break; - case ELF32BEKind: - link(args); - break; - case ELF64LEKind: - link(args); - break; - case ELF64BEKind: - link(args); - break; - default: - llvm_unreachable("unknown Config->EKind"); - } + link(args); } if (config->timeTraceEnabled) { - if (auto E = timeTraceProfilerWrite(args.getLastArgValue(OPT_time_trace_file_eq).str(), - config->outputFile)) { - handleAllErrors(std::move(E), [&](const StringError &SE) { - error(SE.getMessage()); - }); - return; - } - + checkError(timeTraceProfilerWrite( + args.getLastArgValue(OPT_time_trace_eq).str(), config->outputFile)); timeTraceProfilerCleanup(); } } static std::string getRpath(opt::InputArgList &args) { - std::vector v = args::getStrings(args, OPT_rpath); + SmallVector v = args::getStrings(args, OPT_rpath); return llvm::join(v.begin(), v.end(), ":"); } @@ -609,7 +653,7 @@ static void setUnresolvedSymbolPolicy(opt::InputArgList &args) { OPT_warn_unresolved_symbols, true) ? UnresolvedPolicy::ReportError : UnresolvedPolicy::Warn; - // -shared implies -unresolved-symbols=ignore-all because missing + // -shared implies --unresolved-symbols=ignore-all because missing // symbols are likely to be resolved at runtime. bool diagRegular = !config->shared, diagShlib = !config->shared; @@ -703,6 +747,28 @@ static StringRef getDynamicLinker(opt::InputArgList &args) { return arg->getValue(); } +static int getMemtagMode(opt::InputArgList &args) { + StringRef memtagModeArg = args.getLastArgValue(OPT_android_memtag_mode); + if (!config->androidMemtagHeap && !config->androidMemtagStack) { + if (!memtagModeArg.empty()) + error("when using --android-memtag-mode, at least one of " + "--android-memtag-heap or " + "--android-memtag-stack is required"); + return ELF::NT_MEMTAG_LEVEL_NONE; + } + + if (memtagModeArg == "sync" || memtagModeArg.empty()) + return ELF::NT_MEMTAG_LEVEL_SYNC; + if (memtagModeArg == "async") + return ELF::NT_MEMTAG_LEVEL_ASYNC; + if (memtagModeArg == "none") + return ELF::NT_MEMTAG_LEVEL_NONE; + + error("unknown --android-memtag-mode value: \"" + memtagModeArg + + "\", should be one of {async, sync, none}"); + return ELF::NT_MEMTAG_LEVEL_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) @@ -774,32 +840,15 @@ static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) { return OrphanHandlingPolicy::Place; } -// Parses --power10-stubs= flags, to disable or enable Power 10 -// instructions in stubs. -static bool getP10StubOpt(opt::InputArgList &args) { - - if (args.getLastArgValue(OPT_power10_stubs_eq)== "no") - return false; - - if (!args.hasArg(OPT_power10_stubs_eq) && - args.hasArg(OPT_no_power10_stubs)) - return false; - - return true; -} - // Parse --build-id or --build-id=