Skip to content

[LoongArch] Add reloc types for LA32R/LA32S #146499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions lld/ELF/Arch/LoongArch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ static uint32_t setJ5(uint32_t insn, uint32_t imm) {
return (insn & 0xfffffc1f) | (extractBits(imm, 4, 0) << 5);
}

static uint32_t setK10(uint32_t insn, uint32_t imm) {
return (insn & 0xffc003ff) | (extractBits(imm, 9, 0) << 10);
}

static uint32_t setK12(uint32_t insn, uint32_t imm) {
return (insn & 0xffc003ff) | (extractBits(imm, 11, 0) << 10);
}
Expand Down Expand Up @@ -416,6 +420,8 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
// [1]: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=9f482b73f41a9a1bbfb173aad0733d1c824c788a
// [2]: https://github.com/loongson/la-abi-specs/pull/3
return isJirl(read32le(loc)) ? R_PLT : R_ABS;
case R_LARCH_PCADD_LO12_I:
return RE_LOONGARCH_PC_INDIRECT;
case R_LARCH_TLS_DTPREL32:
case R_LARCH_TLS_DTPREL64:
return R_DTPREL;
Expand Down Expand Up @@ -446,10 +452,12 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_32_PCREL:
case R_LARCH_64_PCREL:
case R_LARCH_PCREL20_S2:
case R_LARCH_PCADD_HI20:
return R_PC;
case R_LARCH_B16:
case R_LARCH_B21:
case R_LARCH_B26:
case R_LARCH_CALL30:
case R_LARCH_CALL36:
return R_PLT_PC;
case R_LARCH_GOT_PC_HI20:
Expand All @@ -459,6 +467,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_IE64_PC_HI12:
return RE_LOONGARCH_GOT_PAGE_PC;
case R_LARCH_PCADD_GOT_HI20:
case R_LARCH_PCADD_TLS_IE_HI20:
return R_GOT_PC;
case R_LARCH_GOT_PC_LO12:
case R_LARCH_TLS_IE_PC_LO12:
return RE_LOONGARCH_GOT;
Expand Down Expand Up @@ -522,6 +533,7 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_DESC_LO12:
case R_LARCH_TLS_DESC64_LO20:
case R_LARCH_TLS_DESC64_HI12:
case R_LARCH_PCADD_TLS_DESC_HI20:
return R_TLSDESC;
case R_LARCH_TLS_DESC_CALL:
return R_TLSDESC_CALL;
Expand Down Expand Up @@ -605,6 +617,22 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
write32le(loc, setD10k16(read32le(loc), val >> 2));
return;

case R_LARCH_CALL30: {
// This relocation is designed for adjacent pcaddu12i+jirl pairs that
// are patched in one time.
// The relocation range is [-4G, +4G) (of course must be 4-byte aligned).
if ((int64_t)val != llvm::SignExtend64(val, 32))
reportRangeError(ctx, loc, rel, Twine(val), llvm::minIntN(32),
llvm::maxIntN(32));
checkAlignment(ctx, loc, val, 4, rel);
uint32_t hi20 = extractBits(val, 31, 12);
// Despite the name, the lower part is actually 12 bits with 4-byte aligned.
uint32_t lo10 = extractBits(val, 11, 2);
write32le(loc, setJ20(read32le(loc), hi20));
write32le(loc + 4, setK10(read32le(loc + 4), lo10));
return;
}

