Skip to content

Commit 6084ee7

Browse files
xen0nSixWeining
authored andcommitted
[lld][ELF] Support LoongArch
This adds support for the LoongArch ELF psABI v2.00 [1] relocation model to LLD. The deprecated stack-machine-based psABI v1 relocs are not supported. The code is tested by successfully bootstrapping a Gentoo/LoongArch stage3, complete with common GNU userland tools and both the LLVM and GNU toolchains (GNU toolchain is present only for building glibc, LLVM+Clang+LLD are used for the rest). Large programs like QEMU are tested to work as well. [1]: https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html Reviewed By: MaskRay, SixWeining Differential Revision: https://reviews.llvm.org/D138135
1 parent 4cf11d8 commit 6084ee7

29 files changed

+2064
-26
lines changed

lld/ELF/Arch/LoongArch.cpp

+687
Large diffs are not rendered by default.

lld/ELF/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_lld_library(lldELF
2525
Arch/ARM.cpp
2626
Arch/AVR.cpp
2727
Arch/Hexagon.cpp
28+
Arch/LoongArch.cpp
2829
Arch/Mips.cpp
2930
Arch/MipsArchTree.cpp
3031
Arch/MSP430.cpp

lld/ELF/Driver.cpp

+8-4
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef emul) {
180180
.Case("elf32lriscv", {ELF32LEKind, EM_RISCV})
181181
.Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC})
182182
.Cases("elf32lppc", "elf32lppclinux", {ELF32LEKind, EM_PPC})
183+
.Case("elf32loongarch", {ELF32LEKind, EM_LOONGARCH})
183184
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
184185
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
185186
.Case("elf64lriscv", {ELF64LEKind, EM_RISCV})
@@ -191,6 +192,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef emul) {
191192
.Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9})
192193
.Case("msp430elf", {ELF32LEKind, EM_MSP430})
193194
.Case("elf64_amdgpu", {ELF64LEKind, EM_AMDGPU})
195+
.Case("elf64loongarch", {ELF64LEKind, EM_LOONGARCH})
194196
.Default({ELFNoneKind, EM_NONE});
195197

196198
if (ret.first == ELFNoneKind)
@@ -1085,8 +1087,9 @@ static bool getIsRela(opt::InputArgList &args) {
10851087

10861088
// Otherwise use the psABI defined relocation entry format.
10871089
uint16_t m = config->emachine;
1088-
return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || m == EM_PPC ||
1089-
m == EM_PPC64 || m == EM_RISCV || m == EM_X86_64;
1090+
return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON ||
1091+
m == EM_LOONGARCH || m == EM_PPC || m == EM_PPC64 || m == EM_RISCV ||
1092+
m == EM_X86_64;
10901093
}
10911094

10921095
static void parseClangOption(StringRef opt, const Twine &msg) {
@@ -1693,8 +1696,9 @@ static void setConfigs(opt::InputArgList &args) {
16931696
// have support for reading Elf_Rel addends, so we only enable for a subset.
16941697
#ifndef NDEBUG
16951698
bool checkDynamicRelocsDefault = m == EM_AARCH64 || m == EM_ARM ||
1696-
m == EM_386 || m == EM_MIPS ||
1697-
m == EM_X86_64 || m == EM_RISCV;
1699+
m == EM_386 || m == EM_LOONGARCH ||
1700+
m == EM_MIPS || m == EM_RISCV ||
1701+
m == EM_X86_64;
16981702
#else
16991703
bool checkDynamicRelocsDefault = false;
17001704
#endif

lld/ELF/InputFiles.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,9 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) {
15751575
return EM_AVR;
15761576
case Triple::hexagon:
15771577
return EM_HEXAGON;
1578+
case Triple::loongarch32:
1579+
case Triple::loongarch64:
1580+
return EM_LOONGARCH;
15781581
case Triple::mips:
15791582
case Triple::mipsel:
15801583
case Triple::mips64:

lld/ELF/InputSection.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ static int64_t getTlsTpOffset(const Symbol &s) {
610610
// to allow a signed 16-bit offset to reach 0x1000 of TCB/thread-library
611611
// data and 0xf000 of the program's TLS segment.
612612
return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)) - 0x7000;
613+
case EM_LOONGARCH:
613614
case EM_RISCV:
614615
return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1));
615616

