From cd66a2ac0f63ce807cabb867cad1a5e807e53091 Mon Sep 17 00:00:00 2001 From: chenli Date: Thu, 17 Aug 2023 15:55:52 +0800 Subject: [PATCH] [LoongArch] Backport patches --- 0001-backport-LoongArch-patches.patch | 2512 +++++++++++++++++++++++++ lld.spec | 6 +- 2 files changed, 2517 insertions(+), 1 deletion(-) create mode 100644 0001-backport-LoongArch-patches.patch diff --git a/0001-backport-LoongArch-patches.patch b/0001-backport-LoongArch-patches.patch new file mode 100644 index 0000000..31cae1b --- /dev/null +++ b/0001-backport-LoongArch-patches.patch @@ -0,0 +1,2512 @@ +diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp +new file mode 100644 +index 000000000..04ddb4682 +--- /dev/null ++++ b/lld/ELF/Arch/LoongArch.cpp +@@ -0,0 +1,694 @@ ++//===- 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; ++ ++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 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 ++ ++enum Op { ++ SUB_W = 0x00110000, ++ SUB_D = 0x00118000, ++ BREAK = 0x002a0000, ++ 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_ZERO = 0, ++ R_RA = 1, ++ R_TP = 2, ++ R_T0 = 12, ++ R_T1 = 13, ++ R_T2 = 14, ++ R_T3 = 15, ++}; ++ ++// Mask out the input's lowest 12 bits for use with `pcalau12i`, in sequences ++// like `pcalau12i + addi.[wd]` or `pcalau12i + {ld,st}.*` where the `pcalau12i` ++// produces a PC-relative intermediate value with the lowest 12 bits zeroed (the ++// "page") for the next instruction to add in the "page offset". (`pcalau12i` ++// stands for something like "PC ALigned Add Upper that starts from the 12th ++// bit, Immediate".) ++// ++// 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, and not ++// related to the system or the kernel's actual page size. The sematics happens ++// to match the AArch64 `adrp`, so the concept of "page" is borrowed here. ++static uint64_t getLoongArchPage(uint64_t p) { ++ return p & ~static_cast(0xfff); ++} ++ ++static uint32_t lo12(uint32_t val) { return val & 0xfff; } ++ ++// Calculate the adjusted page delta between dest and PC. ++uint64_t elf::getLoongArchPageDelta(uint64_t dest, uint64_t pc) { ++ // Consider the large code model access pattern, of which the smaller code ++ // models' access patterns are a subset: ++ // ++ // pcalau12i U, %foo_hi20(sym) ; b in [-0x80000, 0x7ffff] ++ // addi.d T, zero, %foo_lo12(sym) ; a in [-0x800, 0x7ff] ++ // lu32i.d T, %foo64_lo20(sym) ; c in [-0x80000, 0x7ffff] ++ // lu52i.d T, T, %foo64_hi12(sym) ; d in [-0x800, 0x7ff] ++ // {ldx,stx,add}.* dest, U, T ++ // ++ // Let page(pc) = 0xRRR'QQQQQ'PPPPP'000 and dest = 0xZZZ'YYYYY'XXXXX'AAA, ++ // with RQ, P, ZY, X and A representing the respective bitfields as unsigned ++ // integers. We have: ++ // ++ // page(dest) = 0xZZZ'YYYYY'XXXXX'000 ++ // - page(pc) = 0xRRR'QQQQQ'PPPPP'000 ++ // ---------------------------------- ++ // 0xddd'ccccc'bbbbb'000 ++ // ++ // Now consider the above pattern's actual effects: ++ // ++ // page(pc) 0xRRR'QQQQQ'PPPPP'000 ++ // pcalau12i + 0xiii'iiiii'bbbbb'000 ++ // addi + 0xjjj'jjjjj'kkkkk'AAA ++ // lu32i.d & lu52i.d + 0xddd'ccccc'00000'000 ++ // -------------------------------------------------- ++ // dest = U + T ++ // = ((RQ<<32) + (P<<12) + i + (b<<12)) + (j + k + A + (cd<<32)) ++ // = (((RQ+cd)<<32) + i + j) + (((P+b)<<12) + k) + A ++ // = (ZY<<32) + (X<<12) + A ++ // ++ // ZY<<32 = (RQ<<32)+(cd<<32)+i+j, X<<12 = (P<<12)+(b<<12)+k ++ // cd<<32 = (ZY<<32)-(RQ<<32)-i-j, b<<12 = (X<<12)-(P<<12)-k ++ // ++ // where i and k are terms representing the effect of b's and A's sign ++ // extension respectively. ++ // ++ // i = signed b < 0 ? -0x10000'0000 : 0 ++ // k = signed A < 0 ? -0x1000 : 0 ++ // ++ // The j term is a bit complex: it represents the higher half of ++ // sign-extended bits from A that are effectively lost if i == 0 but k != 0, ++ // due to overwriting by lu32i.d & lu52i.d. ++ // ++ // j = signed A < 0 && signed b >= 0 ? 0x10000'0000 : 0 ++ // ++ // The actual effect of the instruction sequence before the final addition, ++ // i.e. our desired result value, is thus: ++ // ++ // result = (cd<<32) + (b<<12) ++ // = (ZY<<32)-(RQ<<32)-i-j + (X<<12)-(P<<12)-k ++ // = ((ZY<<32)+(X<<12)) - ((RQ<<32)+(P<<12)) - i - j - k ++ // = page(dest) - page(pc) - i - j - k ++ // ++ // when signed A >= 0 && signed b >= 0: ++ // ++ // i = j = k = 0 ++ // result = page(dest) - page(pc) ++ // ++ // when signed A >= 0 && signed b < 0: ++ // ++ // i = -0x10000'0000, j = k = 0 ++ // result = page(dest) - page(pc) + 0x10000'0000 ++ // ++ // when signed A < 0 && signed b >= 0: ++ // ++ // i = 0, j = 0x10000'0000, k = -0x1000 ++ // result = page(dest) - page(pc) - 0x10000'0000 + 0x1000 ++ // ++ // when signed A < 0 && signed b < 0: ++ // ++ // i = -0x10000'0000, j = 0, k = -0x1000 ++ // result = page(dest) - page(pc) + 0x1000 ++ uint64_t result = getLoongArchPage(dest) - getLoongArchPage(pc); ++ bool negativeA = lo12(dest) > 0x7ff; ++ bool negativeB = (result & 0x8000'0000) != 0; ++ ++ if (negativeA) ++ result += 0x1000; ++ if (negativeA && !negativeB) ++ result -= 0x10000'0000; ++ else if (!negativeA && negativeB) ++ result += 0x10000'0000; ++ ++ return result; ++} ++ ++static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } ++ ++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); ++} ++ ++static bool isJirl(uint32_t insn) { ++ return (insn & 0xfc000000) == JIRL; ++} ++ ++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.4. The most widespread page size in use, ++ // though, is 16KiB. ++ defaultCommonPageSize = 16384; ++ defaultMaxPageSize = 65536; ++ write32le(trapInstr.data(), BREAK); // 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.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map ++ gotPltHeaderEntriesNum = 2; ++ ++ pltHeaderSize = 32; ++ pltEntrySize = 16; ++ ipltEntrySize = 16; ++} ++ ++static uint32_t getEFlags(const InputFile *f) { ++ if (config->is64) ++ return cast>(f)->getObj().getHeader().e_flags; ++ return cast>(f)->getObj().getHeader().e_flags; ++} ++ ++static bool inputFileHasCode(const InputFile *f) { ++ for (const auto *sec : f->getSections()) ++ if (sec && sec->flags & SHF_EXECINSTR) ++ return true; ++ ++ return false; ++} ++ ++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 = 0; ++ const InputFile *targetFile; ++ for (const InputFile *f : ctx.objectFiles) { ++ // Do not enforce ABI compatibility if the input file does not contain code. ++ // This is useful for allowing linkage with data-only object files produced ++ // with tools like objcopy, that have zero e_flags. ++ if (!inputFileHasCode(f)) ++ continue; ++ ++ // Take the first non-zero e_flags as the reference. ++ uint32_t flags = getEFlags(f); ++ if (target == 0 && flags != 0) { ++ target = flags; ++ targetFile = f; ++ } ++ ++ if ((flags & EF_LOONGARCH_ABI_MODIFIER_MASK) != ++ (target & EF_LOONGARCH_ABI_MODIFIER_MASK)) ++ error(toString(f) + ++ ": cannot link object files with different ABI from " + ++ toString(targetFile)); ++ ++ // We cannot process psABI v1.x / 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 systems were built before the new ABI 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 ((flags & 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::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.00. ++ // ++ // The `pcrel_{hi20,lo12}` operators are illustrative only and not really ++ // supported by LoongArch assemblers. ++ // ++ // pcaddu12i $t2, %pcrel_hi20(.got.plt) ++ // sub.[wd] $t1, $t1, $t3 ++ // ld.[wd] $t3, $t2, %pcrel_lo12(.got.plt) ; t3 = _dl_runtime_resolve ++ // addi.[wd] $t1, $t1, -pltHeaderSize-12 ; t1 = &.plt[i] - &.plt[0] ++ // addi.[wd] $t0, $t2, %pcrel_lo12(.got.plt) ++ // srli.[wd] $t1, $t1, (is64?1:2) ; t1 = &.got.plt[i] - &.got.plt[0] ++ // ld.[wd] $t0, $t0, Wordsize ; 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, R_ZERO, 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. ++ // ++ // pcaddu12i $t3, %pcrel_hi20(f@.got.plt) ++ // ld.[wd] $t3, $t3, %pcrel_lo12(f@.got.plt) ++ // 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, R_ZERO, R_ZERO, 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: ++ return R_ABS; ++ case R_LARCH_PCALA_LO12: ++ // We could just R_ABS, but the JIRL instruction reuses the relocation type ++ // for a different purpose. The questionable usage is part of glibc 2.37 ++ // libc_nonshared.a [1], which is linked into user programs, so we have to ++ // work around it for a while, even if a new relocation type may be ++ // introduced in the future [2]. ++ // ++ // [1]: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=9f482b73f41a9a1bbfb173aad0733d1c824c788a ++ // [2]: https://github.com/loongson/la-abi-specs/pull/3 ++ return isJirl(read32le(loc)) ? R_PLT : 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 behave like the RISCV counterparts; reuse ++ // the RelExpr to avoid code duplication. ++ return R_RISCV_ADD; ++ case R_LARCH_32_PCREL: ++ case R_LARCH_64_PCREL: ++ case R_LARCH_PCREL20_S2: ++ 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: ++ 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_GOT_PC_LO12: ++ case R_LARCH_TLS_IE_PC_LO12: ++ return R_LOONGARCH_GOT; ++ case R_LARCH_TLS_LD_PC_HI20: ++ case R_LARCH_TLS_GD_PC_HI20: ++ return R_LOONGARCH_TLSGD_PAGE_PC; ++ case R_LARCH_PCALA_HI20: ++ // Why not R_LOONGARCH_PAGE_PC, majority of references don't go through PLT ++ // anyway so why waste time checking only to get everything relaxed back to ++ // it? ++ // ++ // This is again due to the R_LARCH_PCALA_LO12 on JIRL case, where we want ++ // both the HI20 and LO12 to potentially refer to the PLT. But in reality ++ // the HI20 reloc appears earlier, and the relocs don't contain enough ++ // information to let us properly resolve semantics per symbol. ++ // Unlike RISCV, our LO12 relocs *do not* point to their corresponding HI20 ++ // relocs, hence it is nearly impossible to 100% accurately determine each ++ // HI20's "flavor" without taking big performance hits, in the presence of ++ // edge cases (e.g. HI20 without pairing LO12; paired LO12 placed so far ++ // apart that relationship is not certain anymore), and programmer mistakes ++ // (e.g. as outlined in https://github.com/loongson/la-abi-specs/pull/3). ++ // ++ // Ideally we would scan in an extra pass for all LO12s on JIRL, then mark ++ // every HI20 reloc referring to the same symbol differently; this is not ++ // feasible with the current function signature of getRelExpr that doesn't ++ // allow for such inter-pass state. ++ // ++ // So, unfortunately we have to again workaround this quirk the same way as ++ // BFD: assuming every R_LARCH_PCALA_HI20 is potentially PLT-needing, only ++ // relaxing back to R_LOONGARCH_PAGE_PC if it's known not so at a later ++ // stage. ++ return R_LOONGARCH_PLT_PAGE_PC; ++ case R_LARCH_PCALA64_LO20: ++ case R_LARCH_PCALA64_HI12: ++ return R_LOONGARCH_PAGE_PC; ++ 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 implemented yet. ++ return R_NONE; ++ ++ // Other known relocs that are explicitly unimplemented: ++ // ++ // - psABI v1 relocs that need a stateful stack machine to work, and not ++ // required when implementing psABI v2; ++ // - relocs that are not used anywhere (R_LARCH_{ADD,SUB}_24 [1], and the ++ // two GNU vtable-related relocs). ++ // ++ // [1]: https://web.archive.org/web/20230709064026/https://github.com/loongson/LoongArch-Documentation/issues/51 ++ 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: ++ case R_LARCH_TLS_DTPREL32: ++ write32le(loc, val); ++ return; ++ case R_LARCH_64: ++ case R_LARCH_TLS_DTPREL64: ++ case R_LARCH_64_PCREL: ++ write64le(loc, val); ++ return; ++ ++ case R_LARCH_PCREL20_S2: ++ checkInt(loc, val, 22, rel); ++ checkAlignment(loc, val, 4, rel); ++ write32le(loc, setJ20(read32le(loc), val >> 2)); ++ 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_PCALA_LO12: ++ // We have to again inspect the insn word to handle the R_LARCH_PCALA_LO12 ++ // on JIRL case: firstly JIRL wants its immediate's 2 lowest zeroes ++ // removed by us (in contrast to regular R_LARCH_PCALA_LO12), secondly ++ // its immediate slot width is different too (16, not 12). ++ // In this case, process like an R_LARCH_B16, but without overflow checking ++ // and only taking the value's lowest 12 bits. ++ if (isJirl(read32le(loc))) { ++ checkAlignment(loc, val, 4, rel); ++ val = SignExtend64<12>(val); ++ write32le(loc, setK16(read32le(loc), val >> 2)); ++ return; ++ } ++ [[fallthrough]]; ++ case R_LARCH_ABS_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_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 +index 8e6a746d2..89955db67 100644 +--- a/lld/ELF/CMakeLists.txt ++++ b/lld/ELF/CMakeLists.txt +@@ -25,6 +25,7 @@ add_lld_library(lldELF + 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 +index 7e2a72acf..3c1803be6 100644 +--- a/lld/ELF/Driver.cpp ++++ b/lld/ELF/Driver.cpp +@@ -167,6 +167,7 @@ static std::tuple parseEmulation(StringRef emul) { + .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}) +@@ -178,6 +179,7 @@ static std::tuple parseEmulation(StringRef emul) { + .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) +@@ -1032,8 +1034,9 @@ static bool getIsRela(opt::InputArgList &args) { + + // 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) { +@@ -1570,8 +1573,9 @@ static void setConfigs(opt::InputArgList &args) { + // 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/InputFiles.cpp b/lld/ELF/InputFiles.cpp +index 7dacdeb9f..c43b4afd6 100644 +--- a/lld/ELF/InputFiles.cpp ++++ b/lld/ELF/InputFiles.cpp +@@ -1534,6 +1534,9 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) { + return EM_AVR; + case Triple::hexagon: + return EM_HEXAGON; ++ case Triple::loongarch32: ++ case Triple::loongarch64: ++ return EM_LOONGARCH; + case Triple::mips: + case Triple::mipsel: + case Triple::mips64: +diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp +index df24f998b..d56291cbd 100644 +--- a/lld/ELF/InputSection.cpp ++++ b/lld/ELF/InputSection.cpp +@@ -609,6 +609,7 @@ static int64_t getTlsTpOffset(const Symbol &s) { + // 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 +644,14 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, + case R_GOT: + case R_RELAX_TLS_GD_TO_IE_ABS: + return sym.getGotVA() + a; ++ case R_LOONGARCH_GOT: ++ // The LoongArch TLS GD relocs reuse the R_LARCH_GOT_PC_LO12 reloc type ++ // for their page offsets. The arithmetics are different in the TLS case ++ // so we have to duplicate some logic here. ++ if (sym.hasFlag(NEEDS_TLSGD) && type != R_LARCH_TLS_IE_PC_LO12) ++ // Like R_LOONGARCH_TLSGD_PAGE_PC but taking the absolute value. ++ return in.got->getGlobalDynAddr(sym) + a; ++ return getRelocTargetVA(file, type, a, p, sym, R_GOT); + case R_GOTONLY_PC: + return in.got->getVA() + a - p; + case R_GOTPLTONLY_PC: +@@ -667,6 +676,10 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, + case R_GOT_PC: + case R_RELAX_TLS_GD_TO_IE: + return sym.getGotVA() + a - p; ++ case R_LOONGARCH_GOT_PAGE_PC: ++ if (sym.hasFlag(NEEDS_TLSGD)) ++ return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p); ++ return getLoongArchPageDelta(sym.getGotVA() + a, p); + case R_MIPS_GOTREL: + return sym.getVA(a) - in.mipsGot->getGp(file); + case R_MIPS_GOT_GP: +@@ -715,6 +728,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, + *hiRel->sym, hiRel->expr); + return 0; + } ++ case R_LOONGARCH_PAGE_PC: ++ return getLoongArchPageDelta(sym.getVA(a), p); + case R_PC: + case R_ARM_PCA: { + uint64_t dest; +@@ -748,6 +763,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, + case R_PLT_PC: + case R_PPC64_CALL_PLT: + return sym.getPltVA() + a - p; ++ case R_LOONGARCH_PLT_PAGE_PC: ++ return getLoongArchPageDelta(sym.getPltVA() + a, p); + case R_PLT_GOTPLT: + return sym.getPltVA() + a - in.gotPlt->getVA(); + case R_PPC32_PLTREL: +@@ -808,6 +825,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, + 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 getLoongArchPageDelta(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.cpp b/lld/ELF/Relocations.cpp +index aeba91829..b43c05255 100644 +--- a/lld/ELF/Relocations.cpp ++++ b/lld/ELF/Relocations.cpp +@@ -190,8 +190,8 @@ static bool isAbsoluteValue(const Symbol &sym) { + + // Returns true if Expr refers a PLT entry. + static bool needsPlt(RelExpr expr) { +- return oneof( +- expr); ++ return oneof(expr); + } + + // Returns true if Expr refers a GOT entry. Note that this function +@@ -200,7 +200,8 @@ static bool needsPlt(RelExpr expr) { + 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,12 +209,14 @@ static bool needsGot(RelExpr expr) { + static bool isRelExpr(RelExpr expr) { + return oneof(expr); ++ R_RISCV_PC_INDIRECT, R_PPC64_RELAX_GOT_PC, R_LOONGARCH_PAGE_PC>( ++ expr); + } + +- + static RelExpr toPlt(RelExpr expr) { + switch (expr) { ++ case R_LOONGARCH_PAGE_PC: ++ return R_LOONGARCH_PLT_PAGE_PC; + case R_PPC64_CALL: + return R_PPC64_CALL_PLT; + case R_PC: +@@ -232,6 +235,8 @@ static RelExpr fromPlt(RelExpr expr) { + case R_PLT_PC: + case R_PPC32_PLTREL: + return R_PC; ++ case R_LOONGARCH_PLT_PAGE_PC: ++ return R_LOONGARCH_PAGE_PC; + case R_PPC64_CALL_PLT: + return R_PPC64_CALL; + case R_PLT: +@@ -946,7 +951,9 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, + 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_PLT_PAGE_PC, R_LOONGARCH_GOT, R_LOONGARCH_GOT_PAGE_PC>( ++ e)) + return true; + + // These never do, except if the entire file is position dependent or if +@@ -1050,7 +1057,9 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, + // for detailed description: + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + in.mipsGot->addEntry(*sec->file, sym, addend, expr); +- } else { ++ } else if (!sym.isTls() || config->emachine != EM_LOONGARCH) { ++ // Many LoongArch TLS relocs reuse the R_LOONGARCH_GOT type, in which ++ // case the NEEDS_GOT flag shouldn't get set. + sym.setFlags(NEEDS_GOT); + } + } else if (needsPlt(expr)) { +@@ -1090,7 +1099,8 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, + (isa(sec) && config->emachine != EM_MIPS)); + 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) { +@@ -1242,11 +1252,13 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym, + 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; + +@@ -1263,8 +1275,7 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym, + // 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, +@@ -1295,7 +1306,8 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym, + } + + 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}); +@@ -1315,8 +1327,8 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym, + 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/Relocations.h b/lld/ELF/Relocations.h +index 29e3edeca..e36215bd0 100644 +--- a/lld/ELF/Relocations.h ++++ b/lld/ELF/Relocations.h +@@ -102,6 +102,15 @@ enum RelExpr { + R_PPC64_RELAX_GOT_PC, + R_RISCV_ADD, + R_RISCV_PC_INDIRECT, ++ // Same as R_PC but with page-aligned semantics. ++ R_LOONGARCH_PAGE_PC, ++ // Same as R_PLT_PC but with page-aligned semantics. ++ R_LOONGARCH_PLT_PAGE_PC, ++ // In addition to having page-aligned semantics, LoongArch GOT relocs are ++ // also reused for TLS, making the semantics differ from other architectures. ++ R_LOONGARCH_GOT, ++ R_LOONGARCH_GOT_PAGE_PC, ++ R_LOONGARCH_TLSGD_PAGE_PC, + }; + + // Architecture-neutral representation of relocation. +diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp +index bb09bde5d..c7b107515 100644 +--- a/lld/ELF/ScriptParser.cpp ++++ b/lld/ELF/ScriptParser.cpp +@@ -438,6 +438,8 @@ static std::pair parseBfdName(StringRef s) { + .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.cpp b/lld/ELF/Target.cpp +index 3873c7a25..32bb2164a 100644 +--- a/lld/ELF/Target.cpp ++++ b/lld/ELF/Target.cpp +@@ -62,6 +62,8 @@ TargetInfo *elf::getTarget() { + return getAVRTargetInfo(); + case EM_HEXAGON: + return getHexagonTargetInfo(); ++ case EM_LOONGARCH: ++ return getLoongArchTargetInfo(); + case EM_MIPS: + switch (config->ekind) { + case ELF32LEKind: +diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h +index e6a781690..d1a4fb195 100644 +--- a/lld/ELF/Target.h ++++ b/lld/ELF/Target.h +@@ -172,6 +172,7 @@ TargetInfo *getAMDGPUTargetInfo(); + TargetInfo *getARMTargetInfo(); + TargetInfo *getAVRTargetInfo(); + TargetInfo *getHexagonTargetInfo(); ++TargetInfo *getLoongArchTargetInfo(); + TargetInfo *getMSP430TargetInfo(); + TargetInfo *getPPC64TargetInfo(); + TargetInfo *getPPCTargetInfo(); +@@ -215,6 +216,7 @@ void writePrefixedInstruction(uint8_t *loc, uint64_t insn); + void addPPC64SaveRestore(); + uint64_t getPPC64TocBase(); + uint64_t getAArch64Page(uint64_t expr); ++uint64_t getLoongArchPageDelta(uint64_t dest, uint64_t pc); + void riscvFinalizeRelax(int passes); + void mergeRISCVAttributesSections(); + +diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst +index a450923cd..d91041f03 100644 +--- a/lld/docs/ReleaseNotes.rst ++++ b/lld/docs/ReleaseNotes.rst +@@ -56,6 +56,7 @@ ELF Improvements + (`D138550 `_) + * For x86-32, ``-fno-plt`` GD/LD TLS models ``call *___tls_get_addr@GOT(%reg)`` + are now supported. Previous output might have runtime crash. ++* LoongArch is now supported. + * Armv4(T) thunks are now supported. + (`D139888 `_) + (`D141272 `_) +diff --git a/lld/docs/index.rst b/lld/docs/index.rst +index ce6320333..37e96c14f 100644 +--- a/lld/docs/index.rst ++++ b/lld/docs/index.rst +@@ -22,10 +22,10 @@ Features + machine, you can expect that LLD runs more than twice as fast as the GNU + gold linker. Your mileage may vary, though. + +-- It supports various CPUs/ABIs including AArch64, AMDGPU, ARM, Hexagon, MIPS +- 32/64 big/little-endian, PowerPC, PowerPC64, RISC-V, SPARC V9, x86-32 and +- x86-64. Among these, AArch64, ARM (>= v6), PowerPC, PowerPC64, x86-32 and +- x86-64 have production quality. MIPS seems decent too. ++- It supports various CPUs/ABIs including AArch64, AMDGPU, ARM, Hexagon, ++ LoongArch, MIPS 32/64 big/little-endian, PowerPC, PowerPC64, RISC-V, SPARC V9, ++ x86-32 and x86-64. Among these, AArch64, ARM (>= v6), LoongArch, PowerPC, ++ PowerPC64, x86-32 and x86-64 have production quality. MIPS seems decent too. + + - It is always a cross-linker, meaning that it always supports all the + above targets however it was built. In fact, we don't provide a +diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 +index edeb7c4bf..4889d04b9 100644 +--- a/lld/docs/ld.lld.1 ++++ b/lld/docs/ld.lld.1 +@@ -4,7 +4,7 @@ + .\" + .\" This man page documents only lld's ELF linking support, obtained originally + .\" from FreeBSD. +-.Dd May 12, 2019 ++.Dd Jul 25, 2023 + .Dt LD.LLD 1 + .Os + .Sh NAME +@@ -27,8 +27,8 @@ It accepts most of the same command line arguments and linker scripts + as GNU linkers. + .Pp + .Nm +-currently supports i386, x86-64, ARM, AArch64, PowerPC32, PowerPC64, +-MIPS32, MIPS64, RISC-V, AMDGPU, Hexagon and SPARC V9 targets. ++currently supports i386, x86-64, ARM, AArch64, LoongArch, PowerPC32, ++PowerPC64, MIPS32, MIPS64, RISC-V, AMDGPU, Hexagon and SPARC V9 targets. + .Nm + acts as a Microsoft link.exe-compatible linker if invoked as + .Nm lld-link +diff --git a/lld/test/ELF/emulation-loongarch.s b/lld/test/ELF/emulation-loongarch.s +new file mode 100644 +index 000000000..343e83627 +--- /dev/null ++++ b/lld/test/ELF/emulation-loongarch.s +@@ -0,0 +1,78 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch32 %s -o %t.o ++# RUN: ld.lld %t.o -o %t ++# RUN: llvm-readobj --file-headers %t | FileCheck --check-prefix=LA32 %s ++# RUN: ld.lld -m elf32loongarch %t.o -o %t ++# RUN: llvm-readobj --file-headers %t | FileCheck --check-prefix=LA32 %s ++# RUN: echo 'OUTPUT_FORMAT(elf32-loongarch)' > %t.script ++# RUN: ld.lld %t.script %t.o -o %t ++# RUN: llvm-readobj --file-headers %t | FileCheck --check-prefix=LA32 %s ++ ++# LA32: ElfHeader { ++# LA32-NEXT: Ident { ++# LA32-NEXT: Magic: (7F 45 4C 46) ++# LA32-NEXT: Class: 32-bit (0x1) ++# LA32-NEXT: DataEncoding: LittleEndian (0x1) ++# LA32-NEXT: FileVersion: 1 ++# LA32-NEXT: OS/ABI: SystemV (0x0) ++# LA32-NEXT: ABIVersion: 0 ++# LA32-NEXT: Unused: (00 00 00 00 00 00 00) ++# LA32-NEXT: } ++# LA32-NEXT: Type: Executable (0x2) ++# LA32-NEXT: Machine: EM_LOONGARCH (0x102) ++# LA32-NEXT: Version: 1 ++# LA32-NEXT: Entry: ++# LA32-NEXT: ProgramHeaderOffset: 0x34 ++# LA32-NEXT: SectionHeaderOffset: ++# LA32-NEXT: Flags [ (0x43) ++# LA32-NEXT: EF_LOONGARCH_ABI_DOUBLE_FLOAT (0x3) ++# LA32-NEXT: EF_LOONGARCH_OBJABI_V1 (0x40) ++# LA32-NEXT: ] ++# LA32-NEXT: HeaderSize: 52 ++# LA32-NEXT: ProgramHeaderEntrySize: 32 ++# LA32-NEXT: ProgramHeaderCount: ++# LA32-NEXT: SectionHeaderEntrySize: 40 ++# LA32-NEXT: SectionHeaderCount: ++# LA32-NEXT: StringTableSectionIndex: ++# LA32-NEXT: } ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t.o ++# RUN: ld.lld %t.o -o %t ++# RUN: llvm-readobj --file-headers %t | FileCheck --check-prefix=LA64 %s ++# RUN: ld.lld -m elf64loongarch %t.o -o %t ++# RUN: llvm-readobj --file-headers %t | FileCheck --check-prefix=LA64 %s ++# RUN: echo 'OUTPUT_FORMAT(elf64-loongarch)' > %t.script ++# RUN: ld.lld %t.script %t.o -o %t ++# RUN: llvm-readobj --file-headers %t | FileCheck --check-prefix=LA64 %s ++ ++# LA64: ElfHeader { ++# LA64-NEXT: Ident { ++# LA64-NEXT: Magic: (7F 45 4C 46) ++# LA64-NEXT: Class: 64-bit (0x2) ++# LA64-NEXT: DataEncoding: LittleEndian (0x1) ++# LA64-NEXT: FileVersion: 1 ++# LA64-NEXT: OS/ABI: SystemV (0x0) ++# LA64-NEXT: ABIVersion: 0 ++# LA64-NEXT: Unused: (00 00 00 00 00 00 00) ++# LA64-NEXT: } ++# LA64-NEXT: Type: Executable (0x2) ++# LA64-NEXT: Machine: EM_LOONGARCH (0x102) ++# LA64-NEXT: Version: 1 ++# LA64-NEXT: Entry: ++# LA64-NEXT: ProgramHeaderOffset: 0x40 ++# LA64-NEXT: SectionHeaderOffset: ++# LA64-NEXT: Flags [ (0x43) ++# LA64-NEXT: EF_LOONGARCH_ABI_DOUBLE_FLOAT (0x3) ++# LA64-NEXT: EF_LOONGARCH_OBJABI_V1 (0x40) ++# LA64-NEXT: ] ++# LA64-NEXT: HeaderSize: 64 ++# LA64-NEXT: ProgramHeaderEntrySize: 56 ++# LA64-NEXT: ProgramHeaderCount: ++# LA64-NEXT: SectionHeaderEntrySize: 64 ++# LA64-NEXT: SectionHeaderCount: ++# LA64-NEXT: StringTableSectionIndex: ++# LA64-NEXT: } ++ ++.globl _start ++_start: +diff --git a/lld/test/ELF/loongarch-abs64.s b/lld/test/ELF/loongarch-abs64.s +new file mode 100644 +index 000000000..4bfe7df91 +--- /dev/null ++++ b/lld/test/ELF/loongarch-abs64.s +@@ -0,0 +1,64 @@ ++# 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 --no-show-raw-insn -d %t.la64.1 | FileCheck --check-prefix=CASE1 %s ++# CASE1: lu12i.w $a0, 0 ++# CASE1-NEXT: ori $a0, $a0, 0 ++# CASE1-NEXT: lu32i.d $a0, 0 ++# CASE1-NEXT: lu52i.d $a0, $a0, 0 ++# CASE1-NEXT: lu12i.w $a1, 0 ++# CASE1-NEXT: ori $a1, $a1, 42 ++# CASE1-NEXT: lu32i.d $a1, 0 ++# CASE1-NEXT: lu52i.d $a1, $a1, 0 ++ ++# RUN: ld.lld %t.la64.o --defsym foo=0x12345678 --defsym bar=0x87654321 -o %t.la64.2 ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la64.2 | FileCheck --check-prefix=CASE2 %s ++# CASE2: lu12i.w $a0, 74565 ++# CASE2-NEXT: ori $a0, $a0, 1656 ++# CASE2-NEXT: lu32i.d $a0, 0 ++# CASE2-NEXT: lu52i.d $a0, $a0, 0 ++# CASE2-NEXT: lu12i.w $a1, -493996 ++# CASE2-NEXT: ori $a1, $a1, 801 ++# CASE2-NEXT: lu32i.d $a1, 0 ++# CASE2-NEXT: lu52i.d $a1, $a1, 0 ++ ++# RUN: ld.lld %t.la64.o --defsym foo=0x12345fedcb678 --defsym bar=0xfedcb12345000 -o %t.la64.3 ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la64.3 | FileCheck --check-prefix=CASE3 %s ++# CASE3: lu12i.w $a0, -4661 ++# CASE3-NEXT: ori $a0, $a0, 1656 ++# CASE3-NEXT: lu32i.d $a0, 74565 ++# CASE3-NEXT: lu52i.d $a0, $a0, 0 ++# CASE3-NEXT: lu12i.w $a1, 74565 ++# CASE3-NEXT: ori $a1, $a1, 0 ++# CASE3-NEXT: lu32i.d $a1, -4661 ++# CASE3-NEXT: lu52i.d $a1, $a1, 0 ++ ++# RUN: ld.lld %t.la64.o --defsym foo=0xfffffeeeeeddd --defsym bar=0xfff00000f1111222 -o %t.la64.4 ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la64.4 | FileCheck --check-prefix=CASE4 %s ++# CASE4: lu12i.w $a0, -69906 ++# CASE4-NEXT: ori $a0, $a0, 3549 ++# CASE4-NEXT: lu32i.d $a0, -1 ++# CASE4-NEXT: lu52i.d $a0, $a0, 0 ++# CASE4-NEXT: lu12i.w $a1, -61167 ++# CASE4-NEXT: ori $a1, $a1, 546 ++# CASE4-NEXT: lu32i.d $a1, 0 ++# CASE4-NEXT: lu52i.d $a1, $a1, -1 ++ ++.global _start ++ ++_start: ++1: ++ lu12i.w $a0, %abs_hi20(foo) ++.reloc 1b, R_LARCH_MARK_LA, foo ++ ori $a0, $a0, %abs_lo12(foo) ++ lu32i.d $a0, %abs64_lo20(foo) ++ lu52i.d $a0, $a0, %abs64_hi12(foo) ++ ++2: ++ lu12i.w $a1, %abs_hi20(bar) ++.reloc 1b, R_LARCH_MARK_LA, 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/ELF/loongarch-add-sub.s b/lld/test/ELF/loongarch-add-sub.s +new file mode 100644 +index 000000000..63a3f7de1 +--- /dev/null ++++ b/lld/test/ELF/loongarch-add-sub.s +@@ -0,0 +1,36 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-elf %s -o %t.la64.o ++ ++# RUN: ld.lld --section-start=.text=0x1234567890 --section-start=.rodata=0x9876543210 %t.la64.o -o %t.la64 ++# RUN: llvm-readelf -x .rodata %t.la64 | FileCheck --check-prefix=CHECK %s ++# CHECK: section '.rodata': ++# CHECK-NEXT: 0x9876543210 10325476 98badcfe 804602be 79ffffff ++# CHECK-NEXT: 0x9876543220 804602be 804680 ++ ++.text ++.global _start ++_start: ++1: ++ break 0 ++ ++.rodata ++2: ++ .dword 0xfedcba9876543210 ++ ++foo: ++ .dword 0 ++ .reloc foo, R_LARCH_ADD64, 1b ++ .reloc foo, R_LARCH_SUB64, 2b ++bar: ++ .word 0 ++ .reloc bar, R_LARCH_ADD32, 1b ++ .reloc bar, R_LARCH_SUB32, 2b ++baz: ++ .short 0 ++ .reloc baz, R_LARCH_ADD16, 1b ++ .reloc baz, R_LARCH_SUB16, 2b ++quux: ++ .byte 0 ++ .reloc quux, R_LARCH_ADD8, 1b ++ .reloc quux, R_LARCH_SUB8, 2b +diff --git a/lld/test/ELF/loongarch-branch.s b/lld/test/ELF/loongarch-branch.s +new file mode 100644 +index 000000000..0090ae114 +--- /dev/null ++++ b/lld/test/ELF/loongarch-branch.s +@@ -0,0 +1,68 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %s -o %t.la32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-elf %s -o %t.la64.o ++ ++# RUN: ld.lld %t.la32.o --defsym foo16=b16+4 --defsym bar16=b16 --defsym foo21=b21+4 --defsym bar21=b21 --defsym foo26=b26+4 --defsym bar26=b26 -o %t.la32 ++# RUN: ld.lld %t.la64.o --defsym foo16=b16+4 --defsym bar16=b16 --defsym foo21=b21+4 --defsym bar21=b21 --defsym foo26=b26+4 --defsym bar26=b26 -o %t.la64 ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la32 | FileCheck %s --check-prefix=CHECK ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la64 | FileCheck %s --check-prefix=CHECK ++# CHECK: beq $zero, $zero, 4 ++# CHECK: bne $zero, $zero, -4 ++# CHECK: beqz $s8, 4 ++# CHECK: bnez $s8, -4 ++# CHECK: b 4 ++# CHECK: bl -4 ++ ++# RUN: ld.lld %t.la32.o --defsym foo16=b16+0x1fffc --defsym bar16=b16+4-0x20000 --defsym foo21=b21+0x3ffffc --defsym bar21=b21+4-0x400000 --defsym foo26=b26+0x7fffffc --defsym bar26=b26+4-0x8000000 -o %t.la32.limits ++# RUN: ld.lld %t.la64.o --defsym foo16=b16+0x1fffc --defsym bar16=b16+4-0x20000 --defsym foo21=b21+0x3ffffc --defsym bar21=b21+4-0x400000 --defsym foo26=b26+0x7fffffc --defsym bar26=b26+4-0x8000000 -o %t.la64.limits ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la32.limits | FileCheck --check-prefix=LIMITS %s ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la64.limits | FileCheck --check-prefix=LIMITS %s ++# LIMITS: beq $zero, $zero, 131068 ++# LIMITS-NEXT: bne $zero, $zero, -131072 ++# LIMITS: beqz $s8, 4194300 ++# LIMITS-NEXT: bnez $s8, -4194304 ++# LIMITS: b 134217724 ++# LIMITS-NEXT: bl -134217728 ++ ++# RUN: not ld.lld %t.la32.o --defsym foo16=b16+0x20000 --defsym bar16=b16+4-0x20004 --defsym foo21=b21+0x400000 --defsym bar21=b21+4-0x400004 --defsym foo26=b26+0x8000000 --defsym bar26=b26+4-0x8000004 -o /dev/null 2>&1 | FileCheck -DFILE=%t.la32.o --check-prefix=ERROR-RANGE %s ++# RUN: not ld.lld %t.la64.o --defsym foo16=b16+0x20000 --defsym bar16=b16+4-0x20004 --defsym foo21=b21+0x400000 --defsym bar21=b21+4-0x400004 --defsym foo26=b26+0x8000000 --defsym bar26=b26+4-0x8000004 -o /dev/null 2>&1 | FileCheck -DFILE=%t.la64.o --check-prefix=ERROR-RANGE %s ++# ERROR-RANGE: error: [[FILE]]:(.text+0x0): relocation R_LARCH_B16 out of range: 131072 is not in [-131072, 131071]; references foo16 ++# ERROR-RANGE: error: [[FILE]]:(.text+0x4): relocation R_LARCH_B16 out of range: -131076 is not in [-131072, 131071]; references bar16 ++# ERROR-RANGE: error: [[FILE]]:(.text+0x8): relocation R_LARCH_B21 out of range: 4194304 is not in [-4194304, 4194303]; references foo21 ++# ERROR-RANGE: error: [[FILE]]:(.text+0xc): relocation R_LARCH_B21 out of range: -4194308 is not in [-4194304, 4194303]; references bar21 ++# ERROR-RANGE: error: [[FILE]]:(.text+0x10): relocation R_LARCH_B26 out of range: 134217728 is not in [-134217728, 134217727]; references foo26 ++# ERROR-RANGE: error: [[FILE]]:(.text+0x14): relocation R_LARCH_B26 out of range: -134217732 is not in [-134217728, 134217727]; references bar26 ++ ++# RUN: not ld.lld %t.la32.o --defsym foo16=b16+1 --defsym bar16=b16-1 --defsym foo21=b21+1 --defsym bar21=b21-1 --defsym foo26=b26+1 --defsym bar26=b26-1 -o /dev/null 2>&1 | FileCheck -DFILE=%t.la32.o --check-prefix=ERROR-ALIGN-1 %s ++# RUN: not ld.lld %t.la64.o --defsym foo16=b16+1 --defsym bar16=b16-1 --defsym foo21=b21+1 --defsym bar21=b21-1 --defsym foo26=b26+1 --defsym bar26=b26-1 -o /dev/null 2>&1 | FileCheck -DFILE=%t.la64.o --check-prefix=ERROR-ALIGN-1 %s ++# ERROR-ALIGN-1: error: [[FILE]]:(.text+0x0): improper alignment for relocation R_LARCH_B16: 0x1 is not aligned to 4 bytes ++# ERROR-ALIGN-1-NEXT: error: [[FILE]]:(.text+0x4): improper alignment for relocation R_LARCH_B16: 0xFFFFFFFFFFFFFFFB is not aligned to 4 bytes ++# ERROR-ALIGN-1-NEXT: error: [[FILE]]:(.text+0x8): improper alignment for relocation R_LARCH_B21: 0x1 is not aligned to 4 bytes ++# ERROR-ALIGN-1-NEXT: error: [[FILE]]:(.text+0xc): improper alignment for relocation R_LARCH_B21: 0xFFFFFFFFFFFFFFFB is not aligned to 4 bytes ++# ERROR-ALIGN-1-NEXT: error: [[FILE]]:(.text+0x10): improper alignment for relocation R_LARCH_B26: 0x1 is not aligned to 4 bytes ++# ERROR-ALIGN-1-NEXT: error: [[FILE]]:(.text+0x14): improper alignment for relocation R_LARCH_B26: 0xFFFFFFFFFFFFFFFB is not aligned to 4 bytes ++ ++# RUN: not ld.lld %t.la32.o --defsym foo16=b16+2 --defsym bar16=b16-2 --defsym foo21=b21+2 --defsym bar21=b21-2 --defsym foo26=b26+2 --defsym bar26=b26-2 -o /dev/null 2>&1 | FileCheck -DFILE=%t.la32.o --check-prefix=ERROR-ALIGN-2 %s ++# RUN: not ld.lld %t.la64.o --defsym foo16=b16+2 --defsym bar16=b16-2 --defsym foo21=b21+2 --defsym bar21=b21-2 --defsym foo26=b26+2 --defsym bar26=b26-2 -o /dev/null 2>&1 | FileCheck -DFILE=%t.la64.o --check-prefix=ERROR-ALIGN-2 %s ++# ERROR-ALIGN-2: error: [[FILE]]:(.text+0x0): improper alignment for relocation R_LARCH_B16: 0x2 is not aligned to 4 bytes ++# ERROR-ALIGN-2-NEXT: error: [[FILE]]:(.text+0x4): improper alignment for relocation R_LARCH_B16: 0xFFFFFFFFFFFFFFFA is not aligned to 4 bytes ++# ERROR-ALIGN-2-NEXT: error: [[FILE]]:(.text+0x8): improper alignment for relocation R_LARCH_B21: 0x2 is not aligned to 4 bytes ++# ERROR-ALIGN-2-NEXT: error: [[FILE]]:(.text+0xc): improper alignment for relocation R_LARCH_B21: 0xFFFFFFFFFFFFFFFA is not aligned to 4 bytes ++# ERROR-ALIGN-2-NEXT: error: [[FILE]]:(.text+0x10): improper alignment for relocation R_LARCH_B26: 0x2 is not aligned to 4 bytes ++# ERROR-ALIGN-2-NEXT: error: [[FILE]]:(.text+0x14): improper alignment for relocation R_LARCH_B26: 0xFFFFFFFFFFFFFFFA is not aligned to 4 bytes ++ ++.global _start ++.global b16 ++.global b21 ++.global b26 ++_start: ++b16: ++ beq $zero, $zero, foo16 ++ bne $zero, $zero, bar16 ++b21: ++ beqz $s8, foo21 ++ bnez $s8, bar21 ++b26: ++ b foo26 ++ bl bar26 +diff --git a/lld/test/ELF/loongarch-interlink.test b/lld/test/ELF/loongarch-interlink.test +new file mode 100644 +index 000000000..cc6557399 +--- /dev/null ++++ b/lld/test/ELF/loongarch-interlink.test +@@ -0,0 +1,76 @@ ++# REQUIRES: loongarch ++# RUN: rm -rf %t && split-file %s %t ++ ++# RUN: yaml2obj %t/blob.yaml -o %t/blob.o ++# RUN: yaml2obj %t/v0-lp64d.yaml -o %t/v0-lp64d.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-gnu %t/start.s -o %t/v1-lp64d.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-gnu %t/bar.s -o %t/v1-b-lp64d.o ++ ++## Check that binary input results in e_flags=0 output. ++# RUN: ld.lld -m elf64loongarch -b binary %t/blob.bin -o %t/blob.out ++# RUN: llvm-readobj -h %t/blob.out | FileCheck --check-prefix=EMPTY %s ++# EMPTY: Flags [ ++# EMPTY-NEXT: ] ++ ++## Check that interlink between e_flags=0 and normal input (that contain code) ++## is allowed. ++## Also check that the e_flags logic work as intended regardless of input file ++## order. ++# RUN: ld.lld %t/blob.o %t/v1-lp64d.o -o %t/v1-lp64d.out ++# RUN: llvm-readobj -h %t/v1-lp64d.out | FileCheck --check-prefix=V1-LP64D %s ++# V1-LP64D: Flags [ (0x43) ++ ++## Check that interlink between different object ABI versions is disallowed. ++# RUN: not ld.lld %t/v0-lp64d.o %t/v1-b-lp64d.o %t/blob.o -o /dev/null 2>&1 | FileCheck -DFILE=%t/v0-lp64d.o --check-prefix=VERSION-ERR %s ++# VERSION-ERR: error: [[FILE]]: unsupported object file ABI version ++ ++#--- blob.bin ++BLOB ++ ++#--- blob.yaml ++--- !ELF ++FileHeader: ++ Class: ELFCLASS64 ++ Data: ELFDATA2LSB ++ Type: ET_REL ++ Machine: EM_LOONGARCH ++ SectionHeaderStringTable: .strtab ++Sections: ++ - Name: .data ++ Type: SHT_PROGBITS ++ Flags: [ SHF_WRITE, SHF_ALLOC ] ++ AddressAlign: 0x1 ++ Content: 424C4F42 ++Symbols: ++ - Name: blob ++ Section: .data ++ Binding: STB_GLOBAL ++ ++#--- v0-lp64d.yaml ++--- !ELF ++FileHeader: ++ Class: ELFCLASS64 ++ Data: ELFDATA2LSB ++ Type: ET_REL ++ Machine: EM_LOONGARCH ++ Flags: [ EF_LOONGARCH_ABI_DOUBLE_FLOAT ] ++ SectionHeaderStringTable: .strtab ++Sections: ++ - Name: .text ++ Type: SHT_PROGBITS ++ Flags: [ SHF_ALLOC, SHF_EXECINSTR ] ++ AddressAlign: 0x4 ++ Content: 0000a002 ++ ++#--- start.s ++.global _start ++_start: ++ la $a0, blob ++ ld.b $a0, $a0, 0 ++ li.w $a7, 94 ++ syscall 0 ++ ++#--- bar.s ++bar: ++ move $a0, $zero ++ ret +diff --git a/lld/test/ELF/loongarch-pc-aligned.s b/lld/test/ELF/loongarch-pc-aligned.s +new file mode 100644 +index 000000000..9df3492d1 +--- /dev/null ++++ b/lld/test/ELF/loongarch-pc-aligned.s +@@ -0,0 +1,283 @@ ++# REQUIRES: loongarch ++# RUN: rm -rf %t && split-file %s %t ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %t/a.s -o %t/a.la32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/a.s -o %t/a.la64.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/extreme.s -o %t/extreme.o ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x11000 --section-start=.text=0x11ffc -o %t/case1.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x11000 --section-start=.text=0x11ffc -o %t/case1.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case1.la32 | FileCheck %s --check-prefix=CASE1 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case1.la64 | FileCheck %s --check-prefix=CASE1 ++# CASE1: pcalau12i $a0, 0 ++# CASE1-NEXT: ld.w $a0, $a0, 0 ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x11000 --section-start=.text=0x12000 -o %t/case2.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x11000 --section-start=.text=0x12000 -o %t/case2.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case2.la32 | FileCheck %s --check-prefix=CASE2 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case2.la64 | FileCheck %s --check-prefix=CASE2 ++# CASE2: pcalau12i $a0, -1 ++# CASE2-NEXT: ld.w $a0, $a0, 0 ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x117ff --section-start=.text=0x12000 -o %t/case3.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x117ff --section-start=.text=0x12000 -o %t/case3.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case3.la32 | FileCheck %s --check-prefix=CASE3 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case3.la64 | FileCheck %s --check-prefix=CASE3 ++# CASE3: pcalau12i $a0, -1 ++# CASE3-NEXT: ld.w $a0, $a0, 2047 ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x11800 --section-start=.text=0x12000 -o %t/case4.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x11800 --section-start=.text=0x12000 -o %t/case4.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case4.la32 | FileCheck %s --check-prefix=CASE4 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case4.la64 | FileCheck %s --check-prefix=CASE4 ++# CASE4: pcalau12i $a0, 0 ++# CASE4-NEXT: ld.w $a0, $a0, -2048 ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x12004 --section-start=.text=0x11ffc -o %t/case5.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x12004 --section-start=.text=0x11ffc -o %t/case5.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case5.la32 | FileCheck %s --check-prefix=CASE5 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case5.la64 | FileCheck %s --check-prefix=CASE5 ++# CASE5: pcalau12i $a0, 1 ++# CASE5-NEXT: ld.w $a0, $a0, 4 ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x12800 --section-start=.text=0x11ffc -o %t/case6.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x12800 --section-start=.text=0x11ffc -o %t/case6.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case6.la32 | FileCheck %s --check-prefix=CASE6 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case6.la64 | FileCheck %s --check-prefix=CASE6 ++# CASE6: pcalau12i $a0, 2 ++# CASE6-NEXT: ld.w $a0, $a0, -2048 ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x7ffff123 --section-start=.text=0x0 -o %t/case7.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x7ffff123 --section-start=.text=0x0 -o %t/case7.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case7.la32 | FileCheck %s --check-prefix=CASE7 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case7.la64 | FileCheck %s --check-prefix=CASE7 ++# CASE7: pcalau12i $a0, 524287 ++# CASE7-NEXT: ld.w $a0, $a0, 291 ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x7ffffabc --section-start=.text=0x0 -o %t/case8.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x7ffffabc --section-start=.text=0x0 -o %t/case8.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case8.la32 | FileCheck %s --check-prefix=CASE8 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case8.la64 | FileCheck %s --check-prefix=CASE8 ++# CASE8: pcalau12i $a0, -524288 ++# CASE8-NEXT: ld.w $a0, $a0, -1348 ++ ++# RUN: ld.lld %t/a.la32.o --section-start=.rodata=0x10123 --section-start=.text=0x80010000 -o %t/case9.la32 ++# RUN: ld.lld %t/a.la64.o --section-start=.rodata=0x10123 --section-start=.text=0x80010000 -o %t/case9.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case9.la32 | FileCheck %s --check-prefix=CASE9 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/case9.la64 | FileCheck %s --check-prefix=CASE9 ++# CASE9: pcalau12i $a0, -524288 ++# CASE9-NEXT: ld.w $a0, $a0, 291 ++ ++## page delta = 0x4443333322222000, page offset = 0x111 ++## %pc_lo12 = 0x111 = 273 ++## %pc_hi20 = 0x22222 = 139810 ++## %pc64_lo20 = 0x33333 = 209715 ++## %pc64_hi12 = 0x444 = 1092 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0x4443333334567111 --section-start=.text=0x0000000012345678 -o %t/extreme0 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme0 | FileCheck %s --check-prefix=EXTREME0 ++# EXTREME0: addi.d $t0, $zero, 273 ++# EXTREME0-NEXT: pcalau12i $t1, 139810 ++# EXTREME0-NEXT: lu32i.d $t0, 209715 ++# EXTREME0-NEXT: lu52i.d $t0, $t0, 1092 ++ ++## page delta = 0x4443333222223000, page offset = 0x888 ++## %pc_lo12 = 0x888 = -1912 ++## %pc_hi20 = 0x22223 = 139811 ++## %pc64_lo20 = 0x33332 = 209714 ++## %pc64_hi12 = 0x444 = 1092 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0x4443333334567888 --section-start=.text=0x0000000012345678 -o %t/extreme1 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme1 | FileCheck %s --check-prefix=EXTREME1 ++# EXTREME1: addi.d $t0, $zero, -1912 ++# EXTREME1-NEXT: pcalau12i $t1, 139811 ++# EXTREME1-NEXT: lu32i.d $t0, 209714 ++# EXTREME1-NEXT: lu52i.d $t0, $t0, 1092 ++ ++## page delta = 0x4443333499999000, page offset = 0x111 ++## %pc_lo12 = 0x111 = 273 ++## %pc_hi20 = 0x99999 = -419431 ++## %pc64_lo20 = 0x33334 = 209716 ++## %pc64_hi12 = 0x444 = 1092 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0x44433333abcde111 --section-start=.text=0x0000000012345678 -o %t/extreme2 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme2 | FileCheck %s --check-prefix=EXTREME2 ++# EXTREME2: addi.d $t0, $zero, 273 ++# EXTREME2-NEXT: pcalau12i $t1, -419431 ++# EXTREME2-NEXT: lu32i.d $t0, 209716 ++# EXTREME2-NEXT: lu52i.d $t0, $t0, 1092 ++ ++## page delta = 0x444333339999a000, page offset = 0x888 ++## %pc_lo12 = 0x888 = -1912 ++## %pc_hi20 = 0x9999a = -419430 ++## %pc64_lo20 = 0x33333 = 209715 ++## %pc64_hi12 = 0x444 = 1092 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0x44433333abcde888 --section-start=.text=0x0000000012345678 -o %t/extreme3 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme3 | FileCheck %s --check-prefix=EXTREME3 ++# EXTREME3: addi.d $t0, $zero, -1912 ++# EXTREME3-NEXT: pcalau12i $t1, -419430 ++# EXTREME3-NEXT: lu32i.d $t0, 209715 ++# EXTREME3-NEXT: lu52i.d $t0, $t0, 1092 ++ ++## page delta = 0x444aaaaa22222000, page offset = 0x111 ++## %pc_lo12 = 0x111 = 273 ++## %pc_hi20 = 0x22222 = 139810 ++## %pc64_lo20 = 0xaaaaa = -349526 ++## %pc64_hi12 = 0x444 = 1092 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0x444aaaaa34567111 --section-start=.text=0x0000000012345678 -o %t/extreme4 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme4 | FileCheck %s --check-prefix=EXTREME4 ++# EXTREME4: addi.d $t0, $zero, 273 ++# EXTREME4-NEXT: pcalau12i $t1, 139810 ++# EXTREME4-NEXT: lu32i.d $t0, -349526 ++# EXTREME4-NEXT: lu52i.d $t0, $t0, 1092 ++ ++## page delta = 0x444aaaa922223000, page offset = 0x888 ++## %pc_lo12 = 0x888 = -1912 ++## %pc_hi20 = 0x22223 = 139811 ++## %pc64_lo20 = 0xaaaa9 = -349527 ++## %pc64_hi12 = 0x444 = 1092 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0x444aaaaa34567888 --section-start=.text=0x0000000012345678 -o %t/extreme5 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme5 | FileCheck %s --check-prefix=EXTREME5 ++# EXTREME5: addi.d $t0, $zero, -1912 ++# EXTREME5-NEXT: pcalau12i $t1, 139811 ++# EXTREME5-NEXT: lu32i.d $t0, -349527 ++# EXTREME5-NEXT: lu52i.d $t0, $t0, 1092 ++ ++## page delta = 0x444aaaab99999000, page offset = 0x111 ++## %pc_lo12 = 0x111 = 273 ++## %pc_hi20 = 0x99999 = -419431 ++## %pc64_lo20 = 0xaaaab = -349525 ++## %pc64_hi12 = 0x444 = 1092 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0x444aaaaaabcde111 --section-start=.text=0x0000000012345678 -o %t/extreme6 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme6 | FileCheck %s --check-prefix=EXTREME6 ++# EXTREME6: addi.d $t0, $zero, 273 ++# EXTREME6-NEXT: pcalau12i $t1, -419431 ++# EXTREME6-NEXT: lu32i.d $t0, -349525 ++# EXTREME6-NEXT: lu52i.d $t0, $t0, 1092 ++ ++## page delta = 0x444aaaaa9999a000, page offset = 0x888 ++## %pc_lo12 = 0x888 = -1912 ++## %pc_hi20 = 0x9999a = -419430 ++## %pc64_lo20 = 0xaaaaa = -349526 ++## %pc64_hi12 = 0x444 = 1092 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0x444aaaaaabcde888 --section-start=.text=0x0000000012345678 -o %t/extreme7 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme7 | FileCheck %s --check-prefix=EXTREME7 ++# EXTREME7: addi.d $t0, $zero, -1912 ++# EXTREME7-NEXT: pcalau12i $t1, -419430 ++# EXTREME7-NEXT: lu32i.d $t0, -349526 ++# EXTREME7-NEXT: lu52i.d $t0, $t0, 1092 ++ ++## page delta = 0xbbb3333322222000, page offset = 0x111 ++## %pc_lo12 = 0x111 = 273 ++## %pc_hi20 = 0x22222 = 139810 ++## %pc64_lo20 = 0x33333 = 209715 ++## %pc64_hi12 = 0xbbb = -1093 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0xbbb3333334567111 --section-start=.text=0x0000000012345678 -o %t/extreme8 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme8 | FileCheck %s --check-prefix=EXTREME8 ++# EXTREME8: addi.d $t0, $zero, 273 ++# EXTREME8-NEXT: pcalau12i $t1, 139810 ++# EXTREME8-NEXT: lu32i.d $t0, 209715 ++# EXTREME8-NEXT: lu52i.d $t0, $t0, -1093 ++ ++## page delta = 0xbbb3333222223000, page offset = 0x888 ++## %pc_lo12 = 0x888 = -1912 ++## %pc_hi20 = 0x22223 = 139811 ++## %pc64_lo20 = 0x33332 = 209714 ++## %pc64_hi12 = 0xbbb = -1093 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0xbbb3333334567888 --section-start=.text=0x0000000012345678 -o %t/extreme9 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme9 | FileCheck %s --check-prefix=EXTREME9 ++# EXTREME9: addi.d $t0, $zero, -1912 ++# EXTREME9-NEXT: pcalau12i $t1, 139811 ++# EXTREME9-NEXT: lu32i.d $t0, 209714 ++# EXTREME9-NEXT: lu52i.d $t0, $t0, -1093 ++ ++## page delta = 0xbbb3333499999000, page offset = 0x111 ++## %pc_lo12 = 0x111 = 273 ++## %pc_hi20 = 0x99999 = -419431 ++## %pc64_lo20 = 0x33334 = 209716 ++## %pc64_hi12 = 0xbbb = -1093 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0xbbb33333abcde111 --section-start=.text=0x0000000012345678 -o %t/extreme10 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme10 | FileCheck %s --check-prefix=EXTREME10 ++# EXTREME10: addi.d $t0, $zero, 273 ++# EXTREME10-NEXT: pcalau12i $t1, -419431 ++# EXTREME10-NEXT: lu32i.d $t0, 209716 ++# EXTREME10-NEXT: lu52i.d $t0, $t0, -1093 ++ ++## page delta = 0xbbb333339999a000, page offset = 0x888 ++## %pc_lo12 = 0x888 = -1912 ++## %pc_hi20 = 0x9999a = -419430 ++## %pc64_lo20 = 0x33333 = 209715 ++## %pc64_hi12 = 0xbbb = -1093 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0xbbb33333abcde888 --section-start=.text=0x0000000012345678 -o %t/extreme11 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme11 | FileCheck %s --check-prefix=EXTREME11 ++# EXTREME11: addi.d $t0, $zero, -1912 ++# EXTREME11-NEXT: pcalau12i $t1, -419430 ++# EXTREME11-NEXT: lu32i.d $t0, 209715 ++# EXTREME11-NEXT: lu52i.d $t0, $t0, -1093 ++ ++## page delta = 0xbbbaaaaa22222000, page offset = 0x111 ++## %pc_lo12 = 0x111 = 273 ++## %pc_hi20 = 0x22222 = 139810 ++## %pc64_lo20 = 0xaaaaa = -349526 ++## %pc64_hi12 = 0xbbb = -1093 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0xbbbaaaaa34567111 --section-start=.text=0x0000000012345678 -o %t/extreme12 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme12 | FileCheck %s --check-prefix=EXTREME12 ++# EXTREME12: addi.d $t0, $zero, 273 ++# EXTREME12-NEXT: pcalau12i $t1, 139810 ++# EXTREME12-NEXT: lu32i.d $t0, -349526 ++# EXTREME12-NEXT: lu52i.d $t0, $t0, -1093 ++ ++## page delta = 0xbbbaaaa922223000, page offset = 0x888 ++## %pc_lo12 = 0x888 = -1912 ++## %pc_hi20 = 0x22223 = 139811 ++## %pc64_lo20 = 0xaaaa9 = -349527 ++## %pc64_hi12 = 0xbbb = -1093 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0xbbbaaaaa34567888 --section-start=.text=0x0000000012345678 -o %t/extreme13 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme13 | FileCheck %s --check-prefix=EXTREME13 ++# EXTREME13: addi.d $t0, $zero, -1912 ++# EXTREME13-NEXT: pcalau12i $t1, 139811 ++# EXTREME13-NEXT: lu32i.d $t0, -349527 ++# EXTREME13-NEXT: lu52i.d $t0, $t0, -1093 ++ ++## page delta = 0xbbbaaaab99999000, page offset = 0x111 ++## %pc_lo12 = 0x111 = 273 ++## %pc_hi20 = 0x99999 = -419431 ++## %pc64_lo20 = 0xaaaab = -349525 ++## %pc64_hi12 = 0xbbb = -1093 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0xbbbaaaaaabcde111 --section-start=.text=0x0000000012345678 -o %t/extreme14 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme14 | FileCheck %s --check-prefix=EXTREME14 ++# EXTREME14: addi.d $t0, $zero, 273 ++# EXTREME14-NEXT: pcalau12i $t1, -419431 ++# EXTREME14-NEXT: lu32i.d $t0, -349525 ++# EXTREME14-NEXT: lu52i.d $t0, $t0, -1093 ++ ++## page delta = 0xbbbaaaaa9999a000, page offset = 0x888 ++## %pc_lo12 = 0x888 = -1912 ++## %pc_hi20 = 0x9999a = -419430 ++## %pc64_lo20 = 0xaaaaa = -349526 ++## %pc64_hi12 = 0xbbb = -1093 ++# RUN: ld.lld %t/extreme.o --section-start=.rodata=0xbbbaaaaaabcde888 --section-start=.text=0x0000000012345678 -o %t/extreme15 ++# RUN: llvm-objdump -d --no-show-raw-insn %t/extreme15 | FileCheck %s --check-prefix=EXTREME15 ++# EXTREME15: addi.d $t0, $zero, -1912 ++# EXTREME15-NEXT: pcalau12i $t1, -419430 ++# EXTREME15-NEXT: lu32i.d $t0, -349526 ++# EXTREME15-NEXT: lu52i.d $t0, $t0, -1093 ++ ++#--- a.s ++.rodata ++x: ++.word 10 ++.text ++.global _start ++_start: ++ pcalau12i $a0, %pc_hi20(x) ++ ld.w $a0, $a0, %pc_lo12(x) ++ ++#--- extreme.s ++.rodata ++x: ++.word 10 ++.text ++.global _start ++_start: ++ addi.d $t0, $zero, %pc_lo12(x) ++ pcalau12i $t1, %pc_hi20(x) ++ lu32i.d $t0, %pc64_lo20(x) ++ lu52i.d $t0, $t0, %pc64_hi12(x) +diff --git a/lld/test/ELF/loongarch-pcala-lo12-jirl-shared.s b/lld/test/ELF/loongarch-pcala-lo12-jirl-shared.s +new file mode 100644 +index 000000000..991f8fbe9 +--- /dev/null ++++ b/lld/test/ELF/loongarch-pcala-lo12-jirl-shared.s +@@ -0,0 +1,60 @@ ++# REQUIRES: loongarch ++# RUN: rm -rf %t && split-file %s %t ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %t/a.s -o %t/a.la32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-elf %t/a.s -o %t/a.la64.o ++ ++# RUN: ld.lld %t/a.la32.o -shared -T %t/a.t -o %t/a.la32.so ++# RUN: ld.lld %t/a.la64.o -shared -T %t/a.t -o %t/a.la64.so ++ ++# RUN: llvm-objdump -d --no-show-raw-insn %t/a.la32.so | FileCheck --check-prefixes=DIS,DIS32 %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/a.la64.so | FileCheck --check-prefixes=DIS,DIS64 %s ++ ++## PLT should be present in this case. ++# DIS: Disassembly of section .plt: ++# DIS: <.plt>: ++# DIS: 234020: pcaddu12i $t3, 510 ++# DIS32-NEXT: ld.w $t3, $t3, 84 ++# DIS64-NEXT: ld.d $t3, $t3, 184 ++# DIS-NEXT: jirl $t1, $t3, 0 ++# DIS-NEXT: nop ++ ++# DIS: Disassembly of section .text: ++# DIS: : ++# DIS-NEXT: nop ++# DIS-NEXT: nop ++# DIS-NEXT: nop ++# DIS-NEXT: pcalau12i $t0, -510 ++# DIS-NEXT: jirl $zero, $t0, 32 ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %t/error.s -o %t/error.la32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-elf %t/error.s -o %t/error.la64.o ++# RUN: not ld.lld %t/error.la32.o -shared -o %t/error.la32.so 2>&1 | FileCheck --check-prefix=ERR %s ++# RUN: not ld.lld %t/error.la64.o -shared -o %t/error.la64.so 2>&1 | FileCheck --check-prefix=ERR %s ++# ERR: error: relocation R_LARCH_PCALA_LO12 cannot be used against symbol 'bar'; recompile with -fPIC ++ ++#--- a.t ++SECTIONS { ++ .plt 0x234000: { *(.plt) } ++ .text 0x432000: { *(.text) } ++} ++ ++#--- a.s ++.p2align 12 ++.global foo ++foo: ++## The nops are for pushing the relocs off page boundary, to better see the ++## page-aligned semantics in action. ++ nop ++ nop ++ nop ++ ## The offsets should be -510 (0x234 - 0x432) and 32 (PLT header size + 0) ++ ## respectively. ++ pcalau12i $t0, %pc_hi20(bar) ++ jirl $zero, $t0, %pc_lo12(bar) ++ ++#--- error.s ++.global foo ++foo: ++ pcalau12i $t0, %pc_hi20(bar) ++ ld.w $t0, $t0, %pc_lo12(bar) +diff --git a/lld/test/ELF/loongarch-pcala-lo12-jirl.s b/lld/test/ELF/loongarch-pcala-lo12-jirl.s +new file mode 100644 +index 000000000..1a03152aa +--- /dev/null ++++ b/lld/test/ELF/loongarch-pcala-lo12-jirl.s +@@ -0,0 +1,42 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %s -o %t.la32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-elf %s -o %t.la64.o ++ ++# RUN: ld.lld %t.la32.o -o %t.la32 ++# RUN: ld.lld %t.la64.o -o %t.la64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t.la32 | FileCheck %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t.la64 | FileCheck %s ++# CHECK: pcalau12i $t0, -1 ++# CHECK-NEXT: jirl $ra, $t0, 564 ++# CHECK-NEXT: pcalau12i $t0, 0 ++# CHECK-NEXT: jirl $zero, $t0, -1348 ++ ++## PLT shouldn't get generated in this case. ++# CHECK-NOT: Disassembly of section .plt: ++ ++.p2align 12 ++.org 0x234 ++.global foo ++foo: ++ li.w $a0, 42 ++ ret ++ ++.org 0xabc ++.global bar ++bar: ++ li.w $a7, 94 ++ syscall 0 ++ ++.org 0x1000 ++.global _start ++_start: ++## The nops are for pushing the relocs off page boundary, to better see the ++## page-aligned semantics in action. ++ nop ++ nop ++ nop ++ pcalau12i $t0, %pc_hi20(foo) ++ jirl $ra, $t0, %pc_lo12(foo) ++ pcalau12i $t0, %pc_hi20(bar) ++ jirl $zero, $t0, %pc_lo12(bar) +diff --git a/lld/test/ELF/loongarch-pcrel20-s2.s b/lld/test/ELF/loongarch-pcrel20-s2.s +new file mode 100644 +index 000000000..06707b6ad +--- /dev/null ++++ b/lld/test/ELF/loongarch-pcrel20-s2.s +@@ -0,0 +1,32 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %s -o %t.la32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-elf %s -o %t.la64.o ++ ++# RUN: ld.lld %t.la32.o --section-start=.text=0x20000 --section-start=.data=0x20008 -o %t.la32.1 ++# RUN: ld.lld %t.la64.o --section-start=.text=0x20000 --section-start=.data=0x20008 -o %t.la64.1 ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la32.1 | FileCheck --match-full-lines %s ++# RUN: llvm-objdump --no-show-raw-insn -d %t.la64.1 | FileCheck --match-full-lines %s ++# CHECK: 20000: pcaddi $t0, 2 ++ ++# RUN: not ld.lld %t.la32.o --section-start=.text=0x20000 --section-start=.data=0x220000 -o /dev/null 2>&1 | \ ++# RUN: FileCheck -DFILE=%t.la32.o --check-prefix=ERROR-RANGE %s ++# RUN: not ld.lld %t.la64.o --section-start=.text=0x20000 --section-start=.data=0x220000 -o /dev/null 2>&1 | \ ++# RUN: FileCheck -DFILE=%t.la64.o --check-prefix=ERROR-RANGE %s ++# ERROR-RANGE: error: [[FILE]]:(.text+0x0): relocation R_LARCH_PCREL20_S2 out of range: 2097152 is not in [-2097152, 2097151] ++ ++# RUN: not ld.lld %t.la32.o --section-start=.text=0x20000 --section-start=.data=0x40001 -o /dev/null 2>&1 | \ ++# RUN: FileCheck -DFILE=%t.la32.o --check-prefix=ERROR-ALIGN %s ++# RUN: not ld.lld %t.la64.o --section-start=.text=0x20000 --section-start=.data=0x40001 -o /dev/null 2>&1 | \ ++# RUN: FileCheck -DFILE=%t.la64.o --check-prefix=ERROR-ALIGN %s ++# ERROR-ALIGN: error: [[FILE]]:(.text+0x0): improper alignment for relocation R_LARCH_PCREL20_S2: 0x20001 is not aligned to 4 bytes ++ ++.global _start ++ ++_start: ++1: ++ pcaddi $t0, 0 ++ .reloc 1b, R_LARCH_PCREL20_S2, .data ++ ++.data ++ .word 0 +diff --git a/lld/test/ELF/loongarch-plt.s b/lld/test/ELF/loongarch-plt.s +new file mode 100644 +index 000000000..82af53d39 +--- /dev/null ++++ b/lld/test/ELF/loongarch-plt.s +@@ -0,0 +1,108 @@ ++# REQUIRES: loongarch ++# RUN: echo '.globl bar, weak; .type bar,@function; .type weak,@function; bar: weak:' > %t1.s ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %t1.s -o %t1.32.o ++# RUN: ld.lld -shared %t1.32.o -soname=t1.32.so -o %t1.32.so ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %s -o %t.32.o ++# RUN: ld.lld %t.32.o %t1.32.so -z separate-code -o %t.32 ++# RUN: llvm-readelf -S -s %t.32 | FileCheck --check-prefixes=SEC,NM %s ++# RUN: llvm-readobj -r %t.32 | FileCheck --check-prefix=RELOC32 %s ++# RUN: llvm-readelf -x .got.plt %t.32 | FileCheck --check-prefix=GOTPLT32 %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t.32 | FileCheck --check-prefixes=DIS,DIS32 %s ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t1.s -o %t1.64.o ++# RUN: ld.lld -shared %t1.64.o -soname=t1.64.so -o %t1.64.so ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %s -o %t.64.o ++# RUN: ld.lld %t.64.o %t1.64.so -z separate-code -o %t.64 ++# RUN: llvm-readelf -S -s %t.64 | FileCheck --check-prefixes=SEC,NM %s ++# RUN: llvm-readobj -r %t.64 | FileCheck --check-prefix=RELOC64 %s ++# RUN: llvm-readelf -x .got.plt %t.64 | FileCheck --check-prefix=GOTPLT64 %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t.64 | FileCheck --check-prefixes=DIS,DIS64 %s ++ ++# SEC: .plt PROGBITS {{0*}}00020020 ++ ++## A canonical PLT has a non-zero st_value. bar and weak are called but their ++## addresses are not taken, so a canonical PLT is not necessary. ++# NM: {{0*}}00000000 0 FUNC GLOBAL DEFAULT UND bar ++# NM: {{0*}}00000000 0 FUNC WEAK DEFAULT UND weak ++ ++## The .got.plt slots relocated by .rela.plt point to .plt ++## This is required by glibc. ++# RELOC32: .rela.plt { ++# RELOC32-NEXT: 0x40070 R_LARCH_JUMP_SLOT bar 0x0 ++# RELOC32-NEXT: 0x40074 R_LARCH_JUMP_SLOT weak 0x0 ++# RELOC32-NEXT: } ++# GOTPLT32: section '.got.plt' ++# GOTPLT32-NEXT: 0x00040068 00000000 00000000 20000200 20000200 ++ ++# RELOC64: .rela.plt { ++# RELOC64-NEXT: 0x400E0 R_LARCH_JUMP_SLOT bar 0x0 ++# RELOC64-NEXT: 0x400E8 R_LARCH_JUMP_SLOT weak 0x0 ++# RELOC64-NEXT: } ++# GOTPLT64: section '.got.plt' ++# GOTPLT64-NEXT: 0x000400d0 00000000 00000000 00000000 00000000 ++# GOTPLT64-NEXT: 0x000400e0 20000200 00000000 20000200 00000000 ++ ++# DIS: <_start>: ++## Direct call ++## foo - . = 0x20010-0x20000 = 16 ++# DIS-NEXT: 20000: bl 16 ++## bar@plt - . = 0x20040-0x20004 = 60 ++# DIS-NEXT: 20004: bl 60 ++## bar@plt - . = 0x20040-0x20008 = 56 ++# DIS-NEXT: 20008: bl 56 ++## weak@plt - . = 0x20050-0x2000c = 68 ++# DIS-NEXT: 2000c: bl 68 ++# DIS: : ++# DIS-NEXT: 20010: ++ ++# DIS: Disassembly of section .plt: ++# DIS: <.plt>: ++## 32-bit: .got.plt - .plt = 0x40068 - 0x20020 = 4096*32+72 ++# DIS32-NEXT: pcaddu12i $t2, 32 ++# DIS32-NEXT: sub.w $t1, $t1, $t3 ++# DIS32-NEXT: ld.w $t3, $t2, 72 ++# DIS32-NEXT: addi.w $t1, $t1, -44 ++# DIS32-NEXT: addi.w $t0, $t2, 72 ++# DIS32-NEXT: srli.w $t1, $t1, 2 ++# DIS32-NEXT: ld.w $t0, $t0, 4 ++# DIS32-NEXT: jr $t3 ++ ++## 64-bit: .got.plt - .plt = 0x400d0 - 0x20020 = 4096*32+176 ++# DIS64-NEXT: pcaddu12i $t2, 32 ++# DIS64-NEXT: sub.d $t1, $t1, $t3 ++# DIS64-NEXT: ld.d $t3, $t2, 176 ++# DIS64-NEXT: addi.d $t1, $t1, -44 ++# DIS64-NEXT: addi.d $t0, $t2, 176 ++# DIS64-NEXT: srli.d $t1, $t1, 1 ++# DIS64-NEXT: ld.d $t0, $t0, 8 ++# DIS64-NEXT: jr $t3 ++ ++## 32-bit: &.got.plt[bar]-. = 0x40070-0x20040 = 4096*32+48 ++## 64-bit: &.got.plt[bar]-. = 0x400e0-0x20040 = 4096*32+160 ++# DIS: 20040: pcaddu12i $t3, 32 ++# DIS32-NEXT: ld.w $t3, $t3, 48 ++# DIS64-NEXT: ld.d $t3, $t3, 160 ++# DIS-NEXT: jirl $t1, $t3, 0 ++# DIS-NEXT: nop ++ ++## 32-bit: &.got.plt[weak]-. = 0x40074-0x20050 = 4096*32+36 ++## 64-bit: &.got.plt[weak]-. = 0x400e8-0x20050 = 4096*32+152 ++# DIS: 20050: pcaddu12i $t3, 32 ++# DIS32-NEXT: ld.w $t3, $t3, 36 ++# DIS64-NEXT: ld.d $t3, $t3, 152 ++# DIS-NEXT: jirl $t1, $t3, 0 ++# DIS-NEXT: nop ++ ++.global _start, foo, bar ++.weak weak ++ ++_start: ++ bl foo ++ bl bar ++ bl %plt(bar) ++ bl weak ++ ++## foo is local and non-preemptible, no PLT is generated. ++foo: ++ ret +diff --git a/lld/test/ELF/loongarch-reloc-pic.s b/lld/test/ELF/loongarch-reloc-pic.s +new file mode 100644 +index 000000000..b23ad55a2 +--- /dev/null ++++ b/lld/test/ELF/loongarch-reloc-pic.s +@@ -0,0 +1,44 @@ ++# REQUIRES: loongarch ++# RUN: rm -rf %t && split-file %s %t ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %t/32.s -o %t/32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/64.s -o %t/64.o ++# RUN: ld.lld -shared %t/32.o -o %t/32.so ++# RUN: llvm-nm %t/32.so | FileCheck --check-prefix=NM32 %s ++# RUN: llvm-readobj -r %t/32.so | FileCheck --check-prefix=RELOC32 %s ++# RUN: ld.lld -shared %t/64.o -o %t/64.so ++# RUN: llvm-nm %t/64.so | FileCheck --check-prefix=NM64 %s ++# RUN: llvm-readobj -r %t/64.so | FileCheck --check-prefix=RELOC64 %s ++ ++## R_LARCH_32 and R_LARCH_64 are absolute relocation types. ++## In PIC mode, they create relative relocations if the symbol is non-preemptable. ++ ++# NM32: 000301fc d b ++# NM64: 00030350 d b ++ ++# RELOC32: .rela.dyn { ++# RELOC32-NEXT: 0x301FC R_LARCH_RELATIVE - 0x301FC ++# RELOC32-NEXT: 0x301F8 R_LARCH_32 a 0 ++# RELOC32-NEXT: } ++# RELOC64: .rela.dyn { ++# RELOC64-NEXT: 0x30350 R_LARCH_RELATIVE - 0x30350 ++# RELOC64-NEXT: 0x30348 R_LARCH_64 a 0 ++# RELOC64-NEXT: } ++ ++#--- 32.s ++.globl a, b ++.hidden b ++ ++.data ++.long a ++b: ++.long b ++ ++#--- 64.s ++.globl a, b ++.hidden b ++ ++.data ++.quad a ++b: ++.quad b +diff --git a/lld/test/ELF/loongarch-tls-gd-edge-case.s b/lld/test/ELF/loongarch-tls-gd-edge-case.s +new file mode 100644 +index 000000000..9f25f10c7 +--- /dev/null ++++ b/lld/test/ELF/loongarch-tls-gd-edge-case.s +@@ -0,0 +1,46 @@ ++# REQUIRES: loongarch ++ ++## Edge case: when a TLS symbol is being accessed in both GD and IE manners, ++## correct reloc behavior should be preserved for both kinds of accesses. ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %s -o %t.la32.o ++# RUN: ld.lld %t.la32.o -shared -o %t.la32 ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %s -o %t.la64.o ++# RUN: ld.lld %t.la64.o -shared -o %t.la64 ++ ++# RUN: llvm-readelf -Wr %t.la32 | FileCheck --check-prefix=LA32-REL %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t.la32 | FileCheck --check-prefix=LA32 %s ++ ++# RUN: llvm-readelf -Wr %t.la64 | FileCheck --check-prefix=LA64-REL %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t.la64 | FileCheck --check-prefix=LA64 %s ++ ++# LA32-REL-NOT: R_LARCH_32 ++# LA32-REL: 0002023c 00000206 R_LARCH_TLS_DTPMOD32 00000000 y + 0 ++# LA32-REL-NEXT: 00020240 00000208 R_LARCH_TLS_DTPREL32 00000000 y + 0 ++# LA32-REL-NEXT: 00020244 0000020a R_LARCH_TLS_TPREL32 00000000 y + 0 ++ ++# LA64-REL-NOT: R_LARCH_64 ++# LA64-REL: 00000000000203a0 0000000200000007 R_LARCH_TLS_DTPMOD64 0000000000000000 y + 0 ++# LA64-REL-NEXT: 00000000000203a8 0000000200000009 R_LARCH_TLS_DTPREL64 0000000000000000 y + 0 ++# LA64-REL-NEXT: 00000000000203b0 000000020000000b R_LARCH_TLS_TPREL64 0000000000000000 y + 0 ++ ++# LA32: 101d4: pcalau12i $a0, 16 ++# LA32-NEXT: ld.w $a0, $a0, 580 ++# LA32-NEXT: pcalau12i $a1, 16 ++# LA32-NEXT: addi.w $a1, $a1, 572 ++ ++# LA64: 102e0: pcalau12i $a0, 16 ++# LA64-NEXT: ld.d $a0, $a0, 944 ++# LA64-NEXT: pcalau12i $a1, 16 ++# LA64-NEXT: addi.d $a1, $a1, 928 ++ ++.global _start ++_start: ++la.tls.ie $a0, y # should refer to the GOT entry relocated by the R_LARCH_TLS_TPRELnn record ++la.tls.gd $a1, y # should refer to the GOT entry relocated by the R_LARCH_TLS_DTPMODnn record ++ ++.section .tbss,"awT",@nobits ++.global y ++y: ++.word 0 ++.size y, 4 +diff --git a/lld/test/ELF/loongarch-tls-gd.s b/lld/test/ELF/loongarch-tls-gd.s +new file mode 100644 +index 000000000..2aecb44c1 +--- /dev/null ++++ b/lld/test/ELF/loongarch-tls-gd.s +@@ -0,0 +1,136 @@ ++# REQUIRES: loongarch ++# RUN: rm -rf %t && split-file %s %t ++ ++## LoongArch psABI doesn't specify TLS relaxation. Though the code sequences are not ++## relaxed, dynamic relocations can be omitted for GD->LE relaxation. ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %t/a.s -o %t/a.32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %t/bc.s -o %t/bc.32.o ++# RUN: ld.lld -shared -soname=bc.so %t/bc.32.o -o %t/bc.32.so ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %t/tga.s -o %t/tga.32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/a.s -o %t/a.64.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/bc.s -o %t/bc.64.o ++# RUN: ld.lld -shared -soname=bc.so %t/bc.64.o -o %t/bc.64.so ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/tga.s -o %t/tga.64.o ++ ++## LA32 GD ++# RUN: ld.lld -shared %t/a.32.o %t/bc.32.o -o %t/gd.32.so ++# RUN: llvm-readobj -r %t/gd.32.so | FileCheck --check-prefix=GD32-REL %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/gd.32.so | FileCheck --check-prefix=GD32 %s ++ ++## LA32 GD -> LE ++# RUN: ld.lld %t/a.32.o %t/bc.32.o %t/tga.32.o -o %t/le.32 ++# RUN: llvm-readelf -r %t/le.32 | FileCheck --check-prefix=NOREL %s ++# RUN: llvm-readelf -x .got %t/le.32 | FileCheck --check-prefix=LE32-GOT %s ++# RUN: ld.lld -pie %t/a.32.o %t/bc.32.o %t/tga.32.o -o %t/le-pie.32 ++# RUN: llvm-readelf -r %t/le-pie.32 | FileCheck --check-prefix=NOREL %s ++# RUN: llvm-readelf -x .got %t/le-pie.32 | FileCheck --check-prefix=LE32-GOT %s ++ ++## LA32 GD -> IE ++# RUN: ld.lld %t/a.32.o %t/bc.32.so %t/tga.32.o -o %t/ie.32 ++# RUN: llvm-readobj -r %t/ie.32 | FileCheck --check-prefix=IE32-REL %s ++# RUN: llvm-readelf -x .got %t/ie.32 | FileCheck --check-prefix=IE32-GOT %s ++ ++## LA64 GD ++# RUN: ld.lld -shared %t/a.64.o %t/bc.64.o -o %t/gd.64.so ++# RUN: llvm-readobj -r %t/gd.64.so | FileCheck --check-prefix=GD64-REL %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/gd.64.so | FileCheck --check-prefix=GD64 %s ++ ++## LA64 GD -> LE ++# RUN: ld.lld %t/a.64.o %t/bc.64.o %t/tga.64.o -o %t/le.64 ++# RUN: llvm-readelf -r %t/le.64 | FileCheck --check-prefix=NOREL %s ++# RUN: llvm-readelf -x .got %t/le.64 | FileCheck --check-prefix=LE64-GOT %s ++# RUN: ld.lld -pie %t/a.64.o %t/bc.64.o %t/tga.64.o -o %t/le-pie.64 ++# RUN: llvm-readelf -r %t/le-pie.64 | FileCheck --check-prefix=NOREL %s ++# RUN: llvm-readelf -x .got %t/le-pie.64 | FileCheck --check-prefix=LE64-GOT %s ++ ++## LA64 GD -> IE ++# RUN: ld.lld %t/a.64.o %t/bc.64.so %t/tga.64.o -o %t/ie.64 ++# RUN: llvm-readobj -r %t/ie.64 | FileCheck --check-prefix=IE64-REL %s ++# RUN: llvm-readelf -x .got %t/ie.64 | FileCheck --check-prefix=IE64-GOT %s ++ ++# GD32-REL: .rela.dyn { ++# GD32-REL-NEXT: 0x20310 R_LARCH_TLS_DTPMOD32 a 0x0 ++# GD32-REL-NEXT: 0x20314 R_LARCH_TLS_DTPREL32 a 0x0 ++# GD32-REL-NEXT: 0x20318 R_LARCH_TLS_DTPMOD32 b 0x0 ++# GD32-REL-NEXT: 0x2031C R_LARCH_TLS_DTPREL32 b 0x0 ++# GD32-REL-NEXT: } ++ ++## &DTPMOD(a) - . = 0x20310 - 0x10250: 0x10 pages, page offset 0x310 ++# GD32: 10250: pcalau12i $a0, 16 ++# GD32-NEXT: addi.w $a0, $a0, 784 ++# GD32-NEXT: bl 56 ++ ++## &DTPMOD(b) - . = 0x20318 - 0x1025c: 0x10 pages, page offset 0x318 ++# GD32: 1025c: pcalau12i $a0, 16 ++# GD32-NEXT: addi.w $a0, $a0, 792 ++# GD32-NEXT: bl 44 ++ ++# GD64-REL: .rela.dyn { ++# GD64-REL-NEXT: 0x204C0 R_LARCH_TLS_DTPMOD64 a 0x0 ++# GD64-REL-NEXT: 0x204C8 R_LARCH_TLS_DTPREL64 a 0x0 ++# GD64-REL-NEXT: 0x204D0 R_LARCH_TLS_DTPMOD64 b 0x0 ++# GD64-REL-NEXT: 0x204D8 R_LARCH_TLS_DTPREL64 b 0x0 ++# GD64-REL-NEXT: } ++ ++## &DTPMOD(a) - . = 0x204c0 - 0x10398: 0x10 pages, page offset 0x4c0 ++# GD64: 10398: pcalau12i $a0, 16 ++# GD64-NEXT: addi.d $a0, $a0, 1216 ++# GD64-NEXT: bl 48 ++ ++## &DTPMOD(b) - . = 0x204d0 - 0x103a4: 0x10 pages, page offset 0x4d0 ++# GD64: 103a4: pcalau12i $a0, 16 ++# GD64-NEXT: addi.d $a0, $a0, 1232 ++# GD64-NEXT: bl 36 ++ ++# NOREL: no relocations ++ ++## .got contains pre-populated values: [a@dtpmod, a@dtprel, b@dtpmod, b@dtprel] ++## a@dtprel = st_value(a) = 0x8 ++## b@dtprel = st_value(b) = 0xc ++# LE32-GOT: section '.got': ++# LE32-GOT-NEXT: 0x[[#%x,A:]] 01000000 08000000 01000000 0c000000 ++# LE64-GOT: section '.got': ++# LE64-GOT-NEXT: 0x[[#%x,A:]] 01000000 00000000 08000000 00000000 ++# LE64-GOT-NEXT: 0x[[#%x,A:]] 01000000 00000000 0c000000 00000000 ++ ++## a is local - relaxed to LE - its DTPMOD/DTPREL slots are link-time constants. ++## b is external - DTPMOD/DTPREL dynamic relocations are required. ++# IE32-REL: .rela.dyn { ++# IE32-REL-NEXT: 0x30228 R_LARCH_TLS_DTPMOD32 b 0x0 ++# IE32-REL-NEXT: 0x3022C R_LARCH_TLS_DTPREL32 b 0x0 ++# IE32-REL-NEXT: } ++# IE32-GOT: section '.got': ++# IE32-GOT-NEXT: 0x00030220 01000000 08000000 00000000 00000000 ++ ++# IE64-REL: .rela.dyn { ++# IE64-REL-NEXT: 0x30388 R_LARCH_TLS_DTPMOD64 b 0x0 ++# IE64-REL-NEXT: 0x30390 R_LARCH_TLS_DTPREL64 b 0x0 ++# IE64-REL-NEXT: } ++# IE64-GOT: section '.got': ++# IE64-GOT-NEXT: 0x00030378 01000000 00000000 08000000 00000000 ++# IE64-GOT-NEXT: 0x00030388 00000000 00000000 00000000 00000000 ++ ++#--- a.s ++la.tls.gd $a0, a ++bl %plt(__tls_get_addr) ++ ++la.tls.gd $a0, b ++bl %plt(__tls_get_addr) ++ ++.section .tbss,"awT",@nobits ++.globl a ++.zero 8 ++a: ++.zero 4 ++ ++#--- bc.s ++.section .tbss,"awT",@nobits ++.globl b, c ++b: ++.zero 4 ++c: ++ ++#--- tga.s ++.globl __tls_get_addr ++__tls_get_addr: +diff --git a/lld/test/ELF/loongarch-tls-ie.s b/lld/test/ELF/loongarch-tls-ie.s +new file mode 100644 +index 000000000..78c207991 +--- /dev/null ++++ b/lld/test/ELF/loongarch-tls-ie.s +@@ -0,0 +1,114 @@ ++# REQUIRES: loongarch ++# RUN: rm -rf %t && split-file %s %t ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %t/32.s -o %t/32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/64.s -o %t/64.o ++ ++## LA32 IE ++# RUN: ld.lld -shared %t/32.o -o %t/32.so ++# RUN: llvm-readobj -r -d %t/32.so | FileCheck --check-prefix=IE32-REL %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/32.so | FileCheck --check-prefixes=IE32 %s ++ ++## LA32 IE -> LE ++# RUN: ld.lld %t/32.o -o %t/32 ++# RUN: llvm-readelf -r %t/32 | FileCheck --check-prefix=NOREL %s ++# RUN: llvm-readelf -x .got %t/32 | FileCheck --check-prefix=LE32-GOT %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/32 | FileCheck --check-prefixes=LE32 %s ++ ++## LA64 IE ++# RUN: ld.lld -shared %t/64.o -o %t/64.so ++# RUN: llvm-readobj -r -d %t/64.so | FileCheck --check-prefix=IE64-REL %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/64.so | FileCheck --check-prefixes=IE64 %s ++ ++## LA64 IE -> LE ++# RUN: ld.lld %t/64.o -o %t/64 ++# RUN: llvm-readelf -r %t/64 | FileCheck --check-prefix=NOREL %s ++# RUN: llvm-readelf -x .got %t/64 | FileCheck --check-prefix=LE64-GOT %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/64 | FileCheck --check-prefixes=LE64 %s ++ ++# IE32-REL: FLAGS STATIC_TLS ++# IE32-REL: .rela.dyn { ++# IE32-REL-NEXT: 0x20218 R_LARCH_TLS_TPREL32 - 0xC ++# IE32-REL-NEXT: 0x20214 R_LARCH_TLS_TPREL32 a 0x0 ++# IE32-REL-NEXT: } ++ ++# IE64-REL: FLAGS STATIC_TLS ++# IE64-REL: .rela.dyn { ++# IE64-REL-NEXT: 0x20370 R_LARCH_TLS_TPREL64 - 0xC ++# IE64-REL-NEXT: 0x20368 R_LARCH_TLS_TPREL64 a 0x0 ++# IE64-REL-NEXT: } ++ ++## LA32: ++## &.got[0] - . = 0x20214 - 0x101a4: 0x10 pages, page offset 0x214 ++## &.got[1] - . = 0x20218 - 0x101b0: 0x10 pages, page offset 0x218 ++# IE32: 101a4: pcalau12i $a4, 16 ++# IE32-NEXT: ld.w $a4, $a4, 532 ++# IE32-NEXT: add.w $a4, $a4, $tp ++# IE32-NEXT: 101b0: pcalau12i $a5, 16 ++# IE32-NEXT: ld.w $a5, $a5, 536 ++# IE32-NEXT: add.w $a5, $a5, $tp ++ ++## LA64: ++## &.got[0] - . = 0x20368 - 0x102a0: 0x10 pages, page offset 0x368 ++## &.got[1] - . = 0x20370 - 0x102ac: 0x10 pages, page offset 0x370 ++# IE64: 102a0: pcalau12i $a4, 16 ++# IE64-NEXT: ld.d $a4, $a4, 872 ++# IE64-NEXT: add.d $a4, $a4, $tp ++# IE64-NEXT: 102ac: pcalau12i $a5, 16 ++# IE64-NEXT: ld.d $a5, $a5, 880 ++# IE64-NEXT: add.d $a5, $a5, $tp ++ ++# NOREL: no relocations ++ ++# a@tprel = st_value(a) = 0x8 ++# b@tprel = st_value(a) = 0xc ++# LE32-GOT: section '.got': ++# LE32-GOT-NEXT: 0x0003012c 08000000 0c000000 ++# LE64-GOT: section '.got': ++# LE64-GOT-NEXT: 0x000301e0 08000000 00000000 0c000000 00000000 ++ ++## LA32: ++## &.got[0] - . = 0x3012c - 0x20114: 0x10 pages, page offset 0x12c ++## &.got[1] - . = 0x30130 - 0x20120: 0x10 pages, page offset 0x130 ++# LE32: 20114: pcalau12i $a4, 16 ++# LE32-NEXT: ld.w $a4, $a4, 300 ++# LE32-NEXT: add.w $a4, $a4, $tp ++# LE32-NEXT: 20120: pcalau12i $a5, 16 ++# LE32-NEXT: ld.w $a5, $a5, 304 ++# LE32-NEXT: add.w $a5, $a5, $tp ++ ++## LA64: ++## &.got[0] - . = 0x301e0 - 0x201c8: 0x10 pages, page offset 0x1e0 ++## &.got[1] - . = 0x301e8 - 0x201d4: 0x10 pages, page offset 0x1e8 ++# LE64: 201c8: pcalau12i $a4, 16 ++# LE64-NEXT: ld.d $a4, $a4, 480 ++# LE64-NEXT: add.d $a4, $a4, $tp ++# LE64-NEXT: 201d4: pcalau12i $a5, 16 ++# LE64-NEXT: ld.d $a5, $a5, 488 ++# LE64-NEXT: add.d $a5, $a5, $tp ++ ++#--- 32.s ++la.tls.ie $a4, a ++add.w $a4, $a4, $tp ++la.tls.ie $a5, b ++add.w $a5, $a5, $tp ++ ++.section .tbss,"awT",@nobits ++.globl a ++.zero 8 ++a: ++.zero 4 ++b: ++ ++#--- 64.s ++la.tls.ie $a4, a ++add.d $a4, $a4, $tp ++la.tls.ie $a5, b ++add.d $a5, $a5, $tp ++ ++.section .tbss,"awT",@nobits ++.globl a ++.zero 8 ++a: ++.zero 4 ++b: +diff --git a/lld/test/ELF/loongarch-tls-ld.s b/lld/test/ELF/loongarch-tls-ld.s +new file mode 100644 +index 000000000..a5be3ad90 +--- /dev/null ++++ b/lld/test/ELF/loongarch-tls-ld.s +@@ -0,0 +1,89 @@ ++# REQUIRES: loongarch ++# RUN: rm -rf %t && split-file %s %t ++ ++## LoongArch psABI doesn't specify TLS relaxation. Though the code sequences are not ++## relaxed, dynamic relocations can be omitted for LD->LE relaxation. ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 --position-independent %t/a.s -o %t/a.32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %t/tga.s -o %t/tga.32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 --position-independent %t/a.s -o %t/a.64.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/tga.s -o %t/tga.64.o ++ ++## LA32 LD ++# RUN: ld.lld -shared %t/a.32.o -o %t/ld.32.so ++# RUN: llvm-readobj -r %t/ld.32.so | FileCheck --check-prefix=LD32-REL %s ++# RUN: llvm-readelf -x .got %t/ld.32.so | FileCheck --check-prefix=LD32-GOT %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/ld.32.so | FileCheck --check-prefixes=LD32 %s ++ ++## LA32 LD -> LE ++# RUN: ld.lld %t/a.32.o %t/tga.32.o -o %t/le.32 ++# RUN: llvm-readelf -r %t/le.32 | FileCheck --check-prefix=NOREL %s ++# RUN: llvm-readelf -x .got %t/le.32 | FileCheck --check-prefix=LE32-GOT %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/le.32 | FileCheck --check-prefixes=LE32 %s ++ ++## LA64 LD ++# RUN: ld.lld -shared %t/a.64.o -o %t/ld.64.so ++# RUN: llvm-readobj -r %t/ld.64.so | FileCheck --check-prefix=LD64-REL %s ++# RUN: llvm-readelf -x .got %t/ld.64.so | FileCheck --check-prefix=LD64-GOT %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/ld.64.so | FileCheck --check-prefixes=LD64 %s ++ ++## LA64 LD -> LE ++# RUN: ld.lld %t/a.64.o %t/tga.64.o -o %t/le.64 ++# RUN: llvm-readelf -r %t/le.64 | FileCheck --check-prefix=NOREL %s ++# RUN: llvm-readelf -x .got %t/le.64 | FileCheck --check-prefix=LE64-GOT %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t/le.64 | FileCheck --check-prefixes=LE64 %s ++ ++## a@dtprel = st_value(a) = 0 is a link-time constant. ++# LD32-REL: .rela.dyn { ++# LD32-REL-NEXT: 0x20280 R_LARCH_TLS_DTPMOD32 - 0x0 ++# LD32-REL-NEXT: } ++# LD32-GOT: section '.got': ++# LD32-GOT-NEXT: 0x00020280 00000000 00000000 ++ ++# LD64-REL: .rela.dyn { ++# LD64-REL-NEXT: 0x20400 R_LARCH_TLS_DTPMOD64 - 0x0 ++# LD64-REL-NEXT: } ++# LD64-GOT: section '.got': ++# LD64-GOT-NEXT: 0x00020400 00000000 00000000 00000000 00000000 ++ ++## LA32: &DTPMOD(a) - . = 0x20280 - 0x101cc: 0x10 pages, page offset 0x280 ++# LD32: 101cc: pcalau12i $a0, 16 ++# LD32-NEXT: addi.w $a0, $a0, 640 ++# LD32-NEXT: bl 44 ++ ++## LA64: &DTPMOD(a) - . = 0x20400 - 0x102e0: 0x10 pages, page offset 0x400 ++# LD64: 102e0: pcalau12i $a0, 16 ++# LD64-NEXT: addi.d $a0, $a0, 1024 ++# LD64-NEXT: bl 40 ++ ++# NOREL: no relocations ++ ++## a is local - its DTPMOD/DTPREL slots are link-time constants. ++## a@dtpmod = 1 (main module) ++# LE32-GOT: section '.got': ++# LE32-GOT-NEXT: 0x00030120 01000000 00000000 ++ ++# LE64-GOT: section '.got': ++# LE64-GOT-NEXT: 0x000301d8 01000000 00000000 00000000 00000000 ++ ++## LA32: DTPMOD(.LANCHOR0) - . = 0x30120 - 0x20114: 0x10 pages, page offset 0x120 ++# LE32: 20114: pcalau12i $a0, 16 ++# LE32-NEXT: addi.w $a0, $a0, 288 ++# LE32-NEXT: bl 4 ++ ++## LA64: DTPMOD(.LANCHOR0) - . = 0x301d8 - 0x201c8: 0x10 pages, page offset 0x1d8 ++# LE64: 201c8: pcalau12i $a0, 16 ++# LE64-NEXT: addi.d $a0, $a0, 472 ++# LE64-NEXT: bl 4 ++ ++#--- a.s ++la.tls.ld $a0, .LANCHOR0 ++bl %plt(__tls_get_addr) ++ ++.section .tbss,"awT",@nobits ++.set .LANCHOR0, . + 0 ++.zero 8 ++ ++#--- tga.s ++.globl __tls_get_addr ++__tls_get_addr: +diff --git a/lld/test/ELF/loongarch-tls-le.s b/lld/test/ELF/loongarch-tls-le.s +new file mode 100644 +index 000000000..a20d7d83b +--- /dev/null ++++ b/lld/test/ELF/loongarch-tls-le.s +@@ -0,0 +1,42 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc --filetype=obj --triple=loongarch32 %s -o %t.32.o ++# RUN: llvm-mc --filetype=obj --triple=loongarch64 %s -o %t.64.o ++ ++# RUN: ld.lld %t.32.o -o %t.32 ++# RUN: llvm-nm -p %t.32 | FileCheck --check-prefixes=NM %s ++# RUN: llvm-objdump -d --no-show-raw-insn %t.32 | FileCheck --check-prefixes=LE %s ++ ++# RUN: ld.lld %t.64.o -o %t.64 ++# RUN: llvm-objdump -d --no-show-raw-insn %t.64 | FileCheck --check-prefixes=LE %s ++ ++# RUN: not ld.lld -shared %t.32.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR --implicit-check-not=error: ++ ++# ERR: error: relocation R_LARCH_TLS_LE_HI20 against .LANCHOR0 cannot be used with -shared ++# ERR: error: relocation R_LARCH_TLS_LE_LO12 against .LANCHOR0 cannot be used with -shared ++# ERR: error: relocation R_LARCH_TLS_LE_HI20 against a cannot be used with -shared ++# ERR: error: relocation R_LARCH_TLS_LE_LO12 against a cannot be used with -shared ++ ++# NM: {{0*}}00000008 b .LANCHOR0 ++# NM: {{0*}}00000800 B a ++ ++## .LANCHOR0@tprel = 8 ++## a@tprel = 0x800 ++# LE: lu12i.w $a0, 0 ++# LE-NEXT: ori $a0, $a0, 8 ++# LE-NEXT: lu12i.w $a1, 0 ++# LE-NEXT: ori $a1, $a1, 2048 ++# LE-EMPTY: ++ ++.text ++_start: ++la.tls.le $a0, .LANCHOR0 ++la.tls.le $a1, a ++ ++.section .tbss,"awT",@nobits ++.space 8 ++.LANCHOR0: ++.space 0x800-8 ++.globl a ++a: ++.zero 4 +diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py +index 96a1d6525..1b1f1e781 100644 +--- a/lld/test/lit.cfg.py ++++ b/lld/test/lit.cfg.py +@@ -68,6 +68,7 @@ llvm_config.feature_config( + 'ARM': 'arm', + 'AVR': 'avr', + 'Hexagon': 'hexagon', ++ 'LoongArch': 'loongarch', + 'Mips': 'mips', + 'MSP430': 'msp430', + 'PowerPC': 'ppc', diff --git a/lld.spec b/lld.spec index d82e554..9e19926 100644 --- a/lld.spec +++ b/lld.spec @@ -1,4 +1,4 @@ -%define anolis_release 1 +%define anolis_release 2 %global toolchain clang %undefine _include_frame_pointers @@ -26,6 +26,7 @@ Patch2: 0001-lld-Use-installed-llvm_gtest-in-standalone-builds.patch # Bundle libunwind header need during build for MachO support Patch1: 0002-PATCH-lld-Import-compact_unwind_encoding.h-from-libu.patch +Patch3: 0001-backport-LoongArch-patches.patch BuildRequires: clang BuildRequires: cmake @@ -141,6 +142,9 @@ fi %doc README.md CODE_OWNERS.TXT docs/README.txt docs/ReleaseNotes.rst %changelog +* Thu Aug 17 2023 Chen Li - 16.0.6-2 +- Backport LoongArch patches from llvmorg-17.0.0-rc2 + * Tue Aug 8 2023 Funda Wang - 16.0.6-1 - New version 16.0.6 -- Gitee