diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp new file mode 100644 --- /dev/null +++ b/lld/ELF/Arch/LoongArch.cpp @@ -0,0 +1,604 @@ +//===- LoongArch.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "OutputSections.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +// The LoongArch `pcalau12i` is behaviorally equivalent to the AArch64 `adrp`, +// so a similar concept of "page" also applies here. A "page" is in fact just +// another way to refer to the 12-bit range allowed by the immediate field of +// the addi/ld/st instructions. +// +// Note: "pcalau12i" stands for something like "PC aligned add upper, from 12th +// bit, immediate"; LoongArch instructions have no canonical full names, +// neither English nor Chinese ones, according to the ISA manual. +static uint64_t getLoongArchPage(uint64_t p) { + return p & ~static_cast(0xFFF); +} + +// Calculate the adjusted page offset between dest and PC. +// +// We must specially handle the cases when the low 12 bits of dest are seen +// as negative, because the instructions consuming it (ld, st, addi, etc.) +// all sign-extend the immediate, unlike AArch64. The higher bits need +// tweaking too, due to potential usage in patterns like: +// +// pcalau12i A, %foo_hi20(sym) +// addi.d T, zero, %foo_lo12(sym) +// lu32i.d T, %foo64_lo20(sym) +// lu52i.d T, T, %foo64_hi12(sym) +// ldx.d A, A, T +// +// in which case the "pc + hi20" part is separately constructed from the rest +// which includes the higher 32-bit half and lo12, so the higher 32 bits need a +// nudge too, due to the signed addition performed by the ldx/stx. +uint64_t elf::getLoongArchPageOffset(uint64_t dest, uint64_t pc) { + uint64_t result = getLoongArchPage(dest) - getLoongArchPage(pc); + if ((dest & 0xfff) > 0x7ff) { + result += 0x1000; + result -= 0x100000000; + } + return result; +} + +namespace { + +class LoongArch final : public TargetInfo { +public: + LoongArch(); + uint32_t calcEFlags() const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void writeGotHeader(uint8_t *buf) 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; + RelType getDynRel(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + bool usesOnlyLowPageBits(RelType type) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; +}; + +} // end anonymous namespace + +const uint64_t dtpOffset = 0; + +enum Op { + SUB_W = 0x00110000, + SUB_D = 0x00118000, + SRLI_W = 0x00448000, + SRLI_D = 0x00450000, + ADDI_W = 0x02800000, + ADDI_D = 0x02c00000, + ANDI = 0x03400000, + PCADDU12I = 0x1c000000, + LD_W = 0x28800000, + LD_D = 0x28c00000, + JIRL = 0x4c000000, +}; + +enum Reg { + R_RA = 1, + R_TP = 2, + R_T0 = 12, + R_T1 = 13, + R_T2 = 14, + R_T3 = 15, +}; + +static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } +static uint32_t lo12(uint32_t val) { return val & 0xfff; } + +static uint32_t insn(uint32_t op, uint32_t d, uint32_t j, uint32_t k) { + return op | d | (j << 5) | (k << 10); +} + +// Extract bits v[begin:end], where range is inclusive. +static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { + return begin == 63 ? v >> end : (v & ((1ULL << (begin + 1)) - 1)) >> end; +} + +static uint32_t setD5k16(uint32_t insn, uint32_t imm) { + uint32_t immLo = extractBits(imm, 15, 0); + uint32_t immHi = extractBits(imm, 20, 16); + return (insn & 0xfc0003e0) | (immLo << 10) | immHi; +} + +static uint32_t setD10k16(uint32_t insn, uint32_t imm) { + uint32_t immLo = extractBits(imm, 15, 0); + uint32_t immHi = extractBits(imm, 25, 16); + return (insn & 0xfc000000) | (immLo << 10) | immHi; +} + +static uint32_t setJ20(uint32_t insn, uint32_t imm) { + return (insn & 0xfe00001f) | (extractBits(imm, 19, 0) << 5); +} + +static uint32_t setK12(uint32_t insn, uint32_t imm) { + return (insn & 0xffc003ff) | (extractBits(imm, 11, 0) << 10); +} + +static uint32_t setK16(uint32_t insn, uint32_t imm) { + return (insn & 0xfc0003ff) | (extractBits(imm, 15, 0) << 10); +} + +LoongArch::LoongArch() { + // The LoongArch ISA itself does not have a limit on page sizes. According to + // the ISA manual, the PS (page size) field in MTLB entries and CSR.STLBPS is + // 6 bits wide, meaning the maximum page size is 2^63 which is equivalent to + // "unlimited". + // However, practically the maximum usable page size is constrained by the + // kernel implementation, and 64KiB is the biggest non-huge page size + // supported by Linux as of v6.1. + defaultMaxPageSize = 65536; + write32(trapInstr.data(), 0x002a0000); // break 0 + + copyRel = R_LARCH_COPY; + pltRel = R_LARCH_JUMP_SLOT; + relativeRel = R_LARCH_RELATIVE; + iRelativeRel = R_LARCH_IRELATIVE; + + if (config->is64) { + symbolicRel = R_LARCH_64; + tlsModuleIndexRel = R_LARCH_TLS_DTPMOD64; + tlsOffsetRel = R_LARCH_TLS_DTPREL64; + tlsGotRel = R_LARCH_TLS_TPREL64; + } else { + symbolicRel = R_LARCH_32; + tlsModuleIndexRel = R_LARCH_TLS_DTPMOD32; + tlsOffsetRel = R_LARCH_TLS_DTPREL32; + tlsGotRel = R_LARCH_TLS_TPREL32; + } + + gotRel = symbolicRel; + + // .got[0] = _DYNAMIC + gotHeaderEntriesNum = 1; + + // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map + gotPltHeaderEntriesNum = 2; + + pltHeaderSize = 32; + pltEntrySize = 16; + ipltEntrySize = 16; +} + +static uint32_t getEFlags(InputFile *f) { + if (config->is64) + return cast>(f)->getObj().getHeader().e_flags; + return cast>(f)->getObj().getHeader().e_flags; +} + +uint32_t LoongArch::calcEFlags() const { + // If there are only binary input files (from -b binary), use a + // value of 0 for the ELF header flags. + if (ctx.objectFiles.empty()) + return 0; + + uint32_t target = getEFlags(ctx.objectFiles.front()); + + for (InputFile *f : ctx.objectFiles) { + if ((getEFlags(f) & EF_LOONGARCH_ABI_MODIFIER_MASK) != + (target & EF_LOONGARCH_ABI_MODIFIER_MASK)) + error(toString(f) + + ": cannot link object files with different ABI"); + + // We cannot process object ABI v0 files (containing stack relocations), + // unlike ld.bfd. + // + // Instead of blindly accepting every v0 object and only failing at + // relocation processing time, just disallow interlink altogether. We + // don't expect significant usage of object ABI v0 in the wild (the old + // world may continue using object ABI v0 for a while, but as it's not + // binary-compatible with the upstream i.e. new-world ecosystem, it's not + // being considered here). + // + // There are briefly some new-world systems with object ABI v0 binaries too. + // It is because these programs were built before v1 was finalized. + // These are not supported either due to the extremely small number of them, + // and the few impacted users are advised to simply rebuild world or + // reinstall a recent system. + if ((getEFlags(f) & EF_LOONGARCH_OBJABI_MASK) != EF_LOONGARCH_OBJABI_V1) + error(toString(f) + ": unsupported object file ABI version"); + } + + return target; +} + +int64_t LoongArch::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + case R_LARCH_32: + case R_LARCH_TLS_DTPMOD32: + case R_LARCH_TLS_DTPREL32: + case R_LARCH_TLS_TPREL32: + return SignExtend64<32>(read32le(buf)); + case R_LARCH_64: + case R_LARCH_TLS_DTPMOD64: + case R_LARCH_TLS_DTPREL64: + case R_LARCH_TLS_TPREL64: + return read64le(buf); + case R_LARCH_RELATIVE: + case R_LARCH_IRELATIVE: + return config->is64 ? read64le(buf) : read32le(buf); + case R_LARCH_NONE: + case R_LARCH_JUMP_SLOT: + // These relocations are defined as not having an implicit addend. + return 0; + } +} + +void LoongArch::writeGotHeader(uint8_t *buf) const { + if (config->is64) + write64le(buf, mainPart->dynamic->getVA()); + else + write32le(buf, mainPart->dynamic->getVA()); +} + +void LoongArch::writeGotPlt(uint8_t *buf, const Symbol &s) const { + if (config->is64) + write64le(buf, in.plt->getVA()); + else + write32le(buf, in.plt->getVA()); +} + +void LoongArch::writeIgotPlt(uint8_t *buf, const Symbol &s) const { + if (config->writeAddends) { + if (config->is64) + write64le(buf, s.getVA()); + else + write32le(buf, s.getVA()); + } +} + +void LoongArch::writePltHeader(uint8_t *buf) const { + // The LoongArch PLT is currently structured just like that of RISCV. + // Annoyingly, this means the PLT is still using `pcaddu12i` to perform + // PC-relative addressing (because `pcaddu12i` is the same as RISCV `auipc`), + // in contrast to the AArch64-like page-offset scheme with `pcalau12i` that + // is used everywhere else involving PC-relative operations in the LoongArch + // ELF psABI v2.0. + // The `pcrel_{hi20,lo12}` operators are illustrative only and not really + // supported by LoongArch assemblers. + // + // 1: pcaddu12i $t2, %pcrel_hi20(.got.plt) + // sub.[wd] $t1, $t1, $t3 + // ld.[wd] $t3, $t2, %pcrel_lo12(1b) ; t3 = _dl_runtime_resolve + // addi.[wd] $t1, $t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0] + // addi.[wd] $t0, $t2, %pcrel_lo12(1b) + // srli.[wd] $t1, $t1, (is64?1:2) ; t1 = &.got.plt[i] - &.got.plt[0] + // ld.[wd] $t0, $t0, Wordsize(t0) ; t0 = link_map + // jr $t3 + uint32_t offset = in.gotPlt->getVA() - in.plt->getVA(); + uint32_t sub = config->is64 ? SUB_D : SUB_W; + uint32_t ld = config->is64 ? LD_D : LD_W; + uint32_t addi = config->is64 ? ADDI_D : ADDI_W; + uint32_t srli = config->is64 ? SRLI_D : SRLI_W; + write32le(buf + 0, insn(PCADDU12I, R_T2, hi20(offset), 0)); + write32le(buf + 4, insn(sub, R_T1, R_T1, R_T3)); + write32le(buf + 8, insn(ld, R_T3, R_T2, lo12(offset))); + write32le(buf + 12, insn(addi, R_T1, R_T1, lo12(-target->pltHeaderSize - 12))); + write32le(buf + 16, insn(addi, R_T0, R_T2, lo12(offset))); + write32le(buf + 20, insn(srli, R_T1, R_T1, config->is64 ? 1 : 2)); + write32le(buf + 24, insn(ld, R_T0, R_T0, config->wordsize)); + write32le(buf + 28, insn(JIRL, 0, R_T3, 0)); +} + +void LoongArch::writePlt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const { + // See the comment in writePltHeader for reason why pcaddu12i is used instead + // of the pcalau12i that's more commonly seen in the ELF psABI v2.0 days. + // + // 1: pcaddu12i $t3, %pcrel_hi20(f@.got.plt) + // ld.[wd] $t3, $t3, %pcrel_lo12(1b) + // jirl $t1, $t3, 0 + // nop + uint32_t offset = sym.getGotPltVA() - pltEntryAddr; + write32le(buf + 0, insn(PCADDU12I, R_T3, hi20(offset), 0)); + write32le(buf + 4, + insn(config->is64 ? LD_D : LD_W, R_T3, R_T3, lo12(offset))); + write32le(buf + 8, insn(JIRL, R_T1, R_T3, 0)); + write32le(buf + 12, insn(ANDI, 0, 0, 0)); +} + +RelType LoongArch::getDynRel(RelType type) const { + return type == target->symbolicRel ? type + : static_cast(R_LARCH_NONE); +} + +RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_LARCH_NONE: + case R_LARCH_MARK_LA: + case R_LARCH_MARK_PCREL: + return R_NONE; + case R_LARCH_32: + case R_LARCH_64: + case R_LARCH_ABS_HI20: + case R_LARCH_ABS_LO12: + case R_LARCH_ABS64_LO20: + case R_LARCH_ABS64_HI12: + case R_LARCH_PCALA_LO12: + return R_ABS; + case R_LARCH_TLS_DTPREL32: + case R_LARCH_TLS_DTPREL64: + return R_DTPREL; + case R_LARCH_TLS_TPREL32: + case R_LARCH_TLS_TPREL64: + case R_LARCH_TLS_LE_HI20: + case R_LARCH_TLS_LE_LO12: + case R_LARCH_TLS_LE64_LO20: + case R_LARCH_TLS_LE64_HI12: + return R_TPREL; + case R_LARCH_ADD8: + case R_LARCH_ADD16: + case R_LARCH_ADD32: + case R_LARCH_ADD64: + case R_LARCH_SUB8: + case R_LARCH_SUB16: + case R_LARCH_SUB32: + case R_LARCH_SUB64: + // The LoongArch add/sub relocs are meant to behave the same as the RISCV + // counterparts; reuse its RelExpr to avoid having to define one more + // RelExpr type just to duplicate everything afterwards. + return R_RISCV_ADD; + case R_LARCH_32_PCREL: + return R_PC; + case R_LARCH_B16: + case R_LARCH_B21: + case R_LARCH_B26: + return R_PLT_PC; + case R_LARCH_GOT_PC_HI20: + case R_LARCH_GOT64_PC_LO20: + case R_LARCH_GOT64_PC_HI12: + return R_LOONGARCH_GOT_PAGE_PC; + case R_LARCH_TLS_LD_PC_HI20: + case R_LARCH_TLS_GD_PC_HI20: + return R_LOONGARCH_TLSGD_PAGE_PC; + case R_LARCH_TLS_IE_PC_HI20: + case R_LARCH_TLS_IE64_PC_LO20: + case R_LARCH_TLS_IE64_PC_HI12: + return R_LOONGARCH_GOT_PAGE_PC; + case R_LARCH_PCALA_HI20: + case R_LARCH_PCALA64_LO20: + case R_LARCH_PCALA64_HI12: + return R_LOONGARCH_PAGE_PC; + case R_LARCH_GOT_PC_LO12: + case R_LARCH_TLS_IE_PC_LO12: + return R_LOONGARCH_GOT; + case R_LARCH_GOT_HI20: + case R_LARCH_GOT_LO12: + case R_LARCH_GOT64_LO20: + case R_LARCH_GOT64_HI12: + case R_LARCH_TLS_IE_HI20: + case R_LARCH_TLS_IE_LO12: + case R_LARCH_TLS_IE64_LO20: + case R_LARCH_TLS_IE64_HI12: + return R_GOT; + case R_LARCH_TLS_LD_HI20: + return R_TLSLD_GOT; + case R_LARCH_TLS_GD_HI20: + return R_TLSGD_GOT; + case R_LARCH_RELAX: + // LoongArch linker relaxation is not defined yet. + return R_NONE; + + // A stack machine (read: global mutable state) is necessary for properly + // computing these relocs, and these relocs are already deprecated after + // the release of LoongArch ELF psABI v2.00, so we are not going to + // implement them. + case R_LARCH_SOP_PUSH_PCREL: + case R_LARCH_SOP_PUSH_ABSOLUTE: + case R_LARCH_SOP_PUSH_DUP: + case R_LARCH_SOP_PUSH_GPREL: + case R_LARCH_SOP_PUSH_TLS_TPREL: + case R_LARCH_SOP_PUSH_TLS_GOT: + case R_LARCH_SOP_PUSH_TLS_GD: + case R_LARCH_SOP_PUSH_PLT_PCREL: + case R_LARCH_SOP_ASSERT: + case R_LARCH_SOP_NOT: + case R_LARCH_SOP_SUB: + case R_LARCH_SOP_SL: + case R_LARCH_SOP_SR: + case R_LARCH_SOP_ADD: + case R_LARCH_SOP_AND: + case R_LARCH_SOP_IF_ELSE: + case R_LARCH_SOP_POP_32_S_10_5: + case R_LARCH_SOP_POP_32_U_10_12: + case R_LARCH_SOP_POP_32_S_10_12: + case R_LARCH_SOP_POP_32_S_10_16: + case R_LARCH_SOP_POP_32_S_10_16_S2: + case R_LARCH_SOP_POP_32_S_5_20: + case R_LARCH_SOP_POP_32_S_0_5_10_16_S2: + case R_LARCH_SOP_POP_32_S_0_10_10_16_S2: + case R_LARCH_SOP_POP_32_U: + // Nor are we implementing these two reloc types that were probably added + // without much thought, and already proposed to be removed. + // See https://github.com/loongson/LoongArch-Documentation/issues/51 + case R_LARCH_ADD24: + case R_LARCH_SUB24: + // Similarly for these two, long deprecated and unused even before the + // inception of LoongArch. + case R_LARCH_GNU_VTINHERIT: + case R_LARCH_GNU_VTENTRY: + error(getErrorLocation(loc) + + "cannot handle deprecated relocation " + toString(type) + + " against symbol " + toString(s)); + return R_NONE; + + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +bool LoongArch::usesOnlyLowPageBits(RelType type) const { + switch (type) { + default: + return false; + case R_LARCH_PCALA_LO12: + case R_LARCH_GOT_LO12: + case R_LARCH_GOT_PC_LO12: + case R_LARCH_TLS_IE_PC_LO12: + return true; + } +} + +void LoongArch::relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { + case R_LARCH_32_PCREL: + checkInt(loc, val, 32, rel); + [[fallthrough]]; + case R_LARCH_32: + write32le(loc, val); + return; + case R_LARCH_64: + write64le(loc, val); + return; + + case R_LARCH_B16: { + checkInt(loc, val, 18, rel); + checkAlignment(loc, val, 4, rel); + write32le(loc, setK16(read32le(loc), val >> 2)); + return; + } + + case R_LARCH_B21: { + checkInt(loc, val, 23, rel); + checkAlignment(loc, val, 4, rel); + write32le(loc, setD5k16(read32le(loc), val >> 2)); + return; + } + + case R_LARCH_B26: { + checkInt(loc, val, 28, rel); + checkAlignment(loc, val, 4, rel); + write32le(loc, setD10k16(read32le(loc), val >> 2)); + return; + } + + // Relocs intended for `addi`, `ld` or `st`. + case R_LARCH_ABS_LO12: + case R_LARCH_PCALA_LO12: + case R_LARCH_GOT_PC_LO12: + case R_LARCH_GOT_LO12: + case R_LARCH_TLS_LE_LO12: + case R_LARCH_TLS_IE_PC_LO12: + case R_LARCH_TLS_IE_LO12: { + write32le(loc, setK12(read32le(loc), extractBits(val, 11, 0))); + return; + } + + // Relocs intended for `lu12i.w` or `pcalau12i`. + case R_LARCH_ABS_HI20: + case R_LARCH_PCALA_HI20: + case R_LARCH_GOT_PC_HI20: + case R_LARCH_GOT_HI20: + case R_LARCH_TLS_LE_HI20: + case R_LARCH_TLS_IE_PC_HI20: + case R_LARCH_TLS_IE_HI20: + case R_LARCH_TLS_LD_PC_HI20: + case R_LARCH_TLS_LD_HI20: + case R_LARCH_TLS_GD_PC_HI20: + case R_LARCH_TLS_GD_HI20: { + write32le(loc, setJ20(read32le(loc), extractBits(val, 31, 12))); + return; + } + + // Relocs intended for `lu32i.d`. + case R_LARCH_ABS64_LO20: + case R_LARCH_PCALA64_LO20: + case R_LARCH_GOT64_PC_LO20: + case R_LARCH_GOT64_LO20: + case R_LARCH_TLS_LE64_LO20: + case R_LARCH_TLS_IE64_PC_LO20: + case R_LARCH_TLS_IE64_LO20: { + write32le(loc, setJ20(read32le(loc), extractBits(val, 51, 32))); + return; + } + + // Relocs intended for `lu52i.d`. + case R_LARCH_ABS64_HI12: + case R_LARCH_PCALA64_HI12: + case R_LARCH_GOT64_PC_HI12: + case R_LARCH_GOT64_HI12: + case R_LARCH_TLS_LE64_HI12: + case R_LARCH_TLS_IE64_PC_HI12: + case R_LARCH_TLS_IE64_HI12: { + write32le(loc, setK12(read32le(loc), extractBits(val, 63, 52))); + return; + } + + case R_LARCH_ADD8: + *loc += val; + return; + case R_LARCH_ADD16: + write16le(loc, read16le(loc) + val); + return; + case R_LARCH_ADD32: + write32le(loc, read32le(loc) + val); + return; + case R_LARCH_ADD64: + write64le(loc, read64le(loc) + val); + return; + case R_LARCH_SUB8: + *loc -= val; + return; + case R_LARCH_SUB16: + write16le(loc, read16le(loc) - val); + return; + case R_LARCH_SUB32: + write32le(loc, read32le(loc) - val); + return; + case R_LARCH_SUB64: + write64le(loc, read64le(loc) - val); + return; + + case R_LARCH_TLS_DTPREL32: + write32le(loc, val - dtpOffset); + break; + case R_LARCH_TLS_DTPREL64: + write64le(loc, val - dtpOffset); + break; + + case R_LARCH_MARK_LA: + case R_LARCH_MARK_PCREL: + // no-op + return; + + case R_LARCH_RELAX: + return; // Ignored (for now) + + default: + llvm_unreachable("unknown relocation"); + } +} + +TargetInfo *elf::getLoongArchTargetInfo() { + static LoongArch target; + return ⌖ +} diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -25,6 +25,7 @@ Arch/ARM.cpp Arch/AVR.cpp Arch/Hexagon.cpp + Arch/LoongArch.cpp Arch/Mips.cpp Arch/MipsArchTree.cpp Arch/MSP430.cpp diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -166,6 +166,7 @@ .Case("elf32lriscv", {ELF32LEKind, EM_RISCV}) .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC}) .Cases("elf32lppc", "elf32lppclinux", {ELF32LEKind, EM_PPC}) + .Case("elf32loongarch", {ELF32LEKind, EM_LOONGARCH}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64lriscv", {ELF64LEKind, EM_RISCV}) @@ -177,6 +178,7 @@ .Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9}) .Case("msp430elf", {ELF32LEKind, EM_MSP430}) .Case("elf64_amdgpu", {ELF64LEKind, EM_AMDGPU}) + .Case("elf64loongarch", {ELF64LEKind, EM_LOONGARCH}) .Default({ELFNoneKind, EM_NONE}); if (ret.first == ELFNoneKind) @@ -1031,8 +1033,9 @@ // Otherwise use the psABI defined relocation entry format. uint16_t m = config->emachine; - return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || m == EM_PPC || - m == EM_PPC64 || m == EM_RISCV || m == EM_X86_64; + return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || + m == EM_LOONGARCH || m == EM_PPC || m == EM_PPC64 || m == EM_RISCV || + m == EM_X86_64; } static void parseClangOption(StringRef opt, const Twine &msg) { @@ -1569,8 +1572,9 @@ // have support for reading Elf_Rel addends, so we only enable for a subset. #ifndef NDEBUG bool checkDynamicRelocsDefault = m == EM_AARCH64 || m == EM_ARM || - m == EM_386 || m == EM_MIPS || - m == EM_X86_64 || m == EM_RISCV; + m == EM_386 || m == EM_LOONGARCH || + m == EM_MIPS || m == EM_RISCV || + m == EM_X86_64; #else bool checkDynamicRelocsDefault = false; #endif diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -21,6 +21,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/xxhash.h" #include +#include #include #include @@ -609,6 +610,7 @@ // to allow a signed 16-bit offset to reach 0x1000 of TCB/thread-library // data and 0xf000 of the program's TLS segment. return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)) - 0x7000; + case EM_LOONGARCH: case EM_RISCV: return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)); @@ -643,6 +645,21 @@ case R_GOT: case R_RELAX_TLS_GD_TO_IE_ABS: return sym.getGotVA() + a; + case R_LOONGARCH_GOT: + // The LoongArch PC-relative GOT relocs are notorious, in that the TLS + // relocs share the same reloc type with non-TLS ones for their page + // offsets. The arithmetics are different in the TLS case so we have to + // duplicate some logic here. + if (sym.isTls()) { + if (sym.hasFlag(NEEDS_TLSGD)) + // Like R_LOONGARCH_TLSGD_PAGE_PC but taking the absolute value. + return in.got->getGlobalDynAddr(sym) + a; + if (ctx.needsTlsLd.load(std::memory_order_relaxed)) + // Like R_TLSLD_PC but taking the absolute value. + return in.got->getTlsIndexVA() + a; + // Fallthrough; relocate like R_GOT. + } + return sym.getGotVA() + a; case R_GOTONLY_PC: return in.got->getVA() + a - p; case R_GOTPLTONLY_PC: @@ -667,6 +684,8 @@ case R_GOT_PC: case R_RELAX_TLS_GD_TO_IE: return sym.getGotVA() + a - p; + case R_LOONGARCH_GOT_PAGE_PC: + return getLoongArchPageOffset(sym.getGotVA() + a, p); case R_MIPS_GOTREL: return sym.getVA(a) - in.mipsGot->getGp(file); case R_MIPS_GOT_GP: @@ -715,6 +734,8 @@ *hiRel->sym, hiRel->expr); return 0; } + case R_LOONGARCH_PAGE_PC: + return getLoongArchPageOffset(sym.getVA(a), p); case R_PC: case R_ARM_PCA: { uint64_t dest; @@ -808,6 +829,8 @@ return in.got->getGlobalDynAddr(sym) + a - in.gotPlt->getVA(); case R_TLSGD_PC: return in.got->getGlobalDynAddr(sym) + a - p; + case R_LOONGARCH_TLSGD_PAGE_PC: + return getLoongArchPageOffset(in.got->getGlobalDynAddr(sym) + a, p); case R_TLSLD_GOTPLT: return in.got->getVA() + in.got->getTlsIndexOff() + a - in.gotPlt->getVA(); case R_TLSLD_GOT: diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -102,6 +102,10 @@ R_PPC64_RELAX_GOT_PC, R_RISCV_ADD, R_RISCV_PC_INDIRECT, + R_LOONGARCH_PAGE_PC, + R_LOONGARCH_GOT, + R_LOONGARCH_GOT_PAGE_PC, + R_LOONGARCH_TLSGD_PAGE_PC, }; // Architecture-neutral representation of relocation. diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -200,7 +200,8 @@ static bool needsGot(RelExpr expr) { return oneof(expr); + R_AARCH64_GOT_PAGE, R_LOONGARCH_GOT, R_LOONGARCH_GOT_PAGE_PC>( + expr); } // True if this expression is of the form Sym - X, where X is a position in the @@ -208,7 +209,8 @@ static bool isRelExpr(RelExpr expr) { return oneof(expr); + R_RISCV_PC_INDIRECT, R_PPC64_RELAX_GOT_PC, + R_LOONGARCH_PAGE_PC>(expr); } @@ -946,7 +948,8 @@ R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC, R_PLT_PC, R_PLT_GOTPLT, R_PPC32_PLTREL, R_PPC64_CALL_PLT, - R_PPC64_RELAX_TOC, R_RISCV_ADD, R_AARCH64_GOT_PAGE>(e)) + R_PPC64_RELAX_TOC, R_RISCV_ADD, R_AARCH64_GOT_PAGE, + R_LOONGARCH_GOT, R_LOONGARCH_GOT_PAGE_PC>(e)) return true; // These never do, except if the entire file is position dependent or if @@ -1082,7 +1085,8 @@ bool canWrite = (sec->flags & SHF_WRITE) || !config->zText; if (canWrite) { RelType rel = target->getDynRel(type); - if (expr == R_GOT || (rel == target->symbolicRel && !sym.isPreemptible)) { + if (oneof(expr) || + (rel == target->symbolicRel && !sym.isPreemptible)) { addRelativeReloc(*sec, offset, sym, addend, expr, type); return; } else if (rel != 0) { @@ -1234,11 +1238,13 @@ return 1; } - // ARM, Hexagon and RISC-V do not support GD/LD to IE/LE relaxation. For - // PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable + // ARM, Hexagon, LoongArch and RISC-V do not support GD/LD to IE/LE + // relaxation. + // For PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable // relaxation as well. bool toExecRelax = !config->shared && config->emachine != EM_ARM && config->emachine != EM_HEXAGON && + config->emachine != EM_LOONGARCH && config->emachine != EM_RISCV && !c.file->ppc64DisableTLSRelax; @@ -1255,8 +1261,7 @@ // being suitable for being dynamically loaded via dlopen. GOT[e0] is the // module index, with a special value of 0 for the current module. GOT[e1] is // unused. There only needs to be one module index entry. - if (oneof( - expr)) { + if (oneof(expr)) { // Local-Dynamic relocs can be relaxed to Local-Exec. if (toExecRelax) { c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_LD_TO_LE), type, @@ -1287,7 +1292,8 @@ } if (oneof(expr)) { + R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC, + R_LOONGARCH_TLSGD_PAGE_PC>(expr)) { if (!toExecRelax) { sym.setFlags(NEEDS_TLSGD); c.addReloc({expr, type, offset, addend, &sym}); @@ -1307,8 +1313,8 @@ return target->getTlsGdRelaxSkip(type); } - if (oneof(expr)) { + if (oneof(expr)) { ctx.hasTlsIe.store(true, std::memory_order_relaxed); // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally // defined. diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -438,6 +438,8 @@ .Case("elf64-littleriscv", {ELF64LEKind, EM_RISCV}) .Case("elf64-sparc", {ELF64BEKind, EM_SPARCV9}) .Case("elf32-msp430", {ELF32LEKind, EM_MSP430}) + .Case("elf32-loongarch", {ELF32LEKind, EM_LOONGARCH}) + .Case("elf64-loongarch", {ELF64LEKind, EM_LOONGARCH}) .Default({ELFNoneKind, EM_NONE}); } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -172,6 +172,7 @@ TargetInfo *getARMTargetInfo(); TargetInfo *getAVRTargetInfo(); TargetInfo *getHexagonTargetInfo(); +TargetInfo *getLoongArchTargetInfo(); TargetInfo *getMSP430TargetInfo(); TargetInfo *getPPC64TargetInfo(); TargetInfo *getPPCTargetInfo(); @@ -215,6 +216,7 @@ void addPPC64SaveRestore(); uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); +uint64_t getLoongArchPageOffset(uint64_t dest, uint64_t pc); void riscvFinalizeRelax(int passes); LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target; diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -62,6 +62,8 @@ return getAVRTargetInfo(); case EM_HEXAGON: return getHexagonTargetInfo(); + case EM_LOONGARCH: + return getLoongArchTargetInfo(); case EM_MIPS: switch (config->ekind) { case ELF32LEKind: diff --git a/lld/test/ELF/loongarch-elf-flags.s b/lld/test/ELF/loongarch-elf-flags.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/loongarch-elf-flags.s @@ -0,0 +1,8 @@ +# REQUIRES: loongarch + +# RUN: echo -n "BLOB" > %t.binary +# RUN: ld.lld -m elf64loongarch -b binary %t.binary -o %t.out +# RUN: llvm-readobj -h %t.out | FileCheck %s + +# CHECK: Flags [ +# CHECK-NEXT: ] diff --git a/lld/test/ELF/loongarch-relocs-abs64.s b/lld/test/ELF/loongarch-relocs-abs64.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/loongarch-relocs-abs64.s @@ -0,0 +1,60 @@ +# REQUIRES: loongarch + +# RUN: llvm-mc -filetype=obj -triple=loongarch64-unknown-elf %s -o %t.la64.o + +# RUN: ld.lld %t.la64.o --defsym foo=0 --defsym bar=42 -o %t.la64.1 +# RUN: llvm-objdump -d %t.la64.1 | FileCheck --check-prefix=CASE1 %s +# CASE1: 04 00 00 14 lu12i.w $a0, 0 +# CASE1-NEXT: 84 00 80 03 ori $a0, $a0, 0 +# CASE1-NEXT: 04 00 00 16 lu32i.d $a0, 0 +# CASE1-NEXT: 84 00 00 03 lu52i.d $a0, $a0, 0 +# CASE1-NEXT: 05 00 00 14 lu12i.w $a1, 0 +# CASE1-NEXT: a5 a8 80 03 ori $a1, $a1, 42 +# CASE1-NEXT: 05 00 00 16 lu32i.d $a1, 0 +# CASE1-NEXT: a5 00 00 03 lu52i.d $a1, $a1, 0 + +# RUN: ld.lld %t.la64.o --defsym foo=0x12345678 --defsym bar=0x87654321 -o %t.la64.2 +# RUN: llvm-objdump -d %t.la64.2 | FileCheck --check-prefix=CASE2 %s +# CASE2: a4 68 24 14 lu12i.w $a0, 74565 +# CASE2-NEXT: 84 e0 99 03 ori $a0, $a0, 1656 +# CASE2-NEXT: 04 00 00 16 lu32i.d $a0, 0 +# CASE2-NEXT: 84 00 00 03 lu52i.d $a0, $a0, 0 +# CASE2-NEXT: 85 ca 0e 15 lu12i.w $a1, -493996 +# CASE2-NEXT: a5 84 8c 03 ori $a1, $a1, 801 +# CASE2-NEXT: 05 00 00 16 lu32i.d $a1, 0 +# CASE2-NEXT: a5 00 00 03 lu52i.d $a1, $a1, 0 + +# RUN: ld.lld %t.la64.o --defsym foo=0x12345fedcb678 --defsym bar=0xfedcb12345000 -o %t.la64.3 +# RUN: llvm-objdump -d %t.la64.3 | FileCheck --check-prefix=CASE3 %s +# CASE3: 64 b9 fd 15 lu12i.w $a0, -4661 +# CASE3-NEXT: 84 e0 99 03 ori $a0, $a0, 1656 +# CASE3-NEXT: a4 68 24 16 lu32i.d $a0, 74565 +# CASE3-NEXT: 84 00 00 03 lu52i.d $a0, $a0, 0 +# CASE3-NEXT: a5 68 24 14 lu12i.w $a1, 74565 +# CASE3-NEXT: a5 00 80 03 ori $a1, $a1, 0 +# CASE3-NEXT: 65 b9 fd 17 lu32i.d $a1, -4661 +# CASE3-NEXT: a5 00 00 03 lu52i.d $a1, $a1, 0 + +# RUN: ld.lld %t.la64.o --defsym foo=0xfffffeeeeeddd --defsym bar=0xfff00000f1111222 -o %t.la64.4 +# RUN: llvm-objdump -d %t.la64.4 | FileCheck --check-prefix=CASE4 %s +# CASE4: c4 dd dd 15 lu12i.w $a0, -69906 +# CASE4-NEXT: 84 74 b7 03 ori $a0, $a0, 3549 +# CASE4-NEXT: e4 ff ff 17 lu32i.d $a0, -1 +# CASE4-NEXT: 84 00 00 03 lu52i.d $a0, $a0, 0 +# CASE4-NEXT: 25 22 e2 15 lu12i.w $a1, -61167 +# CASE4-NEXT: a5 88 88 03 ori $a1, $a1, 546 +# CASE4-NEXT: 05 00 00 16 lu32i.d $a1, 0 +# CASE4-NEXT: a5 fc 3f 03 lu52i.d $a1, $a1, -1 + +.global _start + +_start: + lu12i.w $a0, %abs_hi20(foo) + ori $a0, $a0, %abs_lo12(foo) + lu32i.d $a0, %abs64_lo20(foo) + lu52i.d $a0, $a0, %abs64_hi12(foo) + + lu12i.w $a1, %abs_hi20(bar) + ori $a1, $a1, %abs_lo12(bar) + lu32i.d $a1, %abs64_lo20(bar) + lu52i.d $a1, $a1, %abs64_hi12(bar) diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py --- a/lld/test/lit.cfg.py +++ b/lld/test/lit.cfg.py @@ -68,6 +68,7 @@ 'ARM': 'arm', 'AVR': 'avr', 'Hexagon': 'hexagon', + 'LoongArch': 'loongarch', 'Mips': 'mips', 'MSP430': 'msp430', 'PowerPC': 'ppc',