From deeb118e11797a907d55d63a0643028fb736bc23 Mon Sep 17 00:00:00 2001 From: swcompiler Date: Fri, 16 May 2025 16:01:06 +0800 Subject: [PATCH] Add Sw64 support for lld --- ...Sw64-Add-Sw64-target-support-for-lld.patch | 1274 +++++++++++++++++ lld.spec | 8 +- 2 files changed, 1281 insertions(+), 1 deletion(-) create mode 100755 0012-Sw64-Add-Sw64-target-support-for-lld.patch diff --git a/0012-Sw64-Add-Sw64-target-support-for-lld.patch b/0012-Sw64-Add-Sw64-target-support-for-lld.patch new file mode 100755 index 0000000..e5cc321 --- /dev/null +++ b/0012-Sw64-Add-Sw64-target-support-for-lld.patch @@ -0,0 +1,1274 @@ +From 0476f248aa9f45992d454bf3775f09498f5f87f3 Mon Sep 17 00:00:00 2001 +From: xiaol +Date: Tue, 20 May 2025 17:16:45 +0800 +Subject: [PATCH 3/5] lld + +--- + lld/ELF/Arch/Sw_64.cpp | 354 ++++++++++++++++++++++++++++++++++ + lld/ELF/CMakeLists.txt | 1 + + lld/ELF/Config.h | 4 + + lld/ELF/Driver.cpp | 15 +- + lld/ELF/InputFiles.cpp | 2 + + lld/ELF/InputSection.cpp | 23 ++- + lld/ELF/Options.td | 19 ++ + lld/ELF/Relocations.cpp | 131 ++++++++++++- + lld/ELF/Relocations.h | 6 + + lld/ELF/ScriptParser.cpp | 1 + + lld/ELF/SyntheticSections.cpp | 232 ++++++++++++++++++++++ + lld/ELF/SyntheticSections.h | 56 +++++- + lld/ELF/Target.cpp | 2 + + lld/ELF/Target.h | 1 + + lld/ELF/Writer.cpp | 37 +++- + 15 files changed, 869 insertions(+), 15 deletions(-) + create mode 100644 lld/ELF/Arch/Sw_64.cpp + +diff --git a/lld/ELF/Arch/Sw_64.cpp b/lld/ELF/Arch/Sw_64.cpp +new file mode 100644 +index 000000000..fa3f74adc +--- /dev/null ++++ b/lld/ELF/Arch/Sw_64.cpp +@@ -0,0 +1,354 @@ ++//===- Sw_64.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" ++#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::ELF; ++using namespace llvm::object; ++using namespace llvm::support::endian; ++using namespace lld; ++using namespace lld::elf; ++ ++namespace { ++template class Sw_64 final : public TargetInfo { ++public: ++ Sw_64(); ++ uint32_t calcEFlags() const override; ++ RelExpr getRelExpr(RelType type, const Symbol &s, ++ const uint8_t *loc) const override; ++ RelType getDynRel(RelType type) const override; ++ void writePltHeader(uint8_t *buf) const override; ++ void writePlt(uint8_t *buf, const Symbol &sym, ++ uint64_t pltEntryAddr) const override; ++ void relocate(uint8_t *loc, const Relocation &rel, ++ uint64_t val) const override; ++ ++private: ++ static inline uint32_t high(uint32_t value){ ++ return (value >> 16) + (0 != (value & 0x8000)); ++ } ++ ++ static inline void Write16(void *P, uint16_t V){ ++ return write16(P, V); ++ } ++ ++ static inline void Write32(void *P, uint32_t V){ ++ return write32(P, V); ++ } ++ ++ static inline void Write64(void *P, uint64_t V){ ++ return write64(P, V); ++ } ++ ++ static inline uint16_t Read16(void *P){ ++ return read16(P); ++ } ++ ++ static inline uint32_t Read32(void *P){ ++ return read32(P); ++ } ++ ++ static inline uint64_t Read64(void *P){ ++ return read64(P); ++ } ++ ++ static inline uint32_t bit_select32(uint32_t a, uint32_t b, uint32_t mask){ ++ return (a & ~mask) | (b & mask); ++ } ++ ++ static constexpr int32_t LITUSE_JSR = 3; ++ static constexpr uintptr_t RA_SHIFT = 21; ++ static constexpr uintptr_t RB_SHIFT = 16; ++ static constexpr uintptr_t RC_MASK = 31; ++ static constexpr uintptr_t RA_MASK = RC_MASK << RA_SHIFT; ++ static constexpr uintptr_t RB_MASK = RC_MASK << RB_SHIFT; ++ ++}; ++} // namespace ++ ++template ++Sw_64::Sw_64(){ ++ defaultCommonPageSize = 0x2000; ++ defaultMaxPageSize = 0x10000; ++ defaultImageBase = 0x120000000; ++ gotRel = R_SW_64_GLOB_DAT; ++ pltRel = R_SW_64_JMP_SLOT; ++ relativeRel = R_SW_64_RELATIVE; ++ symbolicRel = R_SW_64_REFQUAD; ++ tlsGotRel = R_SW_64_TPREL64; ++ tlsModuleIndexRel = R_SW_64_DTPMOD64; ++ tlsOffsetRel = R_SW_64_DTPREL64; ++ pltEntrySize = 0x4; ++ pltHeaderSize = 0x24; ++ ++ // .got.plt header should be 16 bytes. We only get 8 bytes here. ++ // A dummy entry will be added when necessary to compensate. ++ gotPltHeaderEntriesNum = 1; ++ ++ // SW dynamic linker treat rela as rel and does not use addend in rela at all, ++ // so we force to write out the addend to the position relocated. ++ config->writeAddends = true; ++} ++ ++template ++uint32_t Sw_64::calcEFlags() const { ++ return /* sw6a */ 0x4; ++} ++ ++template ++RelExpr Sw_64::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { ++ switch(0xff & Type){ ++ case R_SW_64_NONE: ++ return R_NONE; ++ case R_SW_64_LITERAL: ++ if(S.isFunc() && (LITUSE_JSR << 8) == (Type & 0xff00))return R_SW_PLT_GOT_OFF; ++ // fallthrough ++ case R_SW_64_GLOB_DAT: ++ case R_SW_64_GOTTPREL: ++ return R_SW_GOT_OFF; ++ case R_SW_64_TLSGD: ++ case R_SW_64_TLSLDM: ++ return R_SW_TLSGD_GOT; ++ case R_SW_64_GPDISP: ++ return R_SW_GOT_GP_PC; ++ case R_SW_64_LITUSE: ++ case R_SW_64_HINT: ++ return R_SW_HINT_PC; ++ case R_SW_64_BRADDR: ++ case R_SW_64_BRSGP: ++ case R_SW_64_SREL16: ++ case R_SW_64_SREL32: ++ case R_SW_64_SREL64: ++ return R_PC; ++ case R_SW_64_GPREL32: ++ case R_SW_64_GPREL16: ++ case R_SW_64_GPRELHIGH: ++ case R_SW_64_GPRELLOW: ++ return R_SW_GOTREL; ++ case R_SW_64_REFLONG: ++ case R_SW_64_REFQUAD: ++ case R_SW_64_LITERAL_GOT: ++ return R_ABS; ++ case R_SW_64_TPREL64: ++ case R_SW_64_TPRELHI: ++ case R_SW_64_TPRELLO: ++ case R_SW_64_TPREL16: ++ return R_TPREL; ++ case R_SW_64_DTPRELHI: ++ case R_SW_64_DTPRELLO: ++ return R_DTPREL; ++ default: ++ error(getErrorLocation(Loc) + "do not know how to handle relocation " + toString(Type)); ++ return R_NONE; ++ } ++} ++ ++ ++ ++template RelType Sw_64::getDynRel(RelType type) const { ++ return type; ++} ++ ++template ++void Sw_64::relocate(uint8_t *Loc, const Relocation &rel, ++ uint64_t Val) const { ++ RelType Type = rel.type; ++ switch(0xff & Type){ ++ case R_SW_64_NONE: ++ case R_SW_64_LITUSE: ++ case R_SW_64_LITERAL_GOT: ++ // This can't be guarded by `literalgot` option, gcc already generates this ++ // relocations pre-8X. ++ ++ // Nothing to do. ++ break; ++ case R_SW_64_GPDISP: { ++ auto Addend = llvm::SignExtend32<24>(Type >> 8); ++ auto i_hi = Read32(Loc); ++ auto i_lo = Read32(Loc + Addend); ++ ++ auto implicit_addend = ((i_hi & 0xffff) << 16) | (i_lo & 0xffff); ++ implicit_addend = (implicit_addend ^ 0x80008000) - 0x80008000; ++ // Addend is for the address of the other instruction, not the displacement. ++ Val -= Addend; ++ Val += implicit_addend; ++ if(static_cast(Val) < INT32_MIN || INT32_MAX < static_cast(Val)){ ++ error(getErrorLocation(Loc) + toString(Type) + " overflowed: 0x" + Twine::utohexstr(Val)); ++ } ++ i_hi = bit_select32(i_hi, high(Val), 0xffff); ++ i_lo = bit_select32(i_lo, Val, 0xffff); ++ Write32(Loc, i_hi); ++ Write32(Loc + Addend, i_lo); ++ }break; ++ case R_SW_64_BRSGP: ++ // TODO: Check .prologue. ++ // fallthrough ++ case R_SW_64_BRADDR: ++ { ++ auto inst = Read32(Loc); ++ Val = (Val >> 2) - 1; ++ Write32(Loc, bit_select32(inst, Val, 0x1fffff)); ++ }break; ++ case R_SW_64_HINT: { ++ Val = (Val >> 2) - 1; ++ auto inst = bit_select32(Read32(Loc), Val, 0xffff); ++ Write32(Loc, inst); ++ }break; ++ case R_SW_64_SREL16: { ++ // TODO: Overflow? ++ auto inst = Read16(Loc); ++ Write16(Loc, Val + inst); ++ }break; ++ case R_SW_64_REFLONG: ++ case R_SW_64_SREL32: ++ case R_SW_64_GPREL32: ++ { ++ // TODO: Overflow? ++ auto inst = Read32(Loc); ++ Write32(Loc, Val + inst); ++ }break; ++ case R_SW_64_DTPMOD64: ++ Val = !config->shared; ++ // fallthrough ++ case R_SW_64_REFQUAD: ++ case R_SW_64_SREL64: ++ case R_SW_64_TPREL64: ++ { ++ // TODO: Overflow? ++ auto inst = Read64(Loc); ++ Write64(Loc, Val + inst); ++ }break; ++ case R_SW_64_GLOB_DAT: ++ case R_SW_64_JMP_SLOT: ++ { ++ Write64(Loc, Val); ++ }break; ++ case R_SW_64_GPRELHIGH: ++ case R_SW_64_TPRELHI: ++ case R_SW_64_DTPRELHI: ++ { ++ auto inst = Read32(Loc); ++ Write32(Loc, bit_select32(inst, high(Val), 0xffff)); ++ }break; ++ case R_SW_64_LITERAL: { ++ auto literal_inst = Read32(Loc); ++ auto hi_addend = llvm::SignExtend32<16>(Type >> 16); ++ if(0 != hi_addend){ ++ auto hi_loc = Loc + hi_addend; ++ auto got_inst = Read32(hi_loc); ++ // TODO: Check the protocol is correct. ++ ++ // Change previous instruction with R_SW_64_LITERAL_GOT to load the high part of GOT. ++ Write32(hi_loc, bit_select32(got_inst, high(Val), 0xffff)); ++ // Change the base register of low load to the destination register of the high load. ++ literal_inst = bit_select32(literal_inst, (got_inst & RA_MASK) >> (RA_SHIFT - RB_SHIFT), RC_MASK << RB_SHIFT); ++ }else if(static_cast(Val) < INT16_MIN || INT16_MAX < static_cast(Val)){ ++ error(getErrorLocation(Loc) + toString(Type) + " overflowed: 0x" + Twine::utohexstr(Val)); ++ } ++ Write32(Loc, bit_select32(literal_inst, Val, 0xffff)); ++ }break; ++ ++#define LDIH_GOT(Loc, Type, Val, option) \ ++ do{ \ ++ auto inst = Read32(Loc); \ ++ Val += inst & 0xffff; \ ++ \ ++ auto hi_addend = llvm::SignExtend32<16>(Type >> 16); \ ++ if(0 != hi_addend){ \ ++ if(config->option){ \ ++ auto hi_loc = Loc + hi_addend; \ ++ auto got_inst = Read32(hi_loc); \ ++ \ ++ /* Change previous instruction with R_SW_64_TLSREL_GOT to load the high part of GOT. */ \ ++ Write32(hi_loc, bit_select32(got_inst, high(Val), 0xffff)); \ ++ /* Change the base register of low load to the destination register of the high load. */ \ ++ inst = bit_select32(inst, (got_inst & RA_MASK) >> (RA_SHIFT - RB_SHIFT), RC_MASK << RB_SHIFT); \ ++ }else{ \ ++ error(getErrorLocation(Loc) + "add `--" #option "` to enable large range relocation " + toString(0xffff & Type)); \ ++ } \ ++ }else if(static_cast(Val) < INT16_MIN || INT16_MAX < static_cast(Val)){ \ ++ error(getErrorLocation(Loc) + toString(0xffff & Type) + " overflowed: 0x" + Twine::utohexstr(Val)); \ ++ } \ ++ Write32(Loc, bit_select32(inst, Val, 0xffff)); \ ++ }while(false) \ ++ // end of LDIH_GOT ++ ++ case R_SW_64_TLSGD: ++ LDIH_GOT(Loc, Type, Val, sw64_tlsrelgot_tlsgd); ++ break; ++ case R_SW_64_TLSLDM: ++ LDIH_GOT(Loc, Type, Val, sw64_tlsrelgot_tlsldm); ++ break; ++ case R_SW_64_GOTTPREL: ++ LDIH_GOT(Loc, Type, Val, sw64_tlsrelgot_gottprel); ++ break; ++ case R_SW_64_GPRELLOW: ++ case R_SW_64_GPREL16: ++ case R_SW_64_TPRELLO: ++ case R_SW_64_TPREL16: ++ case R_SW_64_DTPRELLO: ++ { ++ auto inst = Read32(Loc); ++ Val += inst & 0xffff; ++ Write32(Loc, bit_select32(inst, Val, 0xffff)); ++ }break; ++ default: ++ error(getErrorLocation(Loc) + "do not know how to relocate " + toString(Type)); ++ } ++} ++ ++static int32_t PltIndex; ++ ++template ++void Sw_64::writePltHeader(uint8_t *Buf) const { ++ const uint8_t PltData[] = { ++ 0x39, 0x01, 0x7c, 0x43, // subl $r27,$r28,$r25 ++ 0x00, 0x00, 0x9c, 0xff, // ldih $r28,0($r28) <--- needs relocation. ++ 0x79, 0x01, 0x39, 0x43, // s4subl $r25,$r25,$r25 ++ 0x00, 0x00, 0x9c, 0xfb, // ldi $r28,0($r28) <--- needs relocation. ++ 0x00, 0x00, 0x7c, 0x8f, // ldl $r27,0($r28) ++ 0x19, 0x01, 0x39, 0x43, // addl $r25,$r25,$r25 ++ 0x08, 0x00, 0x9c, 0x8f, // ldl $r28,8($r28) ++ 0x00, 0x00, 0xfb, 0x0f, // jmp $r31,($r27),1200020, 0x<_PROCEDURE_LINKAGE_TABLE_+0x20> ++ 0xf7, 0xff, 0x9f, 0x13, // br $r28,1200000, 0x<_PROCEDURE_LINKAGE_TABLE_> ++ }; ++ if(sizeof(PltData) != this->pltHeaderSize)error("Wrong PLT header data."); ++ memcpy(Buf, PltData, sizeof(PltData)); ++ constexpr int Addend = 8; ++ Relocation rel; ++ rel.type = (R_SW_64_GPDISP | (Addend << 8)); ++ // $28 point to PLT[0], need to be relocated to the beginning of .got.plt section. ++ auto Val = in.gotPlt->getVA() - in.plt->getVA() - sizeof(PltData) + Addend; ++ relocate(Buf + 4, rel, Val); ++ PltIndex = 0; ++} ++ ++template ++void Sw_64::writePlt(uint8_t *buf, const Symbol &sym, ++ uint64_t pltEntryAddr) const { ++ // `Index` is useless when multiple entries for the same function. ++ Write32(buf, 0x13fffffe - PltIndex++); ++ ++} ++ ++template TargetInfo *elf::getSw_64TargetInfo() { ++ static Sw_64 target; ++ return ⌖ ++} ++ ++template TargetInfo *elf::getSw_64TargetInfo(); +diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt +index 89955db67..048c3e54c 100644 +--- a/lld/ELF/CMakeLists.txt ++++ b/lld/ELF/CMakeLists.txt +@@ -33,6 +33,7 @@ add_lld_library(lldELF + Arch/PPC64.cpp + Arch/RISCV.cpp + Arch/SPARCV9.cpp ++ Arch/Sw_64.cpp + Arch/X86.cpp + Arch/X86_64.cpp + ARMErrataFix.cpp +diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h +index bbf2d2015..b2e2301e8 100644 +--- a/lld/ELF/Config.h ++++ b/lld/ELF/Config.h +@@ -277,6 +277,10 @@ struct Config { + bool isStatic = false; + bool sysvHash = false; + bool target1Rel; ++ bool sw64_tlsrelgot_tlsgd; ++ bool sw64_tlsrelgot_tlsldm; ++ bool sw64_tlsrelgot_gottprel; ++ bool sw64_tlsrelgot_gotdtprel; + bool trace; + bool thinLTOEmitImportsFiles; + bool thinLTOEmitIndexFiles; +diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp +index c2059c70e..01892fa19 100644 +--- a/lld/ELF/Driver.cpp ++++ b/lld/ELF/Driver.cpp +@@ -193,6 +193,7 @@ static std::tuple parseEmulation(StringRef emul) { + .Case("msp430elf", {ELF32LEKind, EM_MSP430}) + .Case("elf64_amdgpu", {ELF64LEKind, EM_AMDGPU}) + .Case("elf64loongarch", {ELF64LEKind, EM_LOONGARCH}) ++ .Cases("elf64-sw_64", "elf64sw_64", {ELF64LEKind, EM_SW64}) + .Default({ELFNoneKind, EM_NONE}); + + if (ret.first == ELFNoneKind) +@@ -1089,7 +1090,7 @@ static bool getIsRela(opt::InputArgList &args) { + uint16_t m = config->emachine; + 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; ++ m == EM_SW64 || m == EM_X86_64; + } + + static void parseClangOption(StringRef opt, const Twine &msg) { +@@ -1340,6 +1341,16 @@ static void readConfigs(opt::InputArgList &args) { + config->timeTraceEnabled = args.hasArg(OPT_time_trace_eq); + config->timeTraceGranularity = + args::getInteger(args, OPT_time_trace_granularity, 500); ++ ++ config->sw64_tlsrelgot_tlsgd = ++ args.hasFlag(OPT_sw64_tlsrelgot_tlsgd, OPT_no_sw64_tlsrelgot_tlsgd, false); ++ config->sw64_tlsrelgot_tlsldm = ++ args.hasFlag(OPT_sw64_tlsrelgot_tlsldm, OPT_no_sw64_tlsrelgot_tlsldm, false); ++ config->sw64_tlsrelgot_gottprel = ++ args.hasFlag(OPT_sw64_tlsrelgot_gottprel, OPT_no_sw64_tlsrelgot_gottprel, false); ++ config->sw64_tlsrelgot_gotdtprel = ++ args.hasFlag(OPT_sw64_tlsrelgot_gotdtprel, OPT_no_sw64_tlsrelgot_gotdtprel, false); ++ + config->trace = args.hasArg(OPT_trace); + config->undefined = args::getStrings(args, OPT_undefined); + config->undefinedVersion = +@@ -2617,7 +2628,7 @@ void LinkerDriver::link(opt::InputArgList &args) { + // If a --hash-style option was not given, set to a default value, + // which varies depending on the target. + if (!args.hasArg(OPT_hash_style)) { +- if (config->emachine == EM_MIPS) ++ if (config->emachine == EM_MIPS || config->emachine == EM_SW64) + config->sysvHash = true; + else + config->sysvHash = config->gnuHash = true; +diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp +index d96b47b35..8a3c6821b 100644 +--- a/lld/ELF/InputFiles.cpp ++++ b/lld/ELF/InputFiles.cpp +@@ -1594,6 +1594,8 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) { + case Triple::riscv32: + case Triple::riscv64: + return EM_RISCV; ++ case Triple::sw_64: ++ return EM_SW64; + case Triple::x86: + return t.isOSIAMCU() ? EM_IAMCU : EM_386; + case Triple::x86_64: +diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp +index 44444b622..4b72595b0 100644 +--- a/lld/ELF/InputSection.cpp ++++ b/lld/ELF/InputSection.cpp +@@ -462,9 +462,12 @@ void InputSection::copyRelocations(uint8_t *buf, + addend += sec->getFile()->mipsGp0; + } + +- if (RelTy::IsRela) ++ if (RelTy::IsRela) { ++ if (config->emachine == EM_SW64 && ++ (R_SW_64_GPDISP == type || R_SW_64_LITUSE == type)) ++ continue; + p->r_addend = sym.getVA(addend) - section->getOutputSection()->addr; +- else if (config->relocatable && type != target.noneRel) ++ } else if (config->relocatable && type != target.noneRel) + sec->addReloc({R_ABS, type, rel.offset, addend, &sym}); + } else if (config->emachine == EM_PPC && type == R_PPC_PLTREL24 && + p->r_addend >= 0x8000 && sec->file->ppc32Got2) { +@@ -634,6 +637,7 @@ static int64_t getTlsTpOffset(const Symbol &s) { + // Variant 1. + case EM_ARM: + case EM_AARCH64: ++ case EM_SW64: + return s.getVA(0) + config->wordsize * 2 + + ((tls->p_vaddr - config->wordsize * 2) & (tls->p_align - 1)); + case EM_MIPS: +@@ -867,6 +871,21 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, + return in.got->getTlsIndexOff() + a; + case R_TLSLD_PC: + return in.got->getTlsIndexVA() + a - p; ++ case R_SW_HINT_PC: { ++ uint64_t symVA = sym.getVA(a); ++ return 0 == symVA ? 4 : symVA - p; ++ } ++ case R_SW_GOT_OFF: ++ case R_SW_PLT_GOT_OFF: ++ return in.sw64Got->getSymEntryOffset(file, sym, a, expr) - ++ in.sw64Got->getGpOffset(file); ++ case R_SW_GOTREL: ++ return sym.getVA(a) - in.sw64Got->getVA() - in.sw64Got->getGpOffset(file); ++ case R_SW_GOT_GP_PC: ++ return in.sw64Got->getVA() + in.sw64Got->getGpOffset(file) + a - p; ++ case R_SW_TLSGD_GOT: ++ return in.sw64Got->getDynOffset(file, sym) - ++ in.sw64Got->getGpOffset(file); + default: + llvm_unreachable("invalid expression"); + } +diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td +index 0d5c6c3d8..d3cba5b67 100644 +--- a/lld/ELF/Options.td ++++ b/lld/ELF/Options.td +@@ -278,6 +278,10 @@ def library: JoinedOrSeparate<["-"], "l">, MetaVarName<"">, + def library_path: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, + HelpText<"Add to the library search path">; + ++defm literalgot: B<"literalgot", ++ "Support LITERAL_GOT relocation", ++ "Do not support LITERAL_GOT relocation (default)">; ++ + def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">; + + defm Map: Eq<"Map", "Print a link map to the specified file">; +@@ -448,6 +452,19 @@ def : FF<"time-trace">, Alias, + defm time_trace_granularity: EEq<"time-trace-granularity", + "Minimum time granularity (in microseconds) traced by time profiler">; + ++defm sw64_tlsrelgot_tlsgd: B<"sw64_tlsrelgot_tlsgd", ++ "Support TLSREL_GOT relocation on TLSGD", ++ "Do not support TLSREL_GOT relocation on TLSGD (default)">; ++defm sw64_tlsrelgot_tlsldm: B<"sw64_tlsrelgot_tlsldm", ++ "Support TLSREL_GOT relocation on TLSLDM", ++ "Do not support TLSREL_GOT relocation on TLSLDM (default)">; ++defm sw64_tlsrelgot_gottprel: B<"sw64_tlsrelgot_gottprel", ++ "Support TLSREL_GOT relocation on GOTTPREL", ++ "Do not support TLSREL_GOT relocation on GOTTPREL (default)">; ++defm sw64_tlsrelgot_gotdtprel: B<"sw64_tlsrelgot_gotdtprel", ++ "Support TLSREL_GOT relocation on GOTDTPREL", ++ "Do not support TLSREL_GOT relocation on GOTDTPREL (default)">; ++ + defm toc_optimize : BB<"toc-optimize", + "(PowerPC64) Enable TOC related optimizations (default)", + "(PowerPC64) Disable TOC related optimizations">; +@@ -728,12 +745,14 @@ def plugin_opt_eq : J<"plugin-opt=">; + // Options listed below are silently ignored for now for compatibility. + def: Flag<["-"], "d">; + def: Flag<["-"], "g">; ++def: J<"hash-size=">; + def: F<"long-plt">; + def: FF<"no-add-needed">; + def: F<"no-copy-dt-needed-entries">; + def: F<"no-ctors-in-init-array">; + def: F<"no-keep-memory">; + def: F<"no-warn-mismatch">; ++def: F<"reduce-memory-overheads">; + def: Separate<["--", "-"], "rpath-link">; + def: J<"rpath-link=">; + def: F<"secure-plt">; +diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp +index af15f5a92..60263a586 100644 +--- a/lld/ELF/Relocations.cpp ++++ b/lld/ELF/Relocations.cpp +@@ -205,8 +205,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, ++ R_SW_GOT_OFF, R_SW_PLT_GOT_OFF>(expr); + } + + // True if this expression is of the form Sym - X, where X is a position in the +@@ -214,8 +214,8 @@ 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, ++ R_SW_PLT_GOT_OFF>(expr); + } + + static RelExpr toPlt(RelExpr expr) { +@@ -228,6 +228,8 @@ static RelExpr toPlt(RelExpr expr) { + return R_PLT_PC; + case R_ABS: + return R_PLT; ++ case R_SW_GOT_OFF: ++ return R_SW_PLT_GOT_OFF; + default: + return expr; + } +@@ -246,6 +248,8 @@ static RelExpr fromPlt(RelExpr expr) { + return R_PPC64_CALL; + case R_PLT: + return R_ABS; ++ case R_SW_PLT_GOT_OFF: ++ return R_SW_GOT_OFF; + case R_PLT_GOTPLT: + return R_GOTPLTREL; + default: +@@ -853,6 +857,64 @@ RelType RelocationScanner::getMipsN32RelType(RelTy *&rel) const { + return type; + } + ++template ++static RelType getSw64RelType(RelTy *&I, RelTy *const E) { ++ static __thread RelTy *last_rel = nullptr; ++ static __thread RelTy *last_tls_rel = nullptr; ++ RelType Type = R_SW_64_NONE; ++ auto prev = I++; ++ Type = prev->getType(false); ++ switch (0xff & Type) { ++ case R_SW_64_GPDISP: ++ // TODO: Check addend overflow for huge function? ++ Type |= (getAddend(*prev) << 8); ++ break; ++ case R_SW_64_LITERAL: { ++ if (nullptr != last_rel && ++ R_SW_64_LITERAL_GOT == last_rel->getType(false)) { ++ auto addend = last_rel->r_offset - prev->r_offset; ++ // TODO: Check addend overflow. ++ Type |= (addend << 16); ++ } ++ if (E != I && R_SW_64_LITUSE == I->getType(false)) { ++ auto addend = getAddend(*I); ++ // TODO: Check addend overflow. ++ Type |= addend << 8; ++ } ++ } break; ++ case R_SW_64_TLSGD: ++ case R_SW_64_TLSLDM: ++ case R_SW_64_GOTTPREL: ++ case R_SW_64_GOTDTPREL: ++ if (nullptr != last_tls_rel && ++ R_SW_64_TLSREL_GOT == last_tls_rel->getType(false)) { ++ auto addend = last_tls_rel->r_offset - prev->r_offset; ++ // TODO: Check addend overflow. ++ Type |= (addend << 16); ++ } ++ break; ++ case R_SW_64_LITUSE: ++ Type = R_SW_64_NONE; ++ break; ++ case R_SW_64_TLSREL_GOT: ++ if (E != I) { ++ last_tls_rel = prev; ++ return R_SW_64_NONE; ++ } ++ break; ++ case R_SW_64_LITERAL_GOT: ++ if (E != I) { ++ last_rel = prev; ++ return R_SW_64_NONE; ++ } ++ // fallthrough ++ default: ++ break; ++ } ++ last_rel = nullptr; ++ return Type; ++} ++ + template + static void addRelativeReloc(InputSectionBase &isec, uint64_t offsetInSec, + Symbol &sym, int64_t addend, RelExpr expr, +@@ -957,8 +1019,8 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, + 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, +- R_LOONGARCH_PLT_PAGE_PC, R_LOONGARCH_GOT, R_LOONGARCH_GOT_PAGE_PC>( +- e)) ++ R_LOONGARCH_PLT_PAGE_PC, R_LOONGARCH_GOT, R_LOONGARCH_GOT_PAGE_PC, ++ R_SW_GOT_OFF, R_SW_PLT_GOT_OFF, R_SW_GOTREL, R_SW_GOT_GP_PC>(e)) + return true; + + // These never do, except if the entire file is position dependent or if +@@ -1062,6 +1124,8 @@ 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 if (config->emachine == EM_SW64) { ++ in.sw64Got->addEntry(*sec->file, sym, addend, expr); + } 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. +@@ -1145,6 +1209,16 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, + return; + } + ++ if (config->emachine == EM_SW64) { ++ error("can't create dynamic relocation " + toString(type) + " against " + ++ (sym.getName().empty() ? "local symbol" ++ : "symbol: " + toString(sym)) + ++ " in readonly segment; " ++ "pass '-Wl,-z,notext' to allow text relocations in the output" + ++ getLocation(*sec, sym, offset)); ++ return; ++ } ++ + if (sym.isObject()) { + // Produce a copy relocation. + if (auto *ss = dyn_cast(&sym)) { +@@ -1225,6 +1299,39 @@ static unsigned handleMipsTlsRelocation(RelType type, Symbol &sym, + return 0; + } + ++static unsigned handleSw64TlsRelocation(RelType Type, Symbol &Sym, ++ InputSectionBase &C, uint64_t Offset, ++ int64_t Addend, RelExpr Expr) { ++ // Local dynamic. ++ if (oneof(Expr) && R_SW_64_TLSLDM == Type) { ++ // TODO: relax ++ in.sw64Got->addTlsIndex(*C.file); ++ C.relocations.push_back({Expr, Type, Offset, Addend, nullptr}); ++ return target->getTlsGdRelaxSkip(Type); ++ } ++ ++ // Global dynamic. ++ if (oneof(Expr)) { ++ // TODO: relax ++ in.sw64Got->addDynTlsEntry(*C.file, Sym); ++ C.relocations.push_back({Expr, Type, Offset, Addend, &Sym}); ++ return target->getTlsGdRelaxSkip(Type); ++ } ++ ++ if (R_TPREL == Expr) { ++ assert(nullptr == dyn_cast(&Sym)); ++ C.relocations.push_back({Expr, Type, Offset, Addend, &Sym}); ++ return 1; ++ } ++ ++ if (R_DTPREL == Expr) { ++ C.addReloc({Expr, Type, Offset, Addend, &Sym}); ++ return 1; ++ } ++ ++ return 0; ++} ++ + // Notes about General Dynamic and Local Dynamic TLS models below. They may + // require the generation of a pair of GOT entries that have associated dynamic + // relocations. The pair of GOT entries created are of the form GOT[e0] Module +@@ -1247,6 +1354,9 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym, + if (config->emachine == EM_MIPS) + return handleMipsTlsRelocation(type, sym, c, offset, addend, expr); + ++ if (config->emachine == EM_SW64) ++ return handleSw64TlsRelocation(type, sym, c, offset, addend, expr); ++ + if (oneof(expr) && + config->shared) { +@@ -1360,6 +1470,8 @@ template void RelocationScanner::scanOne(RelTy *&i) { + RelType type; + if (config->mipsN32Abi) { + type = getMipsN32RelType(i); ++ } else if (config->emachine == EM_SW64) { ++ type = getSw64RelType(i, static_cast(end)); + } else { + type = rel.getType(config->isMips64EL); + ++i; +@@ -1389,6 +1501,11 @@ template void RelocationScanner::scanOne(RelTy *&i) { + maybeReportUndefined(cast(sym), *sec, offset)) + return; + ++ if (R_SW_HINT_PC == expr) { ++ sec->relocations.push_back({expr, type, offset, addend, &sym}); ++ return; ++ } ++ + if (config->emachine == EM_PPC64) { + // We can separate the small code model relocations into 2 categories: + // 1) Those that access the compiler generated .toc sections. +@@ -1534,7 +1651,7 @@ template void elf::scanRelocations() { + // for -z nocombreloc. MIPS and PPC64 use global states which are not suitable + // for parallelism. + bool serial = !config->zCombreloc || config->emachine == EM_MIPS || +- config->emachine == EM_PPC64; ++ config->emachine == EM_PPC64 || config->emachine == EM_SW64; + parallel::TaskGroup tg; + for (ELFFileBase *f : ctx.objectFiles) { + auto fn = [f]() { +diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h +index e36215bd0..250b290a4 100644 +--- a/lld/ELF/Relocations.h ++++ b/lld/ELF/Relocations.h +@@ -111,6 +111,12 @@ enum RelExpr { + R_LOONGARCH_GOT, + R_LOONGARCH_GOT_PAGE_PC, + R_LOONGARCH_TLSGD_PAGE_PC, ++ R_SW_HINT_PC, ++ R_SW_GOT_OFF, ++ R_SW_PLT_GOT_OFF, ++ R_SW_GOTREL, ++ R_SW_GOT_GP_PC, ++ R_SW_TLSGD_GOT, + }; + + // Architecture-neutral representation of relocation. +diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp +index 3577e78c0..46f92e1ef 100644 +--- a/lld/ELF/ScriptParser.cpp ++++ b/lld/ELF/ScriptParser.cpp +@@ -447,6 +447,7 @@ static std::pair parseBfdName(StringRef s) { + .Case("elf32-msp430", {ELF32LEKind, EM_MSP430}) + .Case("elf32-loongarch", {ELF32LEKind, EM_LOONGARCH}) + .Case("elf64-loongarch", {ELF64LEKind, EM_LOONGARCH}) ++ .Cases("elf64-sw_64", "elf64sw_64", {ELF64LEKind, EM_SW64}) + .Default({ELFNoneKind, EM_NONE}); + } + +diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp +index de25750bf..35571235f 100644 +--- a/lld/ELF/SyntheticSections.cpp ++++ b/lld/ELF/SyntheticSections.cpp +@@ -1129,6 +1129,230 @@ void MipsGotSection::writeTo(uint8_t *buf) { + } + } + ++Sw64GotSection::Sw64GotSection() ++ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize, ++ ".got") {} ++ ++// True if non-preemptable symbol always has the same value regardless of where ++// the DSO is loaded. ++static bool isAbsolute(const Symbol &Sym) { ++ if (Sym.isUndefWeak()) ++ return true; ++ if (const auto *DR = dyn_cast(&Sym)) ++ return DR->section == nullptr; // Absolute symbol. ++ return false; ++} ++ ++void Sw64GotSection::build() { ++ for (auto i = std::begin(Gots); i < std::end(Gots); ++i) { ++ if (nullptr == i->File) ++ continue; ++ for (auto j = std::next(i); j < std::end(Gots); ++j) { ++ if (nullptr == j->File) ++ continue; ++ mergeGots(*i, *j); ++ if (MAX_GOTS <= i->getEntriesNum()) ++ break; ++ } ++ } ++ ++ // Calculate indexes for each GOT entry. ++ size_t Index = 0; ++ uint32_t PltIndex = 0; ++ for (auto &Got : Gots) { ++ if (nullptr == Got.File) ++ continue; ++ Got.StartIndex = Index; ++ for (auto &P : Got.Global) { ++ P.second = Index++; ++ ++ auto Sym = P.first.first; ++ auto Addend = P.first.second; ++ uint64_t Offset = P.second * config->wordsize; ++ mainPart->relaDyn->addReloc( ++ {Sym->isTls() ? target->tlsGotRel : target->gotRel, this, Offset, ++ DynamicReloc::AgainstSymbolWithTargetVA, *Sym, Addend, R_ADDEND}); ++ if (0 != Addend || (Sym->isDefined() && Sym->isWeak())) { ++ relocations.push_back({R_ABS, R_SW_64_REFQUAD, Offset, Addend, Sym}); ++ } ++ } ++ ++ for (auto &P : Got.Jump) { ++ P.second = Index++; ++ ++ auto Sym = P.first.first; ++ ++ auto Addend = P.first.second; ++ uint64_t Offset = P.second * config->wordsize; ++ assert(0 == Addend); ++ in.plt->addEntry(*Sym); ++ Addend += target->pltEntrySize * (PltIndex - Sym->getPltIdx()); ++ relocations.push_back({R_PLT, target->gotRel, Offset, Addend, Sym}); ++ in.relaPlt->addReloc({target->pltRel, this, Offset, ++ DynamicReloc::AgainstSymbolWithTargetVA, *Sym, 0, ++ R_ADDEND}); ++ ++ ++PltIndex; ++ } ++ // Add the only one entry to make it not empty and to compensate ++ // the size when necessary. ++ if (!Got.Jump.empty() && !in.gotPlt->isNeeded()) { ++ in.gotPlt->addEntry(*Got.Jump.front().first.first); ++ } ++ ++ for (auto &P : Got.Local) { ++ P.second = Index++; ++ ++ auto Sym = P.first.first; ++ auto Addend = P.first.second; ++ uint64_t Offset = P.second * config->wordsize; ++ if (!config->isPic || isAbsolute(*Sym)) { ++ relocations.push_back({Sym->isTls() ? R_TPREL : R_ABS, target->gotRel, ++ Offset, Addend, Sym}); ++ } else { ++ mainPart->relaDyn->addReloc( ++ DynamicReloc::AgainstSymbolWithTargetVA, ++ Sym->isTls() ? target->tlsGotRel : target->relativeRel, *this, ++ Offset, *Sym, Addend, R_ABS, target->gotRel); ++ } ++ } ++ ++ for (auto &P : Got.DynTls) { ++ P.second = Index; ++ Index += DYNAMIC_GOT_COUNT; ++ ++ auto Sym = P.first; ++ uint64_t Offset = P.second * config->wordsize; ++ uint64_t Offset2 = Offset + config->wordsize; ++ if (config->shared || nullptr == Sym || Sym->isPreemptible) { ++ if (nullptr == Sym) { ++ mainPart->relaDyn->addReloc({R_SW_64_DTPMOD64, this, Offset, ++ DynamicReloc::AddendOnly, *Sym, 0, ++ R_ADDEND}); ++ continue; ++ } ++ mainPart->relaDyn->addReloc({R_SW_64_DTPMOD64, this, Offset, ++ DynamicReloc::AgainstSymbol, *Sym, 0, ++ R_ADDEND}); ++ } ++ if (Sym->isPreemptible) { ++ mainPart->relaDyn->addReloc({R_SW_64_DTPREL64, this, Offset2, ++ DynamicReloc::AgainstSymbol, *Sym, 0, ++ R_ADDEND}); ++ } else { ++ if (!config->shared) { ++ relocations.push_back({ ++ R_ABS, ++ R_SW_64_DTPMOD64, ++ Offset, ++ 1, ++ Sym, ++ }); ++ } ++ relocations.push_back({ ++ R_ABS, ++ R_SW_64_TPREL64, ++ Offset2, ++ 0, ++ Sym, ++ }); ++ } ++ } ++ } ++} ++ ++bool Sw64GotSection::updateAllocSize() { ++ Size = 0; ++ for (const FileGot &G : Gots) { ++ if (nullptr == G.File) ++ continue; ++ Size += G.getEntriesNum() * config->wordsize; ++ } ++ return false; ++} ++ ++size_t Sw64GotSection::FileGot::getEntriesNum() const { ++ return Local.size() + Global.size() + Jump.size() + ++ DynTls.size() * DYNAMIC_GOT_COUNT; ++} ++ ++void Sw64GotSection::addEntry(InputFile &File, Symbol &Sym, int64_t Addend, ++ RelExpr Expr) { ++ FileGot &G = getGot(File); ++ if (Sym.isPreemptible) { ++ auto &Entries = R_SW_PLT_GOT_OFF == Expr ? G.Jump : G.Global; ++ Entries.insert({{&Sym, Addend}, 0}); ++ } else { ++ G.Local.insert({{&Sym, Addend}, 0}); ++ } ++} ++ ++uint64_t Sw64GotSection::getSymEntryOffset(const InputFile *F, const Symbol &S, ++ int64_t Addend, ++ RelExpr Expr) const { ++ const FileGot &G = Gots[F->mipsGotIndex]; ++ Symbol *Sym = const_cast(&S); ++ if (Sym->isPreemptible) { ++ auto &Entries = R_SW_PLT_GOT_OFF == Expr ? G.Jump : G.Global; ++ return Entries.lookup({Sym, Addend}) * config->wordsize; ++ } ++ return G.Local.lookup({Sym, Addend}) * config->wordsize; ++} ++ ++void Sw64GotSection::addDynTlsEntry(InputFile &File, Symbol &Sym) { ++ getGot(File).DynTls.insert({&Sym, 0}); ++} ++ ++void Sw64GotSection::addTlsIndex(InputFile &File) { ++ getGot(File).DynTls.insert({nullptr, 0}); ++} ++ ++uint64_t Sw64GotSection::getDynOffset(const InputFile *F, ++ const Symbol &S) const { ++ const FileGot &G = Gots[F->mipsGotIndex]; ++ Symbol *Sym = const_cast(&S); ++ return G.DynTls.lookup(Sym) * config->wordsize; ++} ++ ++uint64_t Sw64GotSection::getGpOffset(const InputFile *F) const { ++ uint64_t offset = GP_OFFSET; ++ if (F && uint32_t(-1) != F->mipsGotIndex) { ++ offset += Gots[F->mipsGotIndex].StartIndex * config->wordsize; ++ } ++ return offset; ++} ++ ++Sw64GotSection::FileGot &Sw64GotSection::getGot(InputFile &F) { ++ if (uint32_t(-1) == F.mipsGotIndex) { ++ Gots.emplace_back(); ++ Gots.back().File = &F; ++ F.mipsGotIndex = Gots.size() - 1; ++ } ++ return Gots[F.mipsGotIndex]; ++} ++ ++void Sw64GotSection::writeTo(uint8_t *buf) { ++ target->relocateAlloc(*this, buf); ++} ++ ++bool Sw64GotSection::mergeGots(FileGot &Dst, FileGot &Src) { ++ assert(nullptr != Dst.File); ++ ++ // TODO: Do an optimistic merge. ++ if (MAX_GOTS < Dst.getEntriesNum() + Src.getEntriesNum()) ++ return false; ++ ++ set_union(Dst.Local, Src.Local); ++ set_union(Dst.Global, Src.Global); ++ set_union(Dst.Jump, Src.Jump); ++ set_union(Dst.DynTls, Src.DynTls); ++ Src.File->mipsGotIndex = Dst.File->mipsGotIndex; ++ Src.File = nullptr; ++ ++ assert(Dst.getEntriesNum() <= MAX_GOTS); ++ return true; ++} ++ + // On PowerPC the .plt section is used to hold the table of function addresses + // instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss + // section. I don't know why we have a BSS style type for the section but it is +@@ -1441,6 +1665,9 @@ DynamicSection::computeContents() { + break; + } + addInt(DT_PLTREL, config->isRela ? DT_RELA : DT_REL); ++ if (config->emachine == EM_SW64) { ++ addInt(/* SW_64_PLTRO */ DT_LOPROC, 1); ++ } + } + + if (config->emachine == EM_AARCH64) { +@@ -2224,6 +2451,11 @@ template void SymbolTableSection::writeTo(uint8_t *buf) { + eSym->st_name = ent.strTabOffset; + eSym->setBindingAndType(sym->binding, sym->type); + eSym->st_other = sym->stOther; ++ if (config->emachine == EM_SW64) { ++ // It's not clear what the flags `0x88` mean, but `bfd` doesn't copy it. ++ // So we mimic the behaviour of `bfd`. ++ eSym->st_other &= ~0x88; ++ } + + if (BssSection *commonSec = getCommonSec(sym)) { + // When -r is specified, a COMMON symbol is not allocated. Its st_shndx +diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h +index 38d0c80a0..29f932ccc 100644 +--- a/lld/ELF/SyntheticSections.h ++++ b/lld/ELF/SyntheticSections.h +@@ -347,7 +347,59 @@ private: + // Try to merge two GOTs. In case of success the `Dst` contains + // result of merging and the function returns true. In case of + // overflow the `Dst` is unchanged and the function returns false. +- bool tryMergeGots(FileGot & dst, FileGot & src, bool isPrimary); ++ bool tryMergeGots(FileGot &dst, FileGot &src, bool isPrimary); ++}; ++ ++class Sw64GotSection final : public SyntheticSection { ++public: ++ static constexpr int64_t GP_OFFSET = 0x8000; ++ static constexpr size_t DYNAMIC_GOT_COUNT = 2; ++ Sw64GotSection(); ++ ++ void writeTo(uint8_t *Buf) override; ++ size_t getSize() const override { return Size; } ++ bool updateAllocSize() override; ++ void finalizeContents() override { updateAllocSize(); } ++ ++ // Join separate GOTs built for each input file. ++ void build(); ++ ++ void addEntry(InputFile &File, Symbol &Sym, int64_t Addend, RelExpr Expr); ++ uint64_t getSymEntryOffset(const InputFile *F, const Symbol &S, ++ int64_t Addend, RelExpr Expr) const; ++ void addDynTlsEntry(InputFile &File, Symbol &Sym); ++ uint64_t getDynOffset(const InputFile *F, const Symbol &S) const; ++ void addTlsIndex(InputFile &File); ++ // Return gp value for particular input file. ++ uint64_t getGpOffset(const InputFile *F = nullptr) const; ++ ++private: ++ uint64_t Size; ++ ++ // Symbol and addend. ++ typedef std::pair GotEntry; ++ ++ struct FileGot { ++ InputFile *File = nullptr; ++ size_t StartIndex = 0; ++ ++ llvm::MapVector Local; ++ llvm::MapVector Global; ++ llvm::MapVector Jump; ++ llvm::MapVector DynTls; ++ ++ // Total number of all entries. ++ size_t getEntriesNum() const; ++ }; ++ ++ // Container of GOT created for each input file. ++ std::vector Gots; ++ ++ // Return (and create if necessary) `FileGot`. ++ FileGot &getGot(InputFile &F); ++ ++ static constexpr size_t MAX_GOTS = 1 << 13; ++ static bool mergeGots(FileGot &Dst, FileGot &Src); + }; + + class GotPltSection final : public SyntheticSection { +@@ -709,6 +761,7 @@ public: + HashTableSection(); + void finalizeContents() override; + void writeTo(uint8_t *buf) override; ++ template void writeTo(uint8_t *Buf); + size_t getSize() const override { return size; } + + private: +@@ -1321,6 +1374,7 @@ struct InStruct { + std::unique_ptr relaIplt; + std::unique_ptr shStrTab; + std::unique_ptr strTab; ++ std::unique_ptr sw64Got; + std::unique_ptr symTab; + std::unique_ptr symTabShndx; + +diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp +index 32bb2164a..78f73c432 100644 +--- a/lld/ELF/Target.cpp ++++ b/lld/ELF/Target.cpp +@@ -89,6 +89,8 @@ TargetInfo *elf::getTarget() { + return getSPARCV9TargetInfo(); + case EM_X86_64: + return getX86_64TargetInfo(); ++ case EM_SW64: ++ return getSw_64TargetInfo(); + } + llvm_unreachable("unknown target machine"); + } +diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h +index aeabe47f9..ee3c3994b 100644 +--- a/lld/ELF/Target.h ++++ b/lld/ELF/Target.h +@@ -190,6 +190,7 @@ TargetInfo *getSPARCV9TargetInfo(); + TargetInfo *getX86TargetInfo(); + TargetInfo *getX86_64TargetInfo(); + template TargetInfo *getMipsTargetInfo(); ++template TargetInfo *getSw_64TargetInfo(); + + struct ErrorPlace { + InputSectionBase *isec; +diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp +index dd37bbbf7..2513bc9ab 100644 +--- a/lld/ELF/Writer.cpp ++++ b/lld/ELF/Writer.cpp +@@ -434,6 +434,9 @@ template void elf::createSyntheticSections() { + if (config->emachine == EM_MIPS) { + in.mipsGot = std::make_unique(); + add(*in.mipsGot); ++ } else if (config->emachine == EM_SW64) { ++ in.sw64Got = std::make_unique(); ++ add(*in.sw64Got); + } else { + in.got = std::make_unique(); + add(*in.got); +@@ -850,6 +853,9 @@ enum RankFlags { + RF_NOT_RELRO = 1 << 9, + RF_NOT_TLS = 1 << 8, + RF_BSS = 1 << 7, ++ ++ RF_SW_64_GPREL = 1 << 2, ++ RF_SW_64_GOT = 1 << 1, + }; + + static unsigned getSectionRank(const OutputSection &osec) { +@@ -928,6 +934,16 @@ static unsigned getSectionRank(const OutputSection &osec) { + if (osec.type == SHT_NOBITS) + rank |= RF_BSS; + ++ if (config->emachine == EM_SW64) { ++ StringRef name = osec.name; ++ // Matching bfd section layout to make `gprel16` works as bfd. ++ if (".got" == name) { ++ rank |= RF_SW_64_GOT; ++ } else if (".sdata" == name || ".sbss" == name) { ++ rank |= RF_SW_64_GPREL; ++ } ++ } ++ + // Some architectures have additional ordering restrictions for sections + // within the same PT_LOAD. + if (config->emachine == EM_PPC64) { +@@ -1955,6 +1971,18 @@ template void Writer::finalizeSections() { + + { + llvm::TimeTraceScope timeScope("Add symbols to symtabs"); ++ ++ if (nullptr != in.sw64Got) { ++ in.sw64Got->build(); ++ if (in.plt->isNeeded()) { ++ // Make it compatible with bfd. ++ Symbol *s = symtab.addSymbol(Defined{ ++ /*file*/ nullptr, "_PROCEDURE_LINKAGE_TABLE_", STB_LOCAL, ++ STV_DEFAULT, STT_OBJECT, /*value*/ 0, /*size*/ 0, in.plt.get()}); ++ s->isUsedInRegularObj = true; ++ } ++ } ++ + // Now that we have defined all possible global symbols including linker- + // synthesized ones. Visit all symbols to give the finishing touches. + for (Symbol *sym : symtab.getSymbols()) { +@@ -2073,6 +2101,7 @@ template void Writer::finalizeSections() { + finalizeSynthetic(in.strTab.get()); + finalizeSynthetic(in.got.get()); + finalizeSynthetic(in.mipsGot.get()); ++ finalizeSynthetic(in.sw64Got.get()); + finalizeSynthetic(in.igotPlt.get()); + finalizeSynthetic(in.gotPlt.get()); + finalizeSynthetic(in.relaIplt.get()); +@@ -2678,9 +2707,11 @@ template void Writer::setPhdrs(Partition &part) { + // musl/glibc ld.so rounds the size down, so we need to round up + // to protect the last page. This is a no-op on FreeBSD which always + // rounds up. +- p->p_memsz = +- alignToPowerOf2(p->p_offset + p->p_memsz, config->commonPageSize) - +- p->p_offset; ++ if (config->emachine != EM_SW64) { ++ p->p_memsz = ++ alignToPowerOf2(p->p_offset + p->p_memsz, config->commonPageSize) - ++ p->p_offset; ++ } + } + } + } +-- +2.33.0 + diff --git a/lld.spec b/lld.spec index 90886de..378bc2a 100644 --- a/lld.spec +++ b/lld.spec @@ -1,4 +1,4 @@ -%define anolis_release 3 +%define anolis_release 4 %global toolchain clang %undefine _include_frame_pointers @@ -36,6 +36,9 @@ Patch9: 0009-lld-ELF-Add-a-corner-testcase-for-elf-getLoongArchPa.patch Patch10: 0010-lld-LoongArch-Add-a-another-corner-testcase-for-elf-.patch Patch11: 0011-lld-LoongArch-Handle-extreme-code-model-relocs-accor.patch +# Patches for Sw64 +Patch12: 0012-Sw64-Add-Sw64-target-support-for-lld.patch + BuildRequires: clang BuildRequires: cmake BuildRequires: ninja-build @@ -150,6 +153,9 @@ fi %doc README.md CODE_OWNERS.TXT docs/README.txt docs/ReleaseNotes.rst %changelog +* Fri May 16 2025 swcompiler - 17.0.6-4 +- Add Sw64 support for lld + * Thu Oct 31 2024 Chen Li - 17.0.6-3 - LoongArch Backport: Add support for R_LARCH_CALL36 -- Gitee