@@ -644,6 +645,14 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
644645
case R_GOT:
645646
case R_RELAX_TLS_GD_TO_IE_ABS:
646647
return sym.getGotVA() + a;
648+
case R_LOONGARCH_GOT:
649+
// The LoongArch TLS GD relocs reuse the R_LARCH_GOT_PC_LO12 reloc type
650+
// for their page offsets. The arithmetics are different in the TLS case
651+
// so we have to duplicate some logic here.
652+
if (sym.hasFlag(NEEDS_TLSGD) && type != R_LARCH_TLS_IE_PC_LO12)
653+
// Like R_LOONGARCH_TLSGD_PAGE_PC but taking the absolute value.
654+
return in.got->getGlobalDynAddr(sym) + a;
655+
return getRelocTargetVA(file, type, a, p, sym, R_GOT);
647656
case R_GOTONLY_PC:
648657
return in.got->getVA() + a - p;
649658
case R_GOTPLTONLY_PC:
@@ -668,6 +677,10 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
668677
case R_GOT_PC:
669678
case R_RELAX_TLS_GD_TO_IE:
670679
return sym.getGotVA() + a - p;
680+
case R_LOONGARCH_GOT_PAGE_PC:
681+
if (sym.hasFlag(NEEDS_TLSGD))
682+
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p);
683+
return getLoongArchPageDelta(sym.getGotVA() + a, p);
671684
case R_MIPS_GOTREL:
672685
return sym.getVA(a) - in.mipsGot->getGp(file);
673686
case R_MIPS_GOT_GP:
@@ -716,6 +729,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
716729
*hiRel->sym, hiRel->expr);
717730
return 0;
718731
}
732+
case R_LOONGARCH_PAGE_PC:
733+
return getLoongArchPageDelta(sym.getVA(a), p);
719734
case R_PC:
720735
case R_ARM_PCA: {
721736
uint64_t dest;
@@ -749,6 +764,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
749764
case R_PLT_PC:
750765
case R_PPC64_CALL_PLT:
751766
return sym.getPltVA() + a - p;
767+
case R_LOONGARCH_PLT_PAGE_PC:
768+
return getLoongArchPageDelta(sym.getPltVA() + a, p);
752769
case R_PLT_GOTPLT:
753770
return sym.getPltVA() + a - in.gotPlt->getVA();
754771
case R_PPC32_PLTREL:
@@ -809,6 +826,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
809826
return in.got->getGlobalDynAddr(sym) + a - in.gotPlt->getVA();
810827
case R_TLSGD_PC:
811828
return in.got->getGlobalDynAddr(sym) + a - p;
829+
case R_LOONGARCH_TLSGD_PAGE_PC:
830+
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p);
812831
case R_TLSLD_GOTPLT:
813832
return in.got->getVA() + in.got->getTlsIndexOff() + a - in.gotPlt->getVA();
814833
case R_TLSLD_GOT:

lld/ELF/Relocations.cpp