case R_LARCH_CALL36: {
// This relocation is designed for adjacent pcaddu18i+jirl pairs that
// are patched in one time. Because of sign extension of these insns'
Expand Down Expand Up @@ -648,6 +676,7 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
case R_LARCH_TLS_LE_LO12_R:
case R_LARCH_TLS_DESC_PC_LO12:
case R_LARCH_TLS_DESC_LO12:
case R_LARCH_PCADD_LO12_I:
write32le(loc, setK12(read32le(loc), extractBits(val, 11, 0)));
return;

Expand All @@ -667,6 +696,15 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
case R_LARCH_TLS_DESC_HI20:
write32le(loc, setJ20(read32le(loc), extractBits(val, 31, 12)));
return;
case R_LARCH_PCADD_HI20:
case R_LARCH_PCADD_GOT_HI20:
case R_LARCH_PCADD_TLS_IE_HI20:
case R_LARCH_PCADD_TLS_DESC_HI20: {
uint64_t hi = val + 0x800;
checkInt(ctx, loc, SignExtend64(hi, 32) >> 12, 20, rel);
write32le(loc, setJ20(read32le(loc), extractBits(hi, 31, 12)));
return;
}
case R_LARCH_TLS_LE_HI20_R:
write32le(loc, setJ20(read32le(loc), extractBits(val + 0x800, 31, 12)));
return;
Expand Down
60 changes: 60 additions & 0 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,61 @@ static Relocation *getRISCVPCRelHi20(Ctx &ctx, const InputSectionBase *loSec,
return nullptr;
}

// For RE_LARCH_PC_INDIRECT (R_LARCH_PCADD_LO12_I), the symbol actually
// points the corresponding R_LARCH_PCADD_*_HI20 relocation, and the target VA
// is calculated using PCADD_HI20's symbol.
//
// This function returns the R_LARCH_PCADD_*_HI20 relocation from the
// R_LARCH_PCADD_LO12 relocation.
static Relocation *getLoongArchPCAddHi20(Ctx &ctx,
const InputSectionBase *loSec,
const Relocation &loReloc) {
int64_t addend = loReloc.addend;
Symbol *sym = loReloc.sym;

const Defined *d = cast<Defined>(sym);
if (!d->section) {
Err(ctx) << loSec->getLocation(loReloc.offset)
<< ": R_LARCH_PCADD_LO12 relocation points to an absolute symbol: "
<< sym->getName();
return nullptr;
}
InputSection *hiSec = cast<InputSection>(d->section);

if (hiSec != loSec)
Err(ctx) << loSec->getLocation(loReloc.offset)
<< ": R_LARCH_PCADD_LO12 relocation points to a symbol '"
<< sym->getName() << "' in a different section '" << hiSec->name
<< "'";

if (addend != 0)
Warn(ctx) << loSec->getLocation(loReloc.offset)
<< ": non-zero addend in R_LARCH_PCADD_LO12 relocation to "
<< hiSec->getObjMsg(d->value) << " is ignored";

// Relocations are sorted by offset, so we can use std::equal_range to do
// binary search.
Relocation hiReloc;
hiReloc.offset = d->value + addend;
auto range =
std::equal_range(hiSec->relocs().begin(), hiSec->relocs().end(), hiReloc,
[](const Relocation &lhs, const Relocation &rhs) {
return lhs.offset < rhs.offset;
});

for (auto it = range.first; it != range.second; ++it)
if (it->type == R_LARCH_PCADD_HI20 || it->type == R_LARCH_PCADD_GOT_HI20 ||
it->type == R_LARCH_PCADD_TLS_IE_HI20 ||
it->type == R_LARCH_PCADD_TLS_DESC_HI20)
return &*it;

Err(ctx) << loSec->getLocation(loReloc.offset)
<< ": R_LARCH_PCADD_LO12 relocation points to "
<< hiSec->getObjMsg(d->value)
<< " without an associated R_LARCH_PCADD_HI20 relocation";
return nullptr;
}

// A TLS symbol's virtual address is relative to the TLS segment. Add a
// target-specific adjustment to produce a thread-pointer-relative offset.
static int64_t getTlsTpOffset(Ctx &ctx, const Symbol &s) {
Expand Down Expand Up @@ -884,6 +939,11 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx));
return 0;
}
case RE_LOONGARCH_PC_INDIRECT: {
if (const Relocation *hiRel = getLoongArchPCAddHi20(ctx, this, r))
return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx, a));
return 0;
}
case RE_LOONGARCH_PAGE_PC:
return getLoongArchPageDelta(r.sym->getVA(ctx, a), p, r.type);
case R_PC:
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ static bool isRelExpr(RelExpr expr) {
return oneof<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
RE_PPC64_CALL, RE_PPC64_RELAX_TOC, RE_AARCH64_PAGE_PC,
R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, RE_PPC64_RELAX_GOT_PC,
RE_LOONGARCH_PAGE_PC>(expr);
RE_LOONGARCH_PAGE_PC, RE_LOONGARCH_PC_INDIRECT>(expr);
}

