diff --git a/0001-Support-LoongArch.patch b/0001-Support-LoongArch.patch new file mode 100644 index 0000000000000000000000000000000000000000..b9d6c2f4cc8e25942f5582a3a12befb899e9faee --- /dev/null +++ b/0001-Support-LoongArch.patch @@ -0,0 +1,1710 @@ +diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp +new file mode 100644 +index 000000000..cf2e3b68a +--- /dev/null ++++ b/lld/ELF/Arch/LoongArch.cpp +@@ -0,0 +1,449 @@ ++//===- LoongArch.cpp ++//--------------------------------------------------------===// ++// ++// The LLVM Linker ++// ++// This file is distributed under the University of Illinois Open Source ++// License. See LICENSE.TXT for details. ++// ++//===----------------------------------------------------------------------===// ++ ++#include "InputFiles.h" ++#include "OutputSections.h" ++#include "Symbols.h" ++#include "SyntheticSections.h" ++#include "Target.h" ++#include "Thunks.h" ++#include "lld/Common/ErrorHandler.h" ++#include "llvm/Object/ELF.h" ++#include "llvm/Support/Endian.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 { ++#define REL_STACK_MAX_SIZE 16 ++struct RelStack { ++ uint64_t stack[REL_STACK_MAX_SIZE] = {}; ++ int top = -1; ++ void push_back(uint64_t e) { ++ if (top < REL_STACK_MAX_SIZE) { ++ top++; ++ stack[top] = e; ++ } else { ++ report_fatal_error("stack is overflow, top = " + Twine(top)); ++ } ++ } ++ ++ uint64_t pop_back_val() { ++ uint64_t e; ++ if (top >= 0) { ++ e = stack[top]; ++ top--; ++ } else { ++ report_fatal_error("stack is empty, top = " + Twine(top)); ++ } ++ return e; ++ } ++}; ++// The lld multi-thread is used to speed up link procedure. The lld will ++// create a thread for every input section to handle relocation. So we ++// must use thread-safe stack for every work thread. ++__thread RelStack relStack; ++ ++template class LoongArch final : public TargetInfo { ++public: ++ LoongArch(); ++ uint32_t calcEFlags() const override; ++ RelExpr getRelExpr(RelType type, const Symbol &s, ++ const uint8_t *loc) const override; ++ RelType getDynRel(RelType type) const override; ++ void writeGotHeader(uint8_t *buf) const override; ++ void writeGotPlt(uint8_t *buf, const Symbol &s) const override; ++ void writePltHeader(uint8_t *buf) const override; ++ void writePlt(uint8_t *buf, const Symbol &sym, ++ uint64_t pltEntryAddr) const override; ++ void relocate(uint8_t *loc, const Relocation &rel, ++ uint64_t val) const override; ++}; ++} // namespace ++ ++template LoongArch::LoongArch() { ++ // .got[0] = _DYNAMIC ++ gotHeaderEntriesNum = 1; ++ // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map ++ gotPltHeaderEntriesNum = 2; ++ defaultMaxPageSize = 65536; ++ gotEntrySize = sizeof(typename ELFT::uint); ++ pltEntrySize = 16; ++ pltHeaderSize = 32; ++ copyRel = R_LARCH_COPY; ++ pltRel = R_LARCH_JUMP_SLOT; ++ relativeRel = R_LARCH_RELATIVE; ++ // _GLOBAL_OFFSET_TABLE_ is relative to .got ++ gotBaseSymInGotPlt = false; ++ ++ if (ELFT::Is64Bits) { ++ symbolicRel = R_LARCH_64; ++ tlsGotRel = R_LARCH_TLS_TPREL64; ++ tlsModuleIndexRel = R_LARCH_TLS_DTPMOD64; ++ tlsOffsetRel = R_LARCH_TLS_DTPREL64; ++ defaultImageBase = 0x120000000; ++ } else { ++ symbolicRel = R_LARCH_32; ++ tlsGotRel = R_LARCH_TLS_TPREL32; ++ tlsModuleIndexRel = R_LARCH_TLS_DTPMOD32; ++ tlsOffsetRel = R_LARCH_TLS_DTPREL32; ++ } ++ gotRel = symbolicRel; ++} ++ ++template static TargetInfo *getTargetInfo() { ++ static LoongArch target; ++ return ⌖ ++} ++ ++TargetInfo *elf::getLoongArch32TargetInfo() { return getTargetInfo(); } ++TargetInfo *elf::getLoongArch64TargetInfo() { return getTargetInfo(); } ++ ++template ++RelExpr LoongArch::getRelExpr(RelType type, const Symbol &s, ++ const uint8_t *loc) const { ++ switch (type) { ++ case R_LARCH_64: ++ case R_LARCH_32: ++ return R_ABS; ++ case R_LARCH_SOP_PUSH_PCREL: ++ return R_PC; ++ case R_LARCH_SOP_PUSH_TLS_GOT: ++ return R_GOT_OFF; ++ case R_LARCH_SOP_PUSH_TLS_GD: ++ return R_TLSGD_GOT; ++ case R_LARCH_SOP_PUSH_TLS_TPREL: ++ return R_TPREL; ++ case R_LARCH_SOP_PUSH_GPREL: ++ return R_LARCH_GOTREL; ++ case R_LARCH_SOP_PUSH_PLT_PCREL: ++ return R_PLT_PC; ++ default: ++ return R_LARCH_ABS; ++ } ++} ++ ++template static uint32_t getEFlags(InputFile *F) { ++ return cast>(F)->getObj().getHeader().e_flags; ++} ++ ++static Twine getAbiName(uint32_t eflags) { ++ switch (eflags) { ++ case EF_LARCH_ABI_LP64: ++ return Twine("LP64"); ++ case EF_LARCH_ABI_LP32: ++ return Twine("LP32"); ++ case EF_LARCH_ABI_LPX32: ++ return Twine("LPX32"); ++ default: ++ return Twine("Unknown ABI"); ++ } ++} ++ ++template uint32_t LoongArch::calcEFlags() const { ++ assert(!ctx->objectFiles.empty()); ++ ++ uint32_t target = getEFlags(ctx->objectFiles.front()); ++ ++ for (InputFile *f : ctx->objectFiles) { ++ uint32_t eflags = getEFlags(f); ++ if (eflags != EF_LARCH_ABI_LP64 && eflags != EF_LARCH_ABI_LP32 && ++ eflags != EF_LARCH_ABI_LPX32) ++ error(toString(f) + ": unrecognized e_flags: " + Twine(eflags)); ++ if (eflags != target) ++ error(toString(f) + ": ABI '" + getAbiName(eflags) + ++ "' is incompatible with target ABI '" + getAbiName(target) + "'"); ++ } ++ ++ return target; ++} ++ ++template RelType LoongArch::getDynRel(RelType type) const { ++ if (type == R_LARCH_32 || type == R_LARCH_64) ++ return type; ++ return R_LARCH_NONE; ++} ++ ++template void LoongArch::writeGotHeader(uint8_t *buf) const { ++ if (ELFT::Is64Bits) ++ write64le(buf, mainPart->dynamic->getVA()); ++ else ++ write32le(buf, mainPart->dynamic->getVA()); ++} ++ ++template ++void LoongArch::writeGotPlt(uint8_t *buf, const Symbol &s) const { ++ if (ELFT::Is64Bits) ++ write64le(buf, in.plt->getVA()); ++ else ++ write32le(buf, in.plt->getVA()); ++} ++ ++/* Add 0x800 to maintain the sign bit */ ++static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } ++static uint32_t lo12(uint32_t val) { return val & 0xfff; } ++ ++template void LoongArch::writePltHeader(uint8_t *buf) const { ++ uint32_t offset = in.gotPlt->getVA() - in.plt->getVA(); ++ ++ /* pcaddu12i $t2, %hi(%pcrel(.got.plt)) ++ sub.[wd] $t1, $t1, $t3 ++ ld.[wd] $t3, $t2, %lo(%pcrel(.got.plt)) # _dl_runtime_resolve ++ addi.[wd] $t1, $t1, -(PLT_HEADER_SIZE + 12) + 4 ++ addi.[wd] $t0, $t2, %lo(%pcrel(.got.plt)) ++ srli.[wd] $t1, $t1, log2(16 / GOT_ENTRY_SIZE) ++ ld.[wd] $t0, $t0, GOT_ENTRY_SIZE ++ jirl $r0, $t3, 0 */ ++ ++ if (ELFT::Is64Bits) { ++ write32le(buf + 0, 0x1c00000e | hi20(offset) << 5); ++ write32le(buf + 4, 0x0011bdad); ++ write32le(buf + 8, 0x28c001cf | lo12(offset) << 10); ++ write32le(buf + 12, 0x02c001ad | ((-(pltHeaderSize + 12) + 4) & 0xfff) ++ << 10); ++ write32le(buf + 16, 0x02c001cc | lo12(offset) << 10); ++ write32le(buf + 20, 0x004501ad | 0x400); ++ write32le(buf + 24, 0x28c0018c | gotEntrySize << 10); ++ write32le(buf + 28, 0x4c0001e0); ++ } else { ++ write32le(buf + 0, 0x1c00000e | hi20(offset) << 5); ++ write32le(buf + 4, 0x00113dad); ++ write32le(buf + 8, 0x288001cf | lo12(offset) << 10); ++ write32le(buf + 12, 0x028001ad | ((-(pltHeaderSize + 12)) & 0xfff) << 10); ++ write32le(buf + 16, 0x028001cc | lo12(offset) << 10); ++ write32le(buf + 20, 0x004481ad | 0x800); ++ write32le(buf + 24, 0x2880018c | gotEntrySize << 10); ++ write32le(buf + 28, 0x4c0001e0); ++ } ++ ++ return; ++} ++ ++template ++void LoongArch::writePlt(uint8_t *buf, const Symbol &sym, ++ uint64_t pltEntryAddr) const { ++ uint32_t offset = sym.getGotPltVA() - pltEntryAddr; ++ ++ /* pcaddu12i $t3, %hi(%pcrel(.got.plt entry)) ++ ld.[wd] $t3, $t3, %lo(%pcrel(.got.plt entry)) ++ pcaddu12i $t1, 0 ++ jirl $r0, $t3, 0 */ ++ ++ write32le(buf, 0x1c00000f | hi20(offset) << 5); ++ if (ELFT::Is64Bits) ++ write32le(buf + 4, 0x28c001ef | lo12(offset) << 10); ++ else ++ write32le(buf + 4, 0x288001ef | lo12(offset) << 10); ++ write32le(buf + 8, 0x1c00000d); ++ write32le(buf + 12, 0x4c0001e0); ++ ++ return; ++} ++ ++// Extract bits v[hi:lo], where range is inclusive, and hi must be < 63. ++static uint32_t extractBits(uint64_t v, uint32_t hi, uint32_t lo) { ++ return (v & ((1ULL << (hi + 1)) - 1)) >> lo; ++} ++ ++// Clean bits v[hi:lo] to 0, where range is inclusive, and hi must be ++// < 32. ++static uint32_t cleanInstrImm(uint32_t v, uint32_t hi, uint32_t lo) { ++ return v & ~((((1ULL << (hi + 1)) - 1) >> lo) << lo); ++} ++ ++template ++void LoongArch::relocate(uint8_t *loc, const Relocation &rel, ++ uint64_t val) const { ++ switch (rel.type) { ++ case R_LARCH_32: ++ write32le(loc, val); ++ break; ++ case R_LARCH_TLS_DTPREL32: ++ write32le(loc, val); ++ break; ++ case R_LARCH_64: ++ write64le(loc, val); ++ return; ++ case R_LARCH_TLS_DTPREL64: ++ write64le(loc, val); ++ break; ++ case R_LARCH_TLS_DTPMOD32: ++ write32le(loc, val); ++ break; ++ case R_LARCH_TLS_DTPMOD64: ++ write64le(loc, val); ++ break; ++ case R_LARCH_MARK_LA: ++ case R_LARCH_MARK_PCREL: ++ case R_LARCH_NONE: ++ break; ++ case R_LARCH_SOP_PUSH_PCREL: ++ case R_LARCH_SOP_PUSH_ABSOLUTE: ++ case R_LARCH_SOP_PUSH_GPREL: ++ case R_LARCH_SOP_PUSH_TLS_TPREL: ++ case R_LARCH_SOP_PUSH_TLS_GOT: ++ case R_LARCH_SOP_PUSH_TLS_GD: ++ case R_LARCH_SOP_PUSH_PLT_PCREL: ++ relStack.push_back(val); ++ break; ++ case R_LARCH_SOP_PUSH_DUP: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ relStack.push_back(opr1); ++ relStack.push_back(opr1); ++ } break; ++ case R_LARCH_SOP_ASSERT: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ assert(opr1 == 0 && "R_LARCH_SOP_ASSERT relocation type assert fail."); ++ } break; ++ case R_LARCH_SOP_NOT: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ relStack.push_back(!opr1); ++ } break; ++ case R_LARCH_SOP_SUB: { ++ uint64_t opr2 = relStack.pop_back_val(); ++ uint64_t opr1 = relStack.pop_back_val(); ++ relStack.push_back(opr1 - opr2); ++ } break; ++ case R_LARCH_SOP_SL: { ++ uint64_t opr2 = relStack.pop_back_val(); ++ uint64_t opr1 = relStack.pop_back_val(); ++ relStack.push_back(opr1 << opr2); ++ } break; ++ case R_LARCH_SOP_SR: { ++ uint64_t opr2 = relStack.pop_back_val(); ++ uint64_t opr1 = relStack.pop_back_val(); ++ relStack.push_back((int64_t)opr1 >> opr2); ++ } break; ++ case R_LARCH_SOP_ADD: { ++ uint64_t opr2 = relStack.pop_back_val(); ++ uint64_t opr1 = relStack.pop_back_val(); ++ relStack.push_back(opr1 + opr2); ++ } break; ++ case R_LARCH_SOP_AND: { ++ uint64_t opr2 = relStack.pop_back_val(); ++ uint64_t opr1 = relStack.pop_back_val(); ++ relStack.push_back(opr1 & opr2); ++ } break; ++ case R_LARCH_SOP_IF_ELSE: { ++ uint64_t opr3 = relStack.pop_back_val(); ++ uint64_t opr2 = relStack.pop_back_val(); ++ uint64_t opr1 = relStack.pop_back_val(); ++ relStack.push_back(opr1 ? opr2 : opr3); ++ } break; ++ case R_LARCH_SOP_POP_32_S_10_5: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ checkInt(loc, static_cast(opr1), 5, rel); ++ uint32_t imm10_5 = extractBits(opr1, 4, 0) << 10; ++ uint32_t ins = cleanInstrImm(read32le(loc), 14, 10); ++ write32le(loc, ins | imm10_5); ++ } break; ++ case R_LARCH_SOP_POP_32_S_10_12: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ checkInt(loc, static_cast(opr1), 12, rel); ++ uint32_t imm10_12 = extractBits(opr1, 11, 0) << 10; ++ uint32_t ins = cleanInstrImm(read32le(loc), 21, 10); ++ write32le(loc, ins | imm10_12); ++ } break; ++ case R_LARCH_SOP_POP_32_S_10_16: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ checkInt(loc, static_cast(opr1), 16, rel); ++ uint32_t imm10_16 = extractBits(opr1, 15, 0) << 10; ++ uint32_t ins = cleanInstrImm(read32le(loc), 25, 10); ++ write32le(loc, ins | imm10_16); ++ } break; ++ case R_LARCH_SOP_POP_32_S_10_16_S2: { ++ int64_t opr1 = (int64_t)relStack.pop_back_val(); ++ checkInt(loc, static_cast(opr1), 18, rel); ++ checkAlignment(loc, opr1, 4, rel); ++ uint32_t imm10_16 = extractBits(opr1, 17, 2) << 10; ++ uint32_t ins = cleanInstrImm(read32le(loc), 25, 10); ++ write32le(loc, ins | imm10_16); ++ } break; ++ case R_LARCH_SOP_POP_32_U_10_12: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ checkUInt(loc, opr1, 12, rel); ++ uint32_t imm10_12 = extractBits(opr1, 11, 0) << 10; ++ uint32_t ins = cleanInstrImm(read32le(loc), 21, 10); ++ write32le(loc, ins | imm10_12); ++ } break; ++ case R_LARCH_SOP_POP_32_S_5_20: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ checkInt(loc, static_cast(opr1), 20, rel); ++ uint32_t imm5_20 = extractBits(opr1, 19, 0) << 5; ++ uint32_t ins = cleanInstrImm(read32le(loc), 24, 5); ++ write32le(loc, ins | imm5_20); ++ } break; ++ case R_LARCH_SOP_POP_32_S_0_5_10_16_S2: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ checkInt(loc, static_cast(opr1), 23, rel); ++ checkAlignment(loc, opr1, 4, rel); ++ uint32_t imm0_5 = extractBits(opr1, 22, 18); ++ uint32_t imm10_16 = extractBits(opr1, 17, 2) << 10; ++ uint32_t ins = cleanInstrImm(read32le(loc), 4, 0); ++ ins = cleanInstrImm(ins, 25, 10); ++ write32le(loc, ins | imm0_5 | imm10_16); ++ } break; ++ case R_LARCH_SOP_POP_32_S_0_10_10_16_S2: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ checkInt(loc, static_cast(opr1), 28, rel); ++ checkAlignment(loc, opr1, 4, rel); ++ uint32_t imm0_10 = extractBits(opr1, 27, 18); ++ uint32_t imm10_16 = extractBits(opr1, 17, 2) << 10; ++ uint32_t ins = cleanInstrImm(read32le(loc), 25, 0); ++ write32le(loc, ins | imm0_10 | imm10_16); ++ } break; ++ case R_LARCH_SOP_POP_32_U: { ++ uint64_t opr1 = relStack.pop_back_val(); ++ checkUInt(loc, opr1, 32, rel); ++ write32le(loc, (uint32_t)opr1); ++ } break; ++ case R_LARCH_ADD8: ++ *loc += val; ++ break; ++ case R_LARCH_ADD16: ++ write16le(loc, read16le(loc) + val); ++ break; ++ case R_LARCH_ADD24: ++ write32le(loc, (read32le(loc) | *(loc + 2) << 16) + val); ++ break; ++ case R_LARCH_ADD32: ++ write32le(loc, read32le(loc) + val); ++ break; ++ case R_LARCH_ADD64: ++ write64le(loc, read64le(loc) + val); ++ break; ++ case R_LARCH_SUB8: ++ *loc -= val; ++ break; ++ case R_LARCH_SUB16: ++ write16le(loc, read16le(loc) - val); ++ break; ++ case R_LARCH_SUB24: ++ write16le(loc, (read16le(loc) | *(loc + 2) << 16) - val); ++ break; ++ case R_LARCH_SUB32: ++ write32le(loc, read32le(loc) - val); ++ break; ++ case R_LARCH_SUB64: ++ write64le(loc, read64le(loc) - val); ++ break; ++ // GNU C++ vtable hierarchy ++ case R_LARCH_GNU_VTINHERIT: ++ // GNU C++ vtable member usage ++ case R_LARCH_GNU_VTENTRY: ++ break; ++ default: ++ error(getErrorLocation(loc) + "unrecognized reloc " + Twine(rel.type)); ++ } ++} +diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt +index b37035d3e..6d12da1f4 100644 +--- a/lld/ELF/CMakeLists.txt ++++ b/lld/ELF/CMakeLists.txt +@@ -13,6 +13,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 58d863776..20faef32e 100644 +--- a/lld/ELF/Driver.cpp ++++ b/lld/ELF/Driver.cpp +@@ -157,6 +157,7 @@ static std::tuple parseEmulation(StringRef emul) { + .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) + .Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9}) + .Case("msp430elf", {ELF32LEKind, EM_MSP430}) ++ .Case("elf64loongarch", {ELF64LEKind, EM_LOONGARCH}) + .Default({ELFNoneKind, EM_NONE}); + + if (ret.first == ELFNoneKind) +@@ -999,7 +1000,7 @@ 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; ++ m == EM_PPC64 || m == EM_RISCV || m == EM_X86_64 || m == EM_LOONGARCH; + } + + static void parseClangOption(StringRef opt, const Twine &msg) { +diff --git a/lld/ELF/EhFrame.cpp b/lld/ELF/EhFrame.cpp +index f2fc99fe3..174b2abd3 100644 +--- a/lld/ELF/EhFrame.cpp ++++ b/lld/ELF/EhFrame.cpp +@@ -37,6 +37,7 @@ class EhReader { + public: + EhReader(InputSectionBase *s, ArrayRef d) : isec(s), d(d) {} + uint8_t getFdeEncoding(); ++ void setPcRelativeEncoding(uint8_t *buf); + bool hasLSDA(); + + private: +@@ -131,6 +132,10 @@ uint8_t elf::getFdeEncoding(EhSectionPiece *p) { + return EhReader(p->sec, p->data()).getFdeEncoding(); + } + ++void elf::setPcRelativeEncoding(EhSectionPiece *p, uint8_t *buf) { ++ EhReader(p->sec, p->data()).setPcRelativeEncoding(buf); ++} ++ + bool elf::hasLSDA(const EhSectionPiece &p) { + return EhReader(p.sec, p.data()).hasLSDA(); + } +@@ -157,7 +162,35 @@ StringRef EhReader::getAugmentation() { + return aug; + } + ++void EhReader::setPcRelativeEncoding(uint8_t *buf) { ++ const uint8_t *start = d.begin(); ++ StringRef aug = getAugmentation(); ++ uint8_t *p = buf + (d.begin() - start); ++ for (char c : aug) { ++ if (c == 'R') { ++ *p = DW_EH_PE_pcrel | DW_EH_PE_sdata8; ++ readByte(); ++ p++; ++ } else if (c == 'z') { ++ skipLeb128(); ++ p += (d.begin() - start) - (p - buf); ++ } else if (c == 'L') { ++ *p = DW_EH_PE_pcrel | DW_EH_PE_sdata8; ++ readByte(); ++ p++; ++ } else if (c == 'P') { ++ *p = DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata8; ++ skipAugP(); ++ p += (d.begin() - start) - (p - buf); ++ } else if (c != 'B' && c != 'S') { ++ failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug); ++ } ++ } ++} ++ + uint8_t EhReader::getFdeEncoding() { ++ if ((config->shared || config->pie) && config->emachine == EM_LOONGARCH) ++ return DW_EH_PE_pcrel | DW_EH_PE_sdata8; + // We only care about an 'R' value, but other records may precede an 'R' + // record. Unfortunately records are not in TLV (type-length-value) format, + // so we need to teach the linker how to skip records for each type. +diff --git a/lld/ELF/EhFrame.h b/lld/ELF/EhFrame.h +index 3b144648c..19010e493 100644 +--- a/lld/ELF/EhFrame.h ++++ b/lld/ELF/EhFrame.h +@@ -17,6 +17,7 @@ class InputSectionBase; + struct EhSectionPiece; + + uint8_t getFdeEncoding(EhSectionPiece *p); ++void setPcRelativeEncoding(EhSectionPiece *p, uint8_t *buf); + bool hasLSDA(const EhSectionPiece &p); + } // namespace elf + } // namespace lld +diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp +index 8fe36eca6..aa279bd48 100644 +--- a/lld/ELF/InputSection.cpp ++++ b/lld/ELF/InputSection.cpp +@@ -597,6 +597,7 @@ static int64_t getTlsTpOffset(const Symbol &s) { + // data and 0xf000 of the program's TLS segment. + return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)) - 0x7000; + case EM_RISCV: ++ case EM_LOONGARCH: + return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)); + + // Variant 2. +@@ -616,6 +617,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, + const Symbol &sym, RelExpr expr) { + switch (expr) { + case R_ABS: ++ case R_LARCH_ABS: + case R_DTPREL: + case R_RELAX_TLS_LD_TO_LE_ABS: + case R_RELAX_GOT_PC_NOPIC: +@@ -637,6 +639,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, + case R_GOTREL: + case R_PPC64_RELAX_TOC: + return sym.getVA(a) - in.got->getVA(); ++ case R_LARCH_GOTREL: ++ return sym.getGotVA() - in.got->getVA(); + case R_GOTPLTREL: + return sym.getVA(a) - in.gotPlt->getVA(); + case R_GOTPLT: +@@ -902,7 +906,7 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef rels) { + // R_ABS/R_DTPREL and some other relocations can be used from non-SHF_ALLOC + // sections. + if (expr == R_ABS || expr == R_DTPREL || expr == R_GOTPLTREL || +- expr == R_RISCV_ADD) { ++ expr == R_RISCV_ADD || expr == R_LARCH_ABS) { + target.relocateNoSym(bufLoc, type, SignExtend64(sym.getVA(addend))); + continue; + } +diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp +index 53af34ef2..11d4c778e 100644 +--- a/lld/ELF/Relocations.cpp ++++ b/lld/ELF/Relocations.cpp +@@ -200,7 +200,7 @@ static bool needsPlt(RelExpr expr) { + static bool needsGot(RelExpr expr) { + return oneof(expr); ++ R_AARCH64_GOT_PAGE, R_LARCH_GOTREL>(expr); + } + + // True if this expression is of the form Sym - X, where X is a position in the +@@ -211,7 +211,6 @@ static bool isRelExpr(RelExpr expr) { + R_RISCV_PC_INDIRECT, R_PPC64_RELAX_GOT_PC>(expr); + } + +- + static RelExpr toPlt(RelExpr expr) { + switch (expr) { + case R_PPC64_CALL: +@@ -518,8 +517,7 @@ int64_t RelocationScanner::computeAddend(const RelTy &rel, RelExpr expr, + } + + // Custom error message if Sym is defined in a discarded section. +-template +-static std::string maybeReportDiscarded(Undefined &sym) { ++template static std::string maybeReportDiscarded(Undefined &sym) { + auto *file = dyn_cast_or_null>(sym.file); + if (!file || !sym.discardedSecIdx || + file->getSections()[sym.discardedSecIdx] != &InputSection::discarded) +@@ -931,9 +929,10 @@ static bool canDefineSymbolInExecutable(Symbol &sym) { + // If the symbol has default visibility the symbol defined in the + // executable will preempt it. + // Note that we want the visibility of the shared symbol itself, not +- // the visibility of the symbol in the output file we are producing. That is +- // why we use Sym.stOther. +- if ((sym.stOther & 0x3) == STV_DEFAULT) ++ // the visibility of the symbol in the output file we are producing. ++ // Use `!= STV_PROTECTED` but not `== STV_DEFAULT`. This is a partial backport ++ // from llvm-16. ++ if ((sym.stOther & 0x3) != STV_PROTECTED) + return true; + + // If we are allowed to break address equality of functions, defining +@@ -962,7 +961,8 @@ 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_LARCH_GOTREL, ++ R_LARCH_ABS>(e)) + return true; + + // These never do, except if the entire file is position dependent or if +@@ -1003,7 +1003,7 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, + // We set the final symbols values for linker script defined symbols later. + // They always can be computed as a link time constant. + if (sym.scriptDefined) +- return true; ++ return true; + + error("relocation " + toString(type) + " cannot refer to absolute symbol: " + + toString(sym) + getLocation(sec, sym, relOff)); +@@ -1049,6 +1049,11 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, + if (canWrite) { + RelType rel = target.getDynRel(type); + if (expr == R_GOT || (rel == target.symbolicRel && !sym.isPreemptible)) { ++ if (config->emachine == EM_LOONGARCH && rel == target.symbolicRel && ++ isa(sec)) { ++ sec.relocations.push_back({expr, type, offset, addend, &sym}); ++ return; ++ } + addRelativeReloc(sec, offset, sym, addend, expr, type); + return; + } else if (rel != 0) { +@@ -1140,6 +1145,12 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, + } + } + ++ if ((config->shared || config->pie) && config->emachine == EM_LOONGARCH && ++ (type == R_LARCH_32 || type == R_LARCH_64)) { ++ sec.relocations.push_back({expr, type, offset, addend, &sym}); ++ return; ++ } ++ + errorOrWarn("relocation " + toString(type) + " cannot be used against " + + (sym.getName().empty() ? "local symbol" + : "symbol '" + toString(sym) + "'") + +@@ -1194,13 +1205,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 +- // relaxation as well. +- bool toExecRelax = !config->shared && config->emachine != EM_ARM && +- config->emachine != EM_HEXAGON && +- config->emachine != EM_RISCV && +- !c.file->ppc64DisableTLSRelax; ++ // 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; + + // If we are producing an executable and the symbol is non-preemptable, it + // must be defined and the code sequence can be relaxed to use Local-Exec. +@@ -1215,8 +1226,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.relocations.push_back( +@@ -1404,10 +1414,10 @@ template void RelocationScanner::scanOne(RelTy *&i) { + // R_HEX_GD_PLT_B22_PCREL (call a@GDPLT) is transformed into + // call __tls_get_addr even if the symbol is non-preemptible. + if (!(config->emachine == EM_HEXAGON && +- (type == R_HEX_GD_PLT_B22_PCREL || +- type == R_HEX_GD_PLT_B22_PCREL_X || +- type == R_HEX_GD_PLT_B32_PCREL_X))) +- expr = fromPlt(expr); ++ (type == R_HEX_GD_PLT_B22_PCREL || ++ type == R_HEX_GD_PLT_B22_PCREL_X || ++ type == R_HEX_GD_PLT_B32_PCREL_X))) ++ expr = fromPlt(expr); + } else if (!isAbsoluteValue(sym)) { + expr = target.adjustGotPcExpr(type, addend, relocatedAddr); + } +diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h +index f70d255ba..4ec807b7c 100644 +--- a/lld/ELF/Relocations.h ++++ b/lld/ELF/Relocations.h +@@ -87,6 +87,8 @@ enum RelExpr { + R_AARCH64_TLSDESC_PAGE, + R_ARM_PCA, + R_ARM_SBREL, ++ R_LARCH_ABS, ++ R_LARCH_GOTREL, + R_MIPS_GOTREL, + R_MIPS_GOT_GP, + R_MIPS_GOT_GP_PC, +diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp +index 7fc50b293..15cc92815 100644 +--- a/lld/ELF/ScriptParser.cpp ++++ b/lld/ELF/ScriptParser.cpp +@@ -438,6 +438,7 @@ 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("elf64-loongarch", {ELF64LEKind, EM_LOONGARCH}) + .Default({ELFNoneKind, EM_NONE}); + } + +diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp +index b359c2e7b..c4bd414cf 100644 +--- a/lld/ELF/SyntheticSections.cpp ++++ b/lld/ELF/SyntheticSections.cpp +@@ -624,6 +624,10 @@ void EhFrameSection::writeTo(uint8_t *buf) { + for (CieRecord *rec : cieRecords) { + size_t cieOffset = rec->cie->outputOff; + writeCieFde(buf + cieOffset, rec->cie->data()); ++ // Make FDE, CIE LSDA and Personality pc relative. ++ if ((config->shared || config->pie) && config->emachine == EM_LOONGARCH) { ++ setPcRelativeEncoding(rec->cie, buf + cieOffset); ++ } + + for (EhSectionPiece *fde : rec->fdes) { + size_t off = fde->outputOff; +@@ -635,6 +639,17 @@ void EhFrameSection::writeTo(uint8_t *buf) { + } + } + ++ if ((config->shared || config->pie) && config->emachine == EM_LOONGARCH) { ++ for (EhInputSection *s : sections) { ++ // Make each relocation pc relative in .eh_frame sections. ++ for (Relocation *rel = s->relocations.begin(); ++ rel != s->relocations.end(); rel++) { ++ if (rel->type == R_LARCH_64 || rel->type == R_LARCH_32) ++ rel->expr = R_PC; ++ } ++ } ++ } ++ + // Apply relocations. .eh_frame section contents are not contiguous + // in the output buffer, but relocateAlloc() still works because + // getOffset() takes care of discontiguous section pieces. +diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp +index 7bc5121ea..da8e7785a 100644 +--- a/lld/ELF/Target.cpp ++++ b/lld/ELF/Target.cpp +@@ -62,6 +62,10 @@ TargetInfo *elf::getTarget() { + return getAVRTargetInfo(); + case EM_HEXAGON: + return getHexagonTargetInfo(); ++ case EM_LOONGARCH: ++ if (config->ekind == ELF32LEKind) ++ return getLoongArch32TargetInfo(); ++ return getLoongArch64TargetInfo(); + case EM_MIPS: + switch (config->ekind) { + case ELF32LEKind: +diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h +index 14b1f53c6..72a6de5ab 100644 +--- a/lld/ELF/Target.h ++++ b/lld/ELF/Target.h +@@ -80,8 +80,7 @@ public: + uint8_t stOther) const; + + // Return true if we can reach dst from src with RelType type. +- virtual bool inBranchRange(RelType type, uint64_t src, +- uint64_t dst) const; ++ virtual bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const; + + virtual void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const = 0; +@@ -180,6 +179,8 @@ TargetInfo *getAMDGPUTargetInfo(); + TargetInfo *getARMTargetInfo(); + TargetInfo *getAVRTargetInfo(); + TargetInfo *getHexagonTargetInfo(); ++TargetInfo *getLoongArch32TargetInfo(); ++TargetInfo *getLoongArch64TargetInfo(); + TargetInfo *getMSP430TargetInfo(); + TargetInfo *getPPC64TargetInfo(); + TargetInfo *getPPCTargetInfo(); +diff --git a/lld/test/ELF/Inputs/loongarch.s b/lld/test/ELF/Inputs/loongarch.s +new file mode 100644 +index 000000000..7ba110fbd +--- /dev/null ++++ b/lld/test/ELF/Inputs/loongarch.s +@@ -0,0 +1,3 @@ ++.global _start ++_start: ++ nop +diff --git a/lld/test/ELF/loongarch-branch.s b/lld/test/ELF/loongarch-branch.s +new file mode 100644 +index 000000000..7c27b9c4e +--- /dev/null ++++ b/lld/test/ELF/loongarch-branch.s +@@ -0,0 +1,42 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t.o ++# RUN: obj2yaml %t.o | FileCheck %s --check-prefix=CHECK-RELOC ++ ++# CHECK-RELOC: Relocations: ++# CHECK-RELOC: - Symbol: foo ++# CHECK-RELOC: Type: R_LARCH_SOP_PUSH_PCREL ++# CHECK-RELOC: - Type: R_LARCH_SOP_POP_32_S_10_16_S2 ++# CHECK-RELOC: - Offset: 0x4 ++# CHECK-RELOC: Symbol: bar ++# CHECK-RELOC: Type: R_LARCH_SOP_PUSH_PCREL ++# CHECK-RELOC: - Offset: 0x4 ++# CHECK-RELOC: Type: R_LARCH_SOP_POP_32_S_10_16_S2 ++ ++# RUN: ld.lld %t.o --defsym foo=_start+0x4 --defsym bar=_start -o %t ++# RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=OBJ ++ ++# OBJ: 00 04 00 58 beq $zero, $zero, 4 ++# OBJ: 00 fc ff 5f bne $zero, $zero, -4 ++ ++# RUN: ld.lld %t.o --defsym foo=_start+0x1FFFC --defsym bar=_start+4-0x20000 \ ++# RUN: -o %t.limits ++# RUN: llvm-objdump -d %t.limits | FileCheck --check-prefix=LIMITS %s ++# LIMITS: 00 fc ff 59 beq $zero, $zero, 131068 ++# LIMITS-NEXT: 00 00 00 5e bne $zero, $zero, -131072 ++ ++# RUN: not ld.lld %t.o --defsym foo=_start+0x20000 \ ++# RUN: --defsym bar=_start+4-0x20004 -o /dev/null 2>&1 \ ++# RUN: | FileCheck --check-prefix=ERROR-RANGE %s ++# ERROR-RANGE: relocation R_LARCH_SOP_POP_32_S_10_16_S2 out of range: 131072 is not in [-131072, 131071] ++# ERROR-RANGE: relocation R_LARCH_SOP_POP_32_S_10_16_S2 out of range: -131076 is not in [-131072, 131071] ++ ++# RUN: not ld.lld %t.o --defsym foo=_start+1 --defsym bar=_start-1 \ ++# RUN: -o /dev/null 2>&1 | FileCheck --check-prefix=ERROR-ALIGN %s ++# ERROR-ALIGN: improper alignment for relocation R_LARCH_SOP_POP_32_S_10_16_S2: 0x1 is not aligned to 4 bytes ++# ERROR-ALIGN-NEXT: improper alignment for relocation R_LARCH_SOP_POP_32_S_10_16_S2: 0xFFFFFFFFFFFFFFFB is not aligned to 4 bytes ++ ++.global _start ++_start: ++ beq $r0, $r0, foo ++ bne $r0, $r0, bar +diff --git a/lld/test/ELF/loongarch-eflags-diff-abi-err.test b/lld/test/ELF/loongarch-eflags-diff-abi-err.test +new file mode 100644 +index 000000000..bae0f3085 +--- /dev/null ++++ b/lld/test/ELF/loongarch-eflags-diff-abi-err.test +@@ -0,0 +1,24 @@ ++# REQUIRES: loongarch ++ ++# RUN: yaml2obj --docnum 1 %s -o %t1.o ++# RUN: yaml2obj --docnum 2 %s -o %t2.o ++# RUN: not ld.lld %t1.o %t2.o -o %t 2>&1 | FileCheck %s ++# ++# CHECK: {{.*}}: ABI 'LPX32' is incompatible with target ABI 'LP32' ++# ++#t1.o ++--- !ELF ++FileHeader: ++ Class: ELFCLASS32 ++ Data: ELFDATA2LSB ++ Type: ET_REL ++ Machine: EM_LOONGARCH ++ Flags: [ EF_LARCH_ABI_LP32 ] ++# t2.o ++--- !ELF ++FileHeader: ++ Class: ELFCLASS32 ++ Data: ELFDATA2LSB ++ Type: ET_REL ++ Machine: EM_LOONGARCH ++ Flags: [ EF_LARCH_ABI_LPX32 ] +diff --git a/lld/test/ELF/loongarch-eflags-lp32.test b/lld/test/ELF/loongarch-eflags-lp32.test +new file mode 100644 +index 000000000..d081e7fb0 +--- /dev/null ++++ b/lld/test/ELF/loongarch-eflags-lp32.test +@@ -0,0 +1,29 @@ ++#.global _start ++#_start: ++# nop ++# ++# REQUIRES: loongarch ++# RUN: yaml2obj %s -o %t.o ++# RUN: ld.lld %t.o -o %t ++# RUN: llvm-readelf -h %t | FileCheck %s ++# Verify the LoongArch LP32 ABI. ++# CHECK: Flags: 0x1 ++# ++--- !ELF ++FileHeader: ++ Class: ELFCLASS32 ++ Data: ELFDATA2LSB ++ Type: ET_REL ++ Machine: EM_LOONGARCH ++ Flags: [ EF_LARCH_ABI_LP32 ] ++Sections: ++ - Name: .text ++ Type: SHT_PROGBITS ++ Flags: [ SHF_ALLOC, SHF_EXECINSTR ] ++ AddressAlign: 0x0000000000000010 ++ Content: '00004003' ++Symbols: ++ - Name: _start ++ Section: .text ++ Binding: STB_GLOBAL ++... +diff --git a/lld/test/ELF/loongarch-eflags-lp64.s b/lld/test/ELF/loongarch-eflags-lp64.s +new file mode 100644 +index 000000000..0775feeb4 +--- /dev/null ++++ b/lld/test/ELF/loongarch-eflags-lp64.s +@@ -0,0 +1,8 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %S/Inputs/loongarch.s -o %t2 ++# RUN: ld.lld %t2 %t -o %t3 ++# RUN: llvm-readelf -h %t3 | FileCheck %s ++# Verify the LoongArch LP64 ABI. ++# CHECK: Flags: 0x3, LP64 +diff --git a/lld/test/ELF/loongarch-eflags-lpx32.test b/lld/test/ELF/loongarch-eflags-lpx32.test +new file mode 100644 +index 000000000..27b2fc20f +--- /dev/null ++++ b/lld/test/ELF/loongarch-eflags-lpx32.test +@@ -0,0 +1,29 @@ ++#.global _start ++#_start: ++# nop ++# ++# REQUIRES: loongarch ++# RUN: yaml2obj %s -o %t.o ++# RUN: ld.lld %t.o -o %t ++# RUN: llvm-readelf -h %t | FileCheck %s ++# Verify the LoongArch LPX32 ABI. ++# CHECK: Flags: 0x2 ++# ++--- !ELF ++FileHeader: ++ Class: ELFCLASS32 ++ Data: ELFDATA2LSB ++ Type: ET_REL ++ Machine: EM_LOONGARCH ++ Flags: [ EF_LARCH_ABI_LPX32 ] ++Sections: ++ - Name: .text ++ Type: SHT_PROGBITS ++ Flags: [ SHF_ALLOC, SHF_EXECINSTR ] ++ AddressAlign: 0x0000000000000010 ++ Content: '00004003' ++Symbols: ++ - Name: _start ++ Section: .text ++ Binding: STB_GLOBAL ++... +diff --git a/lld/test/ELF/loongarch-got-reloc.s b/lld/test/ELF/loongarch-got-reloc.s +new file mode 100644 +index 000000000..a1df5c876 +--- /dev/null ++++ b/lld/test/ELF/loongarch-got-reloc.s +@@ -0,0 +1,67 @@ ++# REQUIRES: loongarch ++# Check la.got relocation calculation. In this case, la.global will be expanded to la.got. ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t.o ++# RUN: llvm-readobj --relocations %t.o | FileCheck -check-prefix=RELOC %s ++# RUN: ld.lld %t.o -o %t.exe ++# RUN: llvm-objdump --section-headers -t %t.exe | FileCheck -check-prefix=EXE_SYM %s ++# RUN: llvm-objdump -s --section=.got %t.exe | FileCheck -check-prefix=EXE_GOT %s ++# RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=EXE_DIS %s ++# RUN: llvm-readobj --relocations %t.exe | FileCheck -check-prefix=NORELOC %s ++ ++.text ++.globl _start ++_start: ++ la.global $r12, value ++ ++.data ++value: ++ .word 1 ++ ++# RELOC: Relocations [ ++# RELOC-NEXT: Section (3) .rela.text { ++# RELOC-NEXT: 0x0 R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x800 ++# RELOC-NEXT: 0x0 R_LARCH_SOP_PUSH_GPREL value 0x0 ++# RELOC-NEXT: 0x0 R_LARCH_SOP_ADD - 0x0 ++# RELOC-NEXT: 0x0 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# RELOC-NEXT: 0x0 R_LARCH_SOP_SR - 0x0 ++# RELOC-NEXT: 0x0 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x4 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_PUSH_GPREL value 0x0 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_ADD - 0x0 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x804 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_PUSH_GPREL value 0x0 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_ADD - 0x0 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# RELOC-NEXT: 0x4 R_LARCH_SOP_SR - 0x0 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# RELOC-NEXT: 0x4 R_LARCH_SOP_SL - 0x0 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_SUB - 0x0 ++# RELOC-NEXT: 0x4 R_LARCH_SOP_POP_32_S_10_12 - 0x0 ++# RELOC-NEXT: } ++# RELOC-NEXT: ] ++ ++# EXE_SYM: Sections: ++# EXE_SYM: Idx Name Size VMA Type ++# EXE_SYM: 2 .got 00000010 00000001200201d8 DATA ++# EXE_SYM: SYMBOL TABLE: ++# EXE_SYM: 00000001200201d8 l .got 0000000000000000 .hidden _GLOBAL_OFFSET_TABLE_ ++# ^---- .got ++ ++# EXE_GOT: Contents of section .got: ++# EXE_GOT-NEXT: 1200201d8 00000000 00000000 f0010320 01000000 ++# ^ ^---------value ++# +-- .dynamic address (if exist) ++ ++# pcaddu12i rd,(%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%gprel(symbol))>>12 ++# value_GotAddr=%gprel(synbol) = 0x1200201e0-0x1200201d8 = 8 ++# (0x1200201d8+0x800-0x1200101d0+8)>>12 = 16 ++# EXE_DIS: 1200101d0: 0c 02 00 1c pcaddu12i $r12, 16 ++ ++# ld.d rd,rd,%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%gprel(symbol)-((%pcrel( ++# _GLOBAL_OFFSET_TABLE_+4+0x800)+%gprel(symbol))>>12<<12) ++# (0x1200201d8+4-0x1200101d4)+8-((0x1200201d8+4+0x800-0x1200101d4)+8)>>12<<12 = 8 ++# EXE_DIS-NEXT: 1200101d4: 8c 41 c0 28 ld.d $r12, $r12, 16 ++ ++# NORELOC: Relocations [ ++# NORELOC-NEXT: ] +diff --git a/lld/test/ELF/loongarch-plt.s b/lld/test/ELF/loongarch-plt.s +new file mode 100644 +index 000000000..f8cbf35e6 +--- /dev/null ++++ b/lld/test/ELF/loongarch-plt.s +@@ -0,0 +1,83 @@ ++# REQUIRES: loongarch ++# RUN: echo '.globl bar, weak; .type bar,@function; .type weak,@function; bar: weak:' > %t1.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 -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*}}1200102f0 ++ ++## 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. ++# RELOC64: .rela.plt { ++# RELOC64-NEXT: 0x120030410 R_LARCH_JUMP_SLOT bar 0x0 ++# RELOC64-NEXT: 0x120030418 R_LARCH_JUMP_SLOT weak 0x0 ++# RELOC64-NEXT: } ++# GOTPLT64: section '.got.plt' ++# GOTPLT64-NEXT: 0x120030400 00000000 00000000 00000000 00000000 ++# GOTPLT64-NEXT: 0x120030410 f0020120 01000000 f0020120 01000000 ++ ++# DIS: <_start>: ++## foo - . = 0x1200102e0-0x1200102d0 = 16 ++# DIS-NEXT: 1200102d0: bl 16 ++## bar@plt - . = 0x120010310-0x1200102d4 = 60 ++# DIS-NEXT: 1200102d4: bl 60 ++## bar@plt - . = 0x120010310-0x1200102d8 = 56 ++# DIS-NEXT: 1200102d8: bl 56 ++## weak@plt - . = 0x120010320-0x1200102dc = 68 ++# DIS-NEXT: 1200102dc: bl 68 ++# DIS: : ++# DIS-NEXT: 1200102e0: jirl $zero, $ra, 0 ++ ++## 120030400 .got.plt ++## 1200102f0 .plt ++# DIS: Disassembly of section .plt: ++# DIS: <.plt>: ++## hi20(.got.plt - .plt + 0x800) = (0x120030400 - 0x1200102f0 + 0x800)>>12 = 0x20910 >> 12 = 0x20 ++# DIS-NEXT: pcaddu12i $r14, 32 ++# DIS-NEXT: sub.d $r13, $r13, $r15 ++## lo12(.got.plt - .plt) = (0x120030400 - 0x1200102f0) & 0xfff = 0x20110 & 0xfff = 0x110 ++# DIS64-NEXT: ld.d $r15, $r14, 272 ++# DIS64-NEXT: addi.d $r13, $r13, -40 ++## lo12(.got.plt - .plt) = (0x120030400 - 0x1200102f0) & 0xfff = 0x20110 & 0xfff = 0x110 ++# DIS64-NEXT: addi.d $r12, $r14, 272 ++# DIS64-NEXT: srli.d $r13, $r13, 1 ++# DIS64-NEXT: ld.d $r12, $r12, 8 ++# DIS-NEXT: jirl $zero, $r15, 0 ++ ++## hi20(&.got.plt[bar]-.) = (0x120030410 - 0x120010310 + 0x800) >> 12 = 0x20900 >> 12 = 0x20 ++# DIS: 120010310: pcaddu12i $r15, 32 ++## lo12(&.got.plt[bar]-.) = (0x120030410 - 0x120010310) & 0xfff = 0x20100 & 0xfff = 0x100 ++# DIS64-NEXT: ld.d $r15, $r15, 256 ++# DIS-NEXT: pcaddu12i $r13, 0 ++# DIS-NEXT: jirl $zero, $r15, 0 ++ ++## hi20(&.got.plt[weak]-.) = (0x120030418 - 0x120010320 + 0x800) >> 12 = 0x208f8 >> 12 = 0x20 ++# DIS: 120010320: pcaddu12i $r15, 32 ++## lo12(&.got.plt[weak]-.) = (0x120030418 - 0x120010320) & 0xfff = 0x200f8 & 0xfff = 0xf8 ++# DIS64-NEXT: ld.d $r15, $r15, 248 ++# DIS-NEXT: pcaddu12i $r13, 0 ++# DIS-NEXT: jirl $zero, $r15, 0 ++ ++.global _start, foo, bar ++.weak weak ++ ++_start: ++ bl foo ++ bl bar ++ bl bar@plt ++ bl weak ++ ++## foo is local and non-preemptale, no PLT is generated. ++foo: ++ jr $ra +diff --git a/lld/test/ELF/loongarch-relative.s b/lld/test/ELF/loongarch-relative.s +new file mode 100644 +index 000000000..cb1456054 +--- /dev/null ++++ b/lld/test/ELF/loongarch-relative.s +@@ -0,0 +1,59 @@ ++// XFAIL: loongarch ++// REQUIRES: loongarch ++// Test that we create R_LARCH_RELATIVE relocations for the dynamic linker ++// but don't put any symbols in the dynamic symbol table. ++ ++// RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t.o ++// RUN: ld.lld -shared %t.o -o %t.so ++// RUN: llvm-readobj -t -r -dyn-symbols %t.so | FileCheck %s ++ ++// CHECK: Relocations [ ++// CHECK-NEXT: Section ({{.*}}) .rela.dyn { ++// CHECK-NEXT: 0x[[FOO_ADDR:.*]] R_LARCH_RELATIVE - 0x[[FOO_ADDR]] ++// CHECK-NEXT: 0x[[BAR_ADDR:.*]] R_LARCH_RELATIVE - 0x[[BAR_ADDR]] ++// CHECK-NEXT: 0x10008 R_LARCH_RELATIVE - 0x10005 ++// CHECK-NEXT: 0x{{.*}} R_LARCH_RELATIVE - 0x[[FOO_ADDR]] ++// CHECK-NEXT: 0x{{.*}} R_LARCH_32 external 0x0 ++// CHECK-NEXT: } ++// CHECK-NEXT: ] ++ ++// CHECK: Symbols [ ++// CHECK: Name: foo ++// CHECK-NEXT: Value: 0x[[FOO_ADDR]] ++// CHECK: Name: bar ++// CHECK-NEXT: Value: 0x[[BAR_ADDR]] ++// CHECK: ] ++ ++// CHECK: DynamicSymbols [ ++// CHECK-NEXT: Symbol { ++// CHECK-NEXT: Name: ++// CHECK-NEXT: Value: 0x0 ++// CHECK-NEXT: Size: 0 ++// CHECK-NEXT: Binding: Local ++// CHECK-NEXT: Type: None ++// CHECK-NEXT: Other: 0 ++// CHECK-NEXT: Section: Undefined ++// CHECK-NEXT: } ++// CHECK-NEXT: Symbol { ++// CHECK-NEXT: Name: external ++// CHECK-NEXT: Value: 0x0 ++// CHECK-NEXT: Size: 0 ++// CHECK-NEXT: Binding: Global ++// CHECK-NEXT: Type: None ++// CHECK-NEXT: Other: 0 ++// CHECK-NEXT: Section: Undefined ++// CHECK-NEXT: } ++// CHECK-NEXT: ] ++ ++ .data ++foo: ++ .long foo ++ ++ .hidden bar ++ .globl bar ++bar: ++ .long bar ++ .long bar + 1 ++ .long foo ++ ++ .long external +diff --git a/lld/test/ELF/loongarch-tls-gd.s b/lld/test/ELF/loongarch-tls-gd.s +new file mode 100644 +index 000000000..a742169b6 +--- /dev/null ++++ b/lld/test/ELF/loongarch-tls-gd.s +@@ -0,0 +1,137 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64-unknown-linux %s -o %t.o ++# RUN: llvm-readobj --relocations %t.o | FileCheck -check-prefix=InputRelocs %s ++# RUN: ld.lld %t.o -o %t ++# RUN: llvm-objdump -s --section=.got %t | FileCheck -check-prefix=GOT %s ++# RUN: ld.lld -shared %t.o -o %t.so ++# RUN: llvm-readobj --relocations %t.so | FileCheck -check-prefix=OutputRelocs %s ++# RUN: llvm-objdump --section-headers -t %t.so | FileCheck -check-prefix=SO_SYM %s ++# RUN: llvm-objdump -s --section=.got %t.so | FileCheck -check-prefix=SO_GOT %s ++# RUN: llvm-objdump -d %t.so | FileCheck --check-prefixes=DIS %s ++ ++# Test the handling of the global-dynamic TLS model. Dynamic Loader finds ++# module index R_LARCH_TLS_DTPMODNN. For an executable we write the module ++# index 1 and the offset into the TLS directly into the GOT. For a shared ++# library we can only write the offset into the TLS directly if the symbol ++# is non-preemptible ++ ++# la.tls.ld alias for la.tls.gd, So we only check la.tls.gd. ++ ++.globl _start ++_start: ++ la.tls.gd $r12, x ++ la.tls.gd $r15, z ++ ++.section .tdata ++.local x ++x: ++.byte 10 ++ ++.global z ++z: ++.long 10 ++ ++# InputRelocs: Relocations [ ++# InputRelocs-NEXT: Section (3) .rela.text { ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x800 ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_PUSH_TLS_GD x 0x0 ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_ADD - 0x0 ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x4 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_PUSH_TLS_GD x 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_ADD - 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x804 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_PUSH_TLS_GD x 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_ADD - 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_SL - 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_SUB - 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_POP_32_S_10_12 - 0x0 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x800 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_PUSH_TLS_GD z 0x0 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_ADD - 0x0 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x4 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_PUSH_TLS_GD z 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_ADD - 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_ 0x804 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_PUSH_TLS_GD z 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_ADD - 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_SL - 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_SUB - 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_POP_32_S_10_12 - 0x0 ++# InputRelocs-NEXT: } ++# InputRelocs-NEXT: ] ++ ++ ++# For an executable we write the module index 1 and the offset ++# into the TLS directly into the GOT. ++ ++# GOT: Contents of section .got: ++# GOT-NEXT: 120020218 00000000 00000000 01000000 00000000 {{.*}} ++# ^ ^-----x-module-id ++# +-.dynamic address(not exist) ++# GOT-NEXT: 120020228 01000000 00000000 01000000 00000000 {{.*}} ++# ^--x-offset ^-----z-module-id ++# GOT-NEXT: 120020238 00000000 00000000 {{.*}} ++# ^--z-offset ++ ++# OutputRelocs: Relocations [ ++# OutputRelocs-NEXT: Section (5) .rela.dyn { ++# OutputRelocs-NEXT: 0x203F0 R_LARCH_TLS_DTPMOD64 - 0x0 ++# OutputRelocs-NEXT: 0x203E0 R_LARCH_TLS_DTPMOD64 z 0x0 ++# OutputRelocs-NEXT: 0x203E8 R_LARCH_TLS_DTPREL64 z 0x0 ++# OutputRelocs-NEXT: } ++# OutputRelocs-NEXT: ] ++ ++# SO_SYM: Sections: ++# SO_SYM: Idx Name Size VMA Type ++# SO_SYM: 7 .tdata 00000005 0000000000020330 DATA ++# SO_SYM: 8 .dynamic 000000a0 0000000000020338 ++# SO_SYM: 9 .got 00000028 00000000000203d8 DATA ++# SO_SYM: SYMBOL TABLE: ++# SO_SYM: {{0*}} l .tdata {{0*}} x ++# _GLOBAL_OFFSET_TABLE_ 0x203d8 ++# SO_SYM: 00000000000203d8 l .got {{0*}} .hidden _GLOBAL_OFFSET_TABLE_ ++# SO_SYM: 0000000000020338 l .dynamic {{0*}} .hidden _DYNAMIC ++# SO_SYM: 0000000000000001 g .tdata {{0*}} z ++ ++# For a shared library we can only write the offset into the TLS directly ++ ++# SO_GOT: Contents of section .got: ++# SO_GOT-NEXT: 203d8 38030200 00000000 00000000 00000000 ++# ^ ^-----x-module-id ++# +---.dynamic address ++# SO_GOT-NEXT: 203e8 00000000 00000000 00000000 00000000 ++# ^---x-offset ^-----z-module-id ++# SO_GOT-NEXT: 203f8 00000000 00000000 ++# ^---z-offset ++ ++# %tlsgd(x)=8; %tlsgd(z)=24 ++ ++# la.tls.gd rd, symbol will be expanded to such instructions: ++# pcaddu12i rd,(%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%tlsgd(symbol))>>12 ++# addi.d rd,rd,%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%tlsgd(symbol) - \ ++# ((%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x800)+%tlsgd(symbol))>>12<<12) ++ ++# la.tls.gd $r12, x ++# (0x203d8+0x800-0x10320+8)>>12 = 16 ++# DIS: 10320: 0c 02 00 1c pcaddu12i $r12, 16 ++# (0x203d8+4-0x10320+8)-((0x203d8+4+0x800-0x10320)+8)>>12<<12 = 0xC4 = 196 TODO ++# DIS-NEXT: 10324: 8c 41 c3 02 addi.d $r12, $r12, 208 ++ ++# la.tls.gd $r15, z ++# (0x203d8+0x800-0x10328+8)>>12 = 16 ++# DIS-NEXT: 10328: 0f 02 00 1c pcaddu12i $r15, 16 ++# (0x203d8+4-0x10328+24)-((0x203d8+4+0x800-0x10328)+24)>>12<<12 = 0xCC = 204 TODO ++# DIS-NEXT: 1032c: ef e1 c2 02 addi.d $r15, $r15, 184 +diff --git a/lld/test/ELF/loongarch-tls-ie.s b/lld/test/ELF/loongarch-tls-ie.s +new file mode 100644 +index 000000000..a0037b958 +--- /dev/null ++++ b/lld/test/ELF/loongarch-tls-ie.s +@@ -0,0 +1,61 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t.64.o ++# RUN: llvm-objdump -r %t.64.o | FileCheck --check-prefix=RELOCS %s ++## loongarch64 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=IE,IE64 %s ++ ++# RELOCS: RELOCATION RECORDS FOR [.text]: ++# RELOCS-NEXT: OFFSET TYPE VALUE ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_+0x800 ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_TLS_GOT y ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_ADD *ABS* ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_ABSOLUTE *ABS*+0xc ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_SR *ABS* ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_POP_32_S_5_20 *ABS* ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_+0x4 ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_TLS_GOT y ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_ADD *ABS* ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_PCREL _GLOBAL_OFFSET_TABLE_+0x804 ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_TLS_GOT y ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_ADD *ABS* ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_ABSOLUTE *ABS*+0xc ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_SR *ABS* ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_PUSH_ABSOLUTE *ABS*+0xc ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_SL *ABS* ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_SUB *ABS* ++# RELOCS-NEXT: {{[0-9abcdef]*}} R_LARCH_SOP_POP_32_S_10_12 *ABS* ++ ++# IE64-REL: .rela.dyn { ++# IE64-REL-NEXT: 0x203B0 R_LARCH_TLS_TPREL64 - 0x4 ++# IE64-REL-NEXT: 0x203A8 R_LARCH_TLS_TPREL64 y 0x0 ++# IE64-REL-NEXT: } ++ ++## loongarch64: &.got[y] - . = 0x203A8 - . = 0x100C8 = 4096*16+200 ++# IE: 102e0: pcaddu12i $r4, 16 ++# IE64-NEXT: ld.d $r4, $r4, 200 ++# IE-NEXT: addi.w $r5, $zero, 10 ++# IE-NEXT: stx.w $r5, $tp, $r4 ++## loongarch64: &.got[z] - . = 0x203B0 - . = 0x100C0 = 4096*16+192 ++# IE: 102f0: pcaddu12i $r6, 16 ++# IE64-NEXT: ld.d $r6, $r6, 192 ++# IE-NEXT: addi.w $r7, $zero, 100 ++# IE-NEXT: stx.w $r7, $tp, $r6 ++ ++la.tls.ie $r4, y ++addi.w $r5, $zero, 10 ++stx.w $r5, $tp, $r4 ++la.tls.ie $r6, z ++addi.w $r7, $zero, 100 ++stx.w $r7, $tp, $r6 ++ ++.section .tbss ++.globl y ++y: ++.word 0 ++.size y, 4 ++z: ++.word 0 ++.size z, 4 +diff --git a/lld/test/ELF/loongarch-tls-le.s b/lld/test/ELF/loongarch-tls-le.s +new file mode 100644 +index 000000000..cdd6bcd7c +--- /dev/null ++++ b/lld/test/ELF/loongarch-tls-le.s +@@ -0,0 +1,130 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t.o ++# RUN: llvm-readobj --relocations %t.o | FileCheck -check-prefix=InputRelocs %s ++# RUN: ld.lld %t.o -o %t ++# RUN: llvm-readobj --relocations %t | FileCheck -check-prefix=OutputRelocs %s ++# RUN: llvm-objdump --section-headers -t %t | FileCheck -check-prefix=SO_SYM %s ++# RUN: llvm-objdump -d %t | FileCheck --check-prefixes=DIS %s ++ ++# Test the handling of the Local-Exec TLS model. TLS can be resolved ++# statically for an application. ++ ++.globl _start ++_start: ++ la.tls.le $r12, x ++ la.tls.le $r13, y ++ la.tls.le $r15, z + 0x100 ++ ++.section .tdata ++.local x ++x: ++.byte 10 ++ ++.globl y ++y: ++.word 10 ++ ++.local z ++z: ++.long 10 ++ ++# InputRelocs: Relocations [ ++# InputRelocs-NEXT: Section (3) .rela.text { ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_PUSH_TLS_TPREL x 0x0 ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_PUSH_ABSOLUTE - 0x20 ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_SL - 0x0 ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_PUSH_ABSOLUTE - 0x2C ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x0 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_PUSH_TLS_TPREL x 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_PUSH_ABSOLUTE - 0xFFF ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_AND - 0x0 ++# InputRelocs-NEXT: 0x4 R_LARCH_SOP_POP_32_U_10_12 - 0x0 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_PUSH_TLS_TPREL x 0x0 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_SL - 0x0 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_PUSH_ABSOLUTE - 0x2C ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x8 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_PUSH_TLS_TPREL x 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_PUSH_ABSOLUTE - 0x34 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0xC R_LARCH_SOP_POP_32_S_10_12 - 0x0 ++# InputRelocs-NEXT: 0x10 R_LARCH_SOP_PUSH_TLS_TPREL y 0x0 ++# InputRelocs-NEXT: 0x10 R_LARCH_SOP_PUSH_ABSOLUTE - 0x20 ++# InputRelocs-NEXT: 0x10 R_LARCH_SOP_SL - 0x0 ++# InputRelocs-NEXT: 0x10 R_LARCH_SOP_PUSH_ABSOLUTE - 0x2C ++# InputRelocs-NEXT: 0x10 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x10 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# InputRelocs-NEXT: 0x14 R_LARCH_SOP_PUSH_TLS_TPREL y 0x0 ++# InputRelocs-NEXT: 0x14 R_LARCH_SOP_PUSH_ABSOLUTE - 0xFFF ++# InputRelocs-NEXT: 0x14 R_LARCH_SOP_AND - 0x0 ++# InputRelocs-NEXT: 0x14 R_LARCH_SOP_POP_32_U_10_12 - 0x0 ++# InputRelocs-NEXT: 0x18 R_LARCH_SOP_PUSH_TLS_TPREL y 0x0 ++# InputRelocs-NEXT: 0x18 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0x18 R_LARCH_SOP_SL - 0x0 ++# InputRelocs-NEXT: 0x18 R_LARCH_SOP_PUSH_ABSOLUTE - 0x2C ++# InputRelocs-NEXT: 0x18 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x18 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# InputRelocs-NEXT: 0x1C R_LARCH_SOP_PUSH_TLS_TPREL y 0x0 ++# InputRelocs-NEXT: 0x1C R_LARCH_SOP_PUSH_ABSOLUTE - 0x34 ++# InputRelocs-NEXT: 0x1C R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x1C R_LARCH_SOP_POP_32_S_10_12 - 0x0 ++# InputRelocs-NEXT: 0x20 R_LARCH_SOP_PUSH_TLS_TPREL z 0x100 ++# InputRelocs-NEXT: 0x20 R_LARCH_SOP_PUSH_ABSOLUTE - 0x20 ++# InputRelocs-NEXT: 0x20 R_LARCH_SOP_SL - 0x0 ++# InputRelocs-NEXT: 0x20 R_LARCH_SOP_PUSH_ABSOLUTE - 0x2C ++# InputRelocs-NEXT: 0x20 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x20 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# InputRelocs-NEXT: 0x24 R_LARCH_SOP_PUSH_TLS_TPREL z 0x100 ++# InputRelocs-NEXT: 0x24 R_LARCH_SOP_PUSH_ABSOLUTE - 0xFFF ++# InputRelocs-NEXT: 0x24 R_LARCH_SOP_AND - 0x0 ++# InputRelocs-NEXT: 0x24 R_LARCH_SOP_POP_32_U_10_12 - 0x0 ++# InputRelocs-NEXT: 0x28 R_LARCH_SOP_PUSH_TLS_TPREL z 0x100 ++# InputRelocs-NEXT: 0x28 R_LARCH_SOP_PUSH_ABSOLUTE - 0xC ++# InputRelocs-NEXT: 0x28 R_LARCH_SOP_SL - 0x0 ++# InputRelocs-NEXT: 0x28 R_LARCH_SOP_PUSH_ABSOLUTE - 0x2C ++# InputRelocs-NEXT: 0x28 R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x28 R_LARCH_SOP_POP_32_S_5_20 - 0x0 ++# InputRelocs-NEXT: 0x2C R_LARCH_SOP_PUSH_TLS_TPREL z 0x100 ++# InputRelocs-NEXT: 0x2C R_LARCH_SOP_PUSH_ABSOLUTE - 0x34 ++# InputRelocs-NEXT: 0x2C R_LARCH_SOP_SR - 0x0 ++# InputRelocs-NEXT: 0x2C R_LARCH_SOP_POP_32_S_10_12 - 0x0 ++# InputRelocs-NEXT: } ++# InputRelocs-NEXT: ] ++ ++# Local-Exec creates no ++# OutputRelocs: Relocations [ ++# OutputRelocs-NEXT: ] ++ ++# SO_SYM: Sections: ++# SO_SYM: Idx Name Size VMA Type ++# SO_SYM: 2 .tdata 00000009 0000000120020230 DATA ++# SO_SYM: SYMBOL TABLE: ++# SO_SYM: 0000000000000000 l .tdata {{0*}} x ++# SO_SYM: 0000000000000005 l .tdata {{0*}} z ++# SO_SYM: 0000000000000001 g .tdata {{0*}} y ++ ++# %tprel(x+addend)=0 ; %tprel(y+addend)=1 ; %tprel(z+0x100)=0x105=261 ++ ++# la.tls.le rd, symbol + addend ++# Load $tp-relative offset of TLS symbol ++# will be expanded to such instructions: ++# lu12i.w rd,%tprel(symbol + addend)<<32>>44 ++# ori rd,rd,%tprel(symbol + addend)&0xfff ++# lu32i.d rd,%tprel(symbol + addend)<<12>>44 ++# lu52i.d rd,rd,%tprel(symbol + addend)>>52 ++ ++# DIS: 120010200: 0c 00 00 14 lu12i.w $r12, 0 ++# DIS-NEXT: 120010204: 8c 01 80 03 ori $r12, $r12, 0 ++# DIS-NEXT: 120010208: 0c 00 00 16 lu32i.d $r12, 0 ++# DIS-NEXT: 12001020c: 8c 01 00 03 lu52i.d $r12, $r12, 0 ++# DIS-NEXT: 120010210: 0d 00 00 14 lu12i.w $r13, 0 ++# DIS-NEXT: 120010214: ad 05 80 03 ori $r13, $r13, 1 ++# DIS-NEXT: 120010218: 0d 00 00 16 lu32i.d $r13, 0 ++# DIS-NEXT: 12001021c: ad 01 00 03 lu52i.d $r13, $r13, 0 ++# DIS-NEXT: 120010220: 0f 00 00 14 lu12i.w $r15, 0 ++# DIS-NEXT: 120010224: ef 15 84 03 ori $r15, $r15, 261 ++# DIS-NEXT: 120010228: 0f 00 00 16 lu32i.d $r15, 0 ++# DIS-NEXT: 12001022c: ef 01 00 03 lu52i.d $r15, $r15, 0 +diff --git a/lld/test/ELF/loongarch_eh_frame.s b/lld/test/ELF/loongarch_eh_frame.s +new file mode 100644 +index 000000000..6e9769ffe +--- /dev/null ++++ b/lld/test/ELF/loongarch_eh_frame.s +@@ -0,0 +1,23 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t.o ++# RUN: ld.lld -shared %t.o -o %t.so ++ ++.globl _start ++_start: ++ nop ++ ++.section foo,"ax",@progbits ++.cfi_startproc ++ nop ++.cfi_endproc ++ ++.section bar,"ax",@progbits ++.cfi_startproc ++ nop ++.cfi_endproc ++ ++.section dah,"ax",@progbits ++.cfi_startproc ++ nop ++.cfi_endproc +diff --git a/lld/test/ELF/loongarch_not_relative_eh_frame.test b/lld/test/ELF/loongarch_not_relative_eh_frame.test +new file mode 100644 +index 000000000..5143a5b8b +--- /dev/null ++++ b/lld/test/ELF/loongarch_not_relative_eh_frame.test +@@ -0,0 +1,49 @@ ++# REQUIRES: loongarch ++ ++# RUN: yaml2obj %s -o %t.o ++# RUN: ld.lld -pie %t.o -o %t ++# RUN: llvm-readelf -r %t | FileCheck %s ++# ++# CHECK-NOT: {{.*}} R_LARCH_RELATIVE {{.*}} ++# ++# t.o ++--- !ELF ++FileHeader: ++ Class: ELFCLASS64 ++ Data: ELFDATA2LSB ++ Type: ET_REL ++ Machine: EM_LOONGARCH ++ Flags: [ EF_LARCH_ABI_LP64 ] ++ SectionHeaderStringTable: .strtab ++Sections: ++ - Name: .text ++ Type: SHT_PROGBITS ++ Flags: [ SHF_ALLOC, SHF_EXECINSTR ] ++ AddressAlign: 0x10 ++ Content: '00004003' ++ - Name: .eh_frame ++ Type: SHT_PROGBITS ++ Flags: [ SHF_WRITE, SHF_ALLOC ] ++ AddressAlign: 0x8 ++ Content: 1000000000000000017A5200017801010C0C030018000000180000000000000000000000040000000000000000000000 ++ - Name: .rela.eh_frame ++ Type: SHT_RELA ++ Link: .symtab ++ AddressAlign: 0x8 ++ Info: .eh_frame ++ Relocations: ++ - Offset: 0x1C ++ Symbol: _start ++ Type: R_LARCH_64 ++ - Type: SectionHeaderTable ++ Sections: ++ - Name: .strtab ++ - Name: .text ++ - Name: .eh_frame ++ - Name: .rela.eh_frame ++ - Name: .symtab ++Symbols: ++ - Name: _start ++ Section: .text ++ Binding: STB_GLOBAL ++... +diff --git a/lld/test/ELF/loongarch_preempt_hidden_sym.s b/lld/test/ELF/loongarch_preempt_hidden_sym.s +new file mode 100644 +index 000000000..a25f0558c +--- /dev/null ++++ b/lld/test/ELF/loongarch_preempt_hidden_sym.s +@@ -0,0 +1,12 @@ ++# REQUIRES: loongarch ++ ++# RUN: llvm-mc -filetype=obj -triple=loongarch64 %s -o %t.o ++# RUN: ld.lld -pie %t.o -o %t.so ++ ++# CHECK-NOT: error: cannot preempt symbol: hidden ++ ++.globl hidden ++.hidden hidden ++hidden: ++.rodata ++.long hidden +diff --git a/lld/test/ELF/x86-64-dyn-rel-error5.s b/lld/test/ELF/x86-64-dyn-rel-error5.s +index 495104509..bfd60ef2f 100644 +--- a/lld/test/ELF/x86-64-dyn-rel-error5.s ++++ b/lld/test/ELF/x86-64-dyn-rel-error5.s +@@ -1,7 +1,7 @@ + # REQUIRES: x86 + # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +-# RUN: not ld.lld -pie %t.o -o /dev/null 2>&1 | FileCheck --check-prefixes=CHECK,PIE %s +-# RUN: not ld.lld -shared %t.o -o /dev/null 2>&1 | FileCheck --check-prefixes=CHECK,SHARED %s ++# RUN: not ld.lld -pie %t.o -o /dev/null 2>&1 | FileCheck %s ++# RUN: not ld.lld -shared %t.o -o /dev/null 2>&1 | FileCheck %s + + ## Check we don't create dynamic relocations in a writable section, + ## if the number of bits is smaller than the wordsize. +@@ -17,8 +17,7 @@ hidden: + # CHECK: error: relocation R_X86_64_16 cannot be used against local symbol; recompile with -fPIC + # CHECK: error: relocation R_X86_64_32 cannot be used against local symbol; recompile with -fPIC + +-# PIE: error: cannot preempt symbol: hidden +-# SHARED: error: relocation R_X86_64_32 cannot be used against symbol 'hidden'; recompile with -fPIC ++# CHECK: error: relocation R_X86_64_32 cannot be used against symbol 'hidden'; recompile with -fPIC + + .data + .byte local # R_X86_64_8 +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 12e7efb0684b649fde63e1ee83c57c65b82228b4..28dbd9e743f54a7b41ddc2e7132049fb2ca66142 100644 --- a/lld.spec +++ b/lld.spec @@ -1,4 +1,4 @@ -%define anolis_release .0.2 +%define anolis_release .0.3 %bcond_without check #global rc_ver 1 @@ -33,6 +33,7 @@ Patch0: 0001-PATCH-lld-CMake-Check-for-gtest-headers-even-if-lit..patch # Bundle libunwind header need during build for MachO support Patch1: 0002-PATCH-lld-Import-compact_unwind_encoding.h-from-libu.patch +Patch2: 0001-Support-LoongArch.patch BuildRequires: gcc BuildRequires: gcc-c++ @@ -230,6 +231,9 @@ cd %{_vpath_builddir} %endif %changelog +* Fri Aug 11 2023 Chen Li - 15.0.7-1.0.3 +- Support LoongArch + * Wed Jul 19 2023 Zhao Hang - 15.0.7-1.0.2 - Add loongarch64 arch