+27-15
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,8 @@ static bool isAbsoluteValue(const Symbol &sym) {
195195

196196
// Returns true if Expr refers a PLT entry.
197197
static bool needsPlt(RelExpr expr) {
198-
return oneof<R_PLT, R_PLT_PC, R_PLT_GOTPLT, R_PPC32_PLTREL, R_PPC64_CALL_PLT>(
199-
expr);
198+
return oneof<R_PLT, R_PLT_PC, R_PLT_GOTPLT, R_LOONGARCH_PLT_PAGE_PC,
199+
R_PPC32_PLTREL, R_PPC64_CALL_PLT>(expr);
200200
}
201201

202202
// Returns true if Expr refers a GOT entry. Note that this function
@@ -205,20 +205,23 @@ static bool needsPlt(RelExpr expr) {
205205
static bool needsGot(RelExpr expr) {
206206
return oneof<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
207207
R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT,
208-
R_AARCH64_GOT_PAGE>(expr);
208+
R_AARCH64_GOT_PAGE, R_LOONGARCH_GOT, R_LOONGARCH_GOT_PAGE_PC>(
209+
expr);
209210
}
210211

211212
// True if this expression is of the form Sym - X, where X is a position in the
212213
// file (PC, or GOT for example).
213214
static bool isRelExpr(RelExpr expr) {
214215
return oneof<R_PC, R_GOTREL, R_GOTPLTREL, R_MIPS_GOTREL, R_PPC64_CALL,
215216
R_PPC64_RELAX_TOC, R_AARCH64_PAGE_PC, R_RELAX_GOT_PC,
216-
R_RISCV_PC_INDIRECT, R_PPC64_RELAX_GOT_PC>(expr);
217+
R_RISCV_PC_INDIRECT, R_PPC64_RELAX_GOT_PC, R_LOONGARCH_PAGE_PC>(
218+
expr);
217219
}
218220

219-
220221
static RelExpr toPlt(RelExpr expr) {
221222
switch (expr) {
223+
case R_LOONGARCH_PAGE_PC:
224+
return R_LOONGARCH_PLT_PAGE_PC;
222225
case R_PPC64_CALL:
223226
return R_PPC64_CALL_PLT;
224227
case R_PC:
@@ -237,6 +240,8 @@ static RelExpr fromPlt(RelExpr expr) {
237240
case R_PLT_PC:
238241
case R_PPC32_PLTREL:
239242
return R_PC;
243+
case R_LOONGARCH_PLT_PAGE_PC:
244+
return R_LOONGARCH_PAGE_PC;
240245
case R_PPC64_CALL_PLT:
241246
return R_PPC64_CALL;
242247
case R_PLT:
@@ -951,7 +956,9 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
951956
R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
952957
R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
953958
R_PLT_PC, R_PLT_GOTPLT, R_PPC32_PLTREL, R_PPC64_CALL_PLT,
954-
R_PPC64_RELAX_TOC, R_RISCV_ADD, R_AARCH64_GOT_PAGE>(e))
959+
R_PPC64_RELAX_TOC, R_RISCV_ADD, R_AARCH64_GOT_PAGE,
960+
R_LOONGARCH_PLT_PAGE_PC, R_LOONGARCH_GOT, R_LOONGARCH_GOT_PAGE_PC>(
961+
e))
955962
return true;
956963

957964
// These never do, except if the entire file is position dependent or if
@@ -1055,7 +1062,9 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
10551062
// for detailed description:
10561063
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
10571064
in.mipsGot->addEntry(*sec->file, sym, addend, expr);
1058-
} else {
1065+
} else if (!sym.isTls() || config->emachine != EM_LOONGARCH) {
1066+
// Many LoongArch TLS relocs reuse the R_LOONGARCH_GOT type, in which
1067+
// case the NEEDS_GOT flag shouldn't get set.
10591068
sym.setFlags(NEEDS_GOT);
10601069
}
10611070
} else if (needsPlt(expr)) {
@@ -1095,7 +1104,8 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
10951104
(isa<EhInputSection>(sec) && config->emachine != EM_MIPS));
10961105
if (canWrite) {
10971106
RelType rel = target->getDynRel(type);
1098-
if (expr == R_GOT || (rel == target->symbolicRel && !sym.isPreemptible)) {
1107+
if (oneof<R_GOT, R_LOONGARCH_GOT>(expr) ||
1108+
(rel == target->symbolicRel && !sym.isPreemptible)) {
10991109
addRelativeReloc<true>(*sec, offset, sym, addend, expr, type);
11001110
return;
11011111
} else if (rel != 0) {
@@ -1247,11 +1257,13 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
12471257
return 1;
12481258
}
12491259

1250-
// ARM, Hexagon and RISC-V do not support GD/LD to IE/LE relaxation. For
1251-
// PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable
1260+
// ARM, Hexagon, LoongArch and RISC-V do not support GD/LD to IE/LE
1261+
// relaxation.
1262+
// For PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable
12521263
// relaxation as well.
12531264
bool toExecRelax = !config->shared && config->emachine != EM_ARM &&
12541265
config->emachine != EM_HEXAGON &&
1266+
config->emachine != EM_LOONGARCH &&
12551267
config->emachine != EM_RISCV &&
12561268
!c.file->ppc64DisableTLSRelax;
12571269

@@ -1268,8 +1280,7 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
12681280
// being suitable for being dynamically loaded via dlopen. GOT[e0] is the
12691281
// module index, with a special value of 0 for the current module. GOT[e1] is
12701282
// unused. There only needs to be one module index entry.
1271-
if (oneof<R_TLSLD_GOT, R_TLSLD_GOTPLT, R_TLSLD_PC, R_TLSLD_HINT>(
1272-
expr)) {
1283+
if (oneof<R_TLSLD_GOT, R_TLSLD_GOTPLT, R_TLSLD_PC, R_TLSLD_HINT>(expr)) {
12731284
// Local-Dynamic relocs can be relaxed to Local-Exec.
12741285
if (toExecRelax) {
12751286
c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_LD_TO_LE), type,
@@ -1300,7 +1311,8 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
13001311
}
13011312

13021313
if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
1303-
R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
1314+
R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC,
1315+
R_LOONGARCH_TLSGD_PAGE_PC>(expr)) {
13041316
if (!toExecRelax) {
13051317
sym.setFlags(NEEDS_TLSGD);
13061318
c.addReloc({expr, type, offset, addend, &sym});
@@ -1320,8 +1332,8 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
13201332
return target->getTlsGdRelaxSkip(type);
13211333
}
13221334

1323-
if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_OFF,
1324-
R_TLSIE_HINT>(expr)) {
1335+
if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC,
1336+
R_LOONGARCH_GOT_PAGE_PC, R_GOT_OFF, R_TLSIE_HINT>(expr)) {
13251337
ctx.hasTlsIe.store(true, std::memory_order_relaxed);
13261338
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
13271339
// defined.

lld/ELF/Relocations.h

+9
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ enum RelExpr {
102102
R_PPC64_RELAX_GOT_PC,
103103
R_RISCV_ADD,
104104
R_RISCV_PC_INDIRECT,
105+
// Same as R_PC but with page-aligned semantics.
106+
R_LOONGARCH_PAGE_PC,
107+
// Same as R_PLT_PC but with page-aligned semantics.
108+
R_LOONGARCH_PLT_PAGE_PC,
109+
// In addition to having page-aligned semantics, LoongArch GOT relocs are
110+
// also reused for TLS, making the semantics differ from other architectures.
111+
R_LOONGARCH_GOT,
112+
R_LOONGARCH_GOT_PAGE_PC,
113+
R_LOONGARCH_TLSGD_PAGE_PC,
105114
};
106115

107116
// Architecture-neutral representation of relocation.

lld/ELF/ScriptParser.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,8 @@ static std::pair<ELFKind, uint16_t> parseBfdName(StringRef s) {
445445
.Case("elf64-littleriscv", {ELF64LEKind, EM_RISCV})
446446
.Case("elf64-sparc", {ELF64BEKind, EM_SPARCV9})
447447
.Case("elf32-msp430", {ELF32LEKind, EM_MSP430})
448+
.Case("elf32-loongarch", {ELF32LEKind, EM_LOONGARCH})
449+
.Case("elf64-loongarch", {ELF64LEKind, EM_LOONGARCH})
448450
.Default({ELFNoneKind, EM_NONE});
449451
}
450452

lld/ELF/Target.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ TargetInfo *elf::getTarget() {
6262
return getAVRTargetInfo();
6363
case EM_HEXAGON:
6464
return getHexagonTargetInfo();
65+
case EM_LOONGARCH:
66+
return getLoongArchTargetInfo();
6567
case EM_MIPS:
6668
switch (config->ekind) {
6769
case ELF32LEKind:

lld/ELF/Target.h

+2
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ TargetInfo *getAMDGPUTargetInfo();
179179
TargetInfo *getARMTargetInfo();
180180
TargetInfo *getAVRTargetInfo();
181181
TargetInfo *getHexagonTargetInfo();
182+
TargetInfo *getLoongArchTargetInfo();
182183
TargetInfo *getMSP430TargetInfo();
183184
TargetInfo *getPPC64TargetInfo();
184185
TargetInfo *getPPCTargetInfo();
@@ -225,6 +226,7 @@ void addPPC64SaveRestore();
225226
uint64_t getPPC64TocBase();
226227
uint64_t getAArch64Page(uint64_t expr);
227228
template <typename ELFT> void writeARMCmseImportLib();
229+
uint64_t getLoongArchPageDelta(uint64_t dest, uint64_t pc);
228230
void riscvFinalizeRelax(int passes);
229231
void mergeRISCVAttributesSections();
230232
void addArmInputSectionMappingSymbols();

lld/docs/ReleaseNotes.rst

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ ELF Improvements
5151
* Program header assignment can now be used within ``OVERLAY``. This functionality was accidentally lost in 2020.
5252
(`D150445 <https://reviews.llvm.org/D150445>`_)
5353
* Operators ``^`` and ``^=`` can now be used in linker scripts.
54+
* LoongArch is now supported.
5455
* ``DT_AARCH64_MEMTAG_*`` dynamic tags are now supported.
5556
(`D143769 <https://reviews.llvm.org/D143769>`_)
5657
* AArch32 port now supports BE-8 and BE-32 modes for big-endian.

lld/docs/index.rst

+5-4
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ Features
2222
machine, you can expect that LLD runs more than twice as fast as the GNU
2323
gold linker. Your mileage may vary, though.
2424

25-
- It supports various CPUs/ABIs including AArch64, AMDGPU, ARM, Hexagon, MIPS
26-
32/64 big/little-endian, PowerPC, PowerPC64, RISC-V, SPARC V9, x86-32 and
27-
x86-64. Among these, AArch64, ARM (>= v4), PowerPC, PowerPC64, RISC-V, x86-32
28-
and x86-64 have production quality. MIPS seems decent too.
25+
- It supports various CPUs/ABIs including AArch64, AMDGPU, ARM, Hexagon,
26+
LoongArch, MIPS 32/64 big/little-endian, PowerPC, PowerPC64, RISC-V,
27+
SPARC V9, x86-32 and x86-64. Among these, AArch64, ARM (>= v4), LoongArch,
28+
PowerPC, PowerPC64, RISC-V, x86-32 and x86-64 have production quality.
29+
MIPS seems decent too.
2930

3031
- It is always a cross-linker, meaning that it always supports all the
3132
above targets however it was built. In fact, we don't provide a

lld/docs/ld.lld.1

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.\"
55
.\" This man page documents only lld's ELF linking support, obtained originally
66
.\" from FreeBSD.
7-
.Dd Feb 9, 2023
7+
.Dd Jul 25, 2023
88
.Dt LD.LLD 1
99
.Os
1010
.Sh NAME
@@ -27,8 +27,8 @@ It accepts most of the same command line arguments and linker scripts
2727
as GNU linkers.
2828
.Pp
2929
.Nm
30-
currently supports i386, x86-64, ARM, AArch64, PowerPC32, PowerPC64,
31-
MIPS32, MIPS64, RISC-V, AMDGPU, Hexagon and SPARC V9 targets.
30+
currently supports i386, x86-64, ARM, AArch64, LoongArch, PowerPC32,
31+
PowerPC64, MIPS32, MIPS64, RISC-V, AMDGPU, Hexagon and SPARC V9 targets.
3232
.Nm
3333
acts as a Microsoft link.exe-compatible linker if invoked as
3434
.Nm lld-link

0 commit comments

Comments
 (0)