static RelExpr toPlt(RelExpr expr) {
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Relocations.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ enum RelExpr {
// also reused for TLS, making the semantics differ from other architectures.
RE_LOONGARCH_GOT,
RE_LOONGARCH_GOT_PAGE_PC,
RE_LOONGARCH_PC_INDIRECT,
RE_LOONGARCH_TLSGD_PAGE_PC,
RE_LOONGARCH_TLSDESC_PAGE_PC,
};
Expand Down
64 changes: 64 additions & 0 deletions lld/test/ELF/loongarch-call30.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# REQUIRES: loongarch

# RUN: rm -rf %t && split-file %s %t
# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %t/a.s -o %t/a.o

# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21020 -o %t/exe1
# RUN: llvm-objdump --no-show-raw-insn -d %t/exe1 | FileCheck --match-full-lines %s --check-prefix=EXE1
## hi20 = target - pc >> 12 = 0x21020 - 0x20010 >> 12 = 1
## lo12 = target - pc & (1 << 12) - 1 = 0x21020 - 0x20010 & 0xfff = 16
# EXE1: 20010: pcaddu12i $t0, 1
# EXE1-NEXT: 20014: jirl $zero, $t0, 16

# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21820 -o %t/exe2
# RUN: llvm-objdump --no-show-raw-insn -d %t/exe2 | FileCheck --match-full-lines %s --check-prefix=EXE2
## hi20 = target - pc >> 12 = 0x21820 - 0x20010 >> 12 = 1
## lo12 = target - pc & (1 << 12) - 1 = 0x21820 - 0x20010 & 0xfff = 2064
# EXE2: 20010: pcaddu12i $t0, 1
# EXE2-NEXT: 20014: jirl $zero, $t0, 2064

# RUN: ld.lld %t/a.o -shared -T %t/a.t -o %t/a.so
# RUN: llvm-readelf -x .got.plt %t/a.so | FileCheck --check-prefix=GOTPLT %s
# RUN: llvm-objdump -d --no-show-raw-insn %t/a.so | FileCheck --check-prefix=SO %s
## PLT should be present in this case.
# SO: Disassembly of section .plt:
# SO: <.plt>:
## foo@plt:
# SO: 1234520: pcaddu12i $t3, 64{{$}}
# SO-NEXT: ld.w $t3, $t3, 444{{$}}
# SO-NEXT: jirl $t1, $t3, 0
# SO-NEXT: nop

# SO: Disassembly of section .text:
# SO: <_start>:
## hi20 = foo@plt - pc >> 12 = 0x1234520 - 0x1274670 >> 12 = -65
## lo18 = foo@plt - pc & (1 << 12) - 1 = 0x1234520 - 0x1274670 & 0xfff = 3760
# SO-NEXT: pcaddu12i $t0, -65{{$}}
# SO-NEXT: jirl $zero, $t0, 3760{{$}}

# GOTPLT: section '.got.plt':
# GOTPLT-NEXT: 0x012746d4 00000000 00000000 00452301

## Impossible case in reality becasue all LoongArch instructions are fixed 4-bytes long.
# RUN: not ld.lld %t/a.o --section-start=.text=0x20000 --section-start=.sec.foo=0x40001 -o /dev/null 2>&1 | \
# RUN: FileCheck -DFILE=%t/a.o --check-prefix=ERROR-ALIGN %s
# ERROR-ALIGN: error: [[FILE]]:(.text+0x0): improper alignment for relocation R_LARCH_CALL30: 0x20001 is not aligned to 4 bytes

#--- a.t
SECTIONS {
.plt 0x1234500: { *(.plt) }
.text 0x1274670: { *(.text) }
}

#--- a.s
.text
.global _start
_start:
.reloc ., R_LARCH_CALL30, foo
pcaddu12i $t0, 0
jirl $zero, $t0, 0

.section .sec.foo,"awx"
.global foo
foo:
ret
4 changes: 2 additions & 2 deletions lld/test/ELF/loongarch-relax-align.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# REQUIRES: loongarch

# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.64.o
# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+32s,+relax %s -o %t.64.o
# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 %t.32.o -o %t.32
# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 %t.64.o -o %t.64
# RUN: ld.lld --section-start=.text=0x10000 --section-start=.text2=0x20000 -e 0 --no-relax %t.32.o -o %t.32n
Expand Down
2 changes: 1 addition & 1 deletion lld/test/ELF/loongarch-relax-emit-relocs.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# REQUIRES: loongarch
## Test that we can handle --emit-relocs while relaxing.

# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax --defsym ELF64=1 %s -o %t.64.o
# RUN: ld.lld -Ttext=0x10000 -section-start=.got=0x20000 --emit-relocs %t.32.o -o %t.32
# RUN: ld.lld -Ttext=0x10000 -section-start=.got=0x20000 --emit-relocs %t.64.o -o %t.64
Expand Down
4 changes: 2 additions & 2 deletions lld/test/ELF/loongarch-relax-pc-hi20-lo12-got-symbols.s
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
# REQUIRES: loongarch
# RUN: rm -rf %t && split-file %s %t && cd %t

# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax symbols.s -o symbols.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s,+relax symbols.s -o symbols.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax symbols.s -o symbols.64.o
# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax abs.s -o abs.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s,+relax abs.s -o abs.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax abs.s -o abs.64.o

# RUN: ld.lld --shared -Tlinker.t symbols.32.o abs.32.o -o symbols.32.so
Expand Down
2 changes: 1 addition & 1 deletion lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# REQUIRES: loongarch

# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s,+relax %s -o %t.32.o
# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax %s -o %t.64.o

# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.32.o -o %t.32
Expand Down
4 changes: 2 additions & 2 deletions lld/test/ELF/loongarch-tls-gd-edge-case.s
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
# LA64-REL-NEXT: 00000000000203a8 0000000200000009 R_LARCH_TLS_DTPREL64 0000000000000000 y + 0
# LA64-REL-NEXT: 00000000000203b0 000000020000000b R_LARCH_TLS_TPREL64 0000000000000000 y + 0

# LA32: 101d4: pcalau12i $a0, 16
# LA32-NEXT: ld.w $a0, $a0, 580
# LA32: 101d4: pcaddu12i $a0, 16
# LA32-NEXT: ld.w $a0, $a0, 112
# LA32-NEXT: pcalau12i $a1, 16
# LA32-NEXT: addi.w $a1, $a1, 572

Expand Down
19 changes: 10 additions & 9 deletions lld/test/ELF/loongarch-tls-ie.s
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@
## LA32:
## &.got[0] - . = 0x20214 - 0x101a4: 0x10 pages, page offset 0x214
## &.got[1] - . = 0x20218 - 0x101b0: 0x10 pages, page offset 0x218
# IE32: 101a4: pcalau12i $a4, 16
# IE32-NEXT: ld.w $a4, $a4, 532
# IE32: 101a4: pcaddu12i $a4, 16
# IE32-NEXT: ld.w $a4, $a4, 112
# IE32-NEXT: add.w $a4, $a4, $tp
# IE32-NEXT: 101b0: pcalau12i $a5, 16
# IE32-NEXT: ld.w $a5, $a5, 536
# IE32: 101b0: pcaddu12i $a5, 16
# IE32-NEXT: ld.w $a5, $a5, 104
# IE32-NEXT: add.w $a5, $a5, $tp

## LA64:
Expand All @@ -62,15 +62,16 @@

# a@tprel = st_value(a) = 0x8
# b@tprel = st_value(a) = 0xc
# LE32-GOT: could not find section '.got'
# LE32-GOT: section '.got':
# LE32-GOT-NEXT: 0x0003012c 08000000 0c000000
# LE64-GOT: could not find section '.got'

## LA32:
# LE32: 200d4: nop
# LE32-NEXT: ori $a4, $zero, 8
# LE32: 20114: pcaddu12i $a4, 16
# LE32-NEXT: ld.w $a4, $a4, 24
# LE32-NEXT: add.w $a4, $a4, $tp
# LE32-NEXT: 200e0: nop
# LE32-NEXT: ori $a5, $zero, 12
# LE32: 20120: pcaddu12i $a5, 16
# LE32-NEXT: ld.w $a5, $a5, 16
# LE32-NEXT: add.w $a5, $a5, $tp

## LA64:
Expand Down
11 changes: 11 additions & 0 deletions llvm/include/llvm/BinaryFormat/ELFRelocs/LoongArch.def
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,14 @@ ELF_RELOC(R_LARCH_TLS_LE_LO12_R, 123)
ELF_RELOC(R_LARCH_TLS_LD_PCREL20_S2, 124)
ELF_RELOC(R_LARCH_TLS_GD_PCREL20_S2, 125)
ELF_RELOC(R_LARCH_TLS_DESC_PCREL20_S2, 126)

// Relocs added in ELF for the LoongArch™ Architecture v2025????, part of the
// v2.40 LoongArch ABI specs.
//
// Spec addition: https://github.com/loongson/la-abi-specs/pull/16
ELF_RELOC(R_LARCH_CALL30, 127)
ELF_RELOC(R_LARCH_PCADD_HI20, 128)
ELF_RELOC(R_LARCH_PCADD_GOT_HI20, 129)
ELF_RELOC(R_LARCH_PCADD_TLS_IE_HI20, 130)
ELF_RELOC(R_LARCH_PCADD_TLS_DESC_HI20, 131)
ELF_RELOC(R_LARCH_PCADD_LO12_I, 132)
Loading
Loading