[lld] [LLD] Initial support for MIPS COFF relocations (PR #205200)
Hervé Poussineau via llvm-commits
llvm-commits at lists.llvm.org
Sun Jun 28 11:42:27 PDT 2026
https://github.com/hpoussin updated https://github.com/llvm/llvm-project/pull/205200
>From 229e0360334c7f24a0e22a55d9a9930c038d8103 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= <hpoussin at reactos.org>
Date: Sun, 14 Jun 2026 08:38:37 +0200
Subject: [PATCH 1/2] [LLD] Initial support for MIPS COFF relocations
Base relocations are not handled, but it will be implemented later.
---
lld/COFF/Chunks.cpp | 45 ++++++++++++++++++++++++
lld/COFF/Chunks.h | 2 ++
lld/test/COFF/reloc-mips.test | 65 +++++++++++++++++++++++++++++++++++
lld/test/lit.cfg.py | 1 +
4 files changed, 113 insertions(+)
create mode 100644 lld/test/COFF/reloc-mips.test
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index efc9ff113e623..fc4cd01b1c3f1 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -368,6 +368,46 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
}
}
+void applyMipsBranch(uint8_t *off, int64_t v) {
+ if (v & 3)
+ error("misaligned jmp offset");
+ or32(off, (v >> 2) & 0x03FFFFFC);
+}
+
+void SectionChunk::applyRelMIPS(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p,
+ uint64_t imageBase) const {
+ switch (type) {
+ case IMAGE_REL_MIPS_REFWORD:
+ add32(off, s + imageBase);
+ break;
+ case IMAGE_REL_MIPS_JMPADDR:
+ applyMipsBranch(off, s + imageBase);
+ break;
+ case IMAGE_REL_MIPS_REFHI:
+ add16(off, (s + imageBase) >> 16);
+ break;
+ case IMAGE_REL_MIPS_REFLO:
+ add16(off, s + imageBase);
+ break;
+ case IMAGE_REL_MIPS_PAIR:
+ // Nothing to do
+ break;
+ case IMAGE_REL_MIPS_REFWORDNB:
+ add32(off, s);
+ break;
+ case IMAGE_REL_MIPS_SECTION:
+ applySecIdx(off, os, file->symtab.ctx.outputSections.size());
+ break;
+ case IMAGE_REL_MIPS_SECREL:
+ applySecRel(this, off, os, s);
+ break;
+ default:
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
+ }
+}
+
static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
Defined *sym,
const coff_relocation &rel,
@@ -466,6 +506,9 @@ void SectionChunk::applyRelocation(uint8_t *off,
case Triple::aarch64:
applyRelARM64(off, rel.Type, os, s, p, imageBase);
break;
+ case Triple::mipsel:
+ applyRelMIPS(off, rel.Type, os, s, p, imageBase);
+ break;
default:
llvm_unreachable("unknown machine type");
}
@@ -552,6 +595,8 @@ static uint8_t getBaserelType(const coff_relocation &rel,
if (rel.Type == IMAGE_REL_ARM64_ADDR64)
return IMAGE_REL_BASED_DIR64;
return IMAGE_REL_BASED_ABSOLUTE;
+ case Triple::mipsel:
+ return IMAGE_REL_BASED_ABSOLUTE;
default:
llvm_unreachable("unknown machine type");
}
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 06c010a7d8a2d..abfad3fccf9d7 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -283,6 +283,8 @@ class SectionChunk : public Chunk {
uint64_t p, uint64_t imageBase) const;
void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
uint64_t p, uint64_t imageBase) const;
+ void applyRelMIPS(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p, uint64_t imageBase) const;
void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res);
diff --git a/lld/test/COFF/reloc-mips.test b/lld/test/COFF/reloc-mips.test
new file mode 100644
index 0000000000000..80e668d8dbf2f
--- /dev/null
+++ b/lld/test/COFF/reloc-mips.test
@@ -0,0 +1,65 @@
+# REQUIRES: mips, llvm-windres
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llc -filetype=obj main.ll -mtriple mipsel-windows-gnu -o main.obj
+# RUN: llvm-windres main.rc rc.obj --target=mipsel-windows-gnu
+# RUN: llvm-objdump main.obj rc.obj --reloc -S | FileCheck %s
+# RUN: lld-link main.obj rc.obj /out:main.exe /subsystem:console,4 /entry:_start /base:0x30000 && llvm-objdump -sS main.exe | FileCheck %s --check-prefix=CHECK-LOW
+# RUN: lld-link main.obj rc.obj /out:main.exe /subsystem:console,4 /entry:_start /base:0x10000000 && llvm-objdump -sS main.exe | FileCheck %s --check-prefix=CHECK-HIGH
+
+# CHECK: lui $1, 0x0 <_start>
+# CHECK-NEXT: IMAGE_REL_MIPS_REFHI i
+# CHECK-NEXT: IMAGE_REL_MIPS_PAIR
+# CHECK-NEXT: sw $zero, 0x0($1)
+# CHECK-NEXT: IMAGE_REL_MIPS_REFLO i
+
+# CHECK: j 0x18 <_start+0x18>
+# CHECK-NEXT: IMAGE_REL_MIPS_JMPADDR .text
+
+# CHECK-LOW: Contents of section .rdata:
+# CHECK-LOW: 32000 00300300
+# CHECK-LOW: Contents of section .rsrc:
+# CHECK-LOW: 34040 09040000 48000000 60400000 15000000
+# CHECK-LOW: 00031000 <.text>:
+# CHECK-LOW: lui $1, 0x3
+# CHECK-LOW: sw $zero, 0x3000($1)
+# CHECK-LOW: j 0x31018 <.text+0x18>
+
+# CHECK-HIGH: Contents of section .rdata:
+# CHECK-HIGH: 10002000 00300010
+# CHECK-HIGH: Contents of section .rsrc:
+# CHECK-HIGH: 10004040 09040000 48000000 60400000 15000000
+# CHECK-HIGH: 10001000 <.text>:
+# CHECK-HIGH: lui $1, 0x1000
+# CHECK-HIGH: sw $zero, 0x3000($1)
+# CHECK-HIGH: j 0x1018 <.text+0x18>
+
+#--- main.ll
+target datalayout = "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"
+target triple = "mipsel-unknown-windows-gnu"
+
+%struct.foo = type { ptr }
+
+ at i = internal global i32 0, align 4
+ at __const._start.f = private unnamed_addr constant %struct.foo { ptr @i }, align 4
+
+; Function Attrs: noinline nounwind optnone
+define dso_local void @_start() {
+entry:
+ %f = alloca %struct.foo, align 4
+ store i32 0, ptr @i, align 4
+ call void @llvm.memcpy.p0.p0.i32(ptr align 4 %f, ptr align 4 @__const._start.f, i32 4, i1 false)
+ br label %for.cond
+
+for.cond: ; preds = %for.cond, %entry
+ br label %for.cond
+}
+
+; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.memcpy.p0.p0.i32(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i32, i1 immarg) #0
+
+attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+#--- main.rc
+42 RCDATA {
+ "a long string of data"
+}
diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py
index 39c3d0aa36bfb..b4ec53ed0a3b1 100644
--- a/lld/test/lit.cfg.py
+++ b/lld/test/lit.cfg.py
@@ -62,6 +62,7 @@
"llvm-dwarfdump",
"llvm-readelf",
"llvm-readobj",
+ "llvm-windres",
"obj2yaml",
"yaml2obj",
"opt",
>From 8f6657b5da89be861bceb1f814738cb4ab588a6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= <hpoussin at reactos.org>
Date: Sun, 28 Jun 2026 20:19:24 +0200
Subject: [PATCH 2/2] Some changes following review
- make applyMipsBranch() static
- use or32() instead of add32() for IMAGE_REL_MIPS_JMPADDR
- rework test to replace IR and resource file by asm
- as a consequence, don't depend anymore of llvm-windres
---
lld/COFF/Chunks.cpp | 4 +-
lld/test/COFF/reloc-mips.test | 121 ++++++++++++++++------------------
lld/test/lit.cfg.py | 1 -
3 files changed, 58 insertions(+), 68 deletions(-)
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index fc4cd01b1c3f1..db0fde70f4511 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -368,10 +368,10 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
}
}
-void applyMipsBranch(uint8_t *off, int64_t v) {
+static void applyMipsBranch(uint8_t *off, int64_t v) {
if (v & 3)
error("misaligned jmp offset");
- or32(off, (v >> 2) & 0x03FFFFFC);
+ add32(off, (v >> 2) & 0x03FFFFFC);
}
void SectionChunk::applyRelMIPS(uint8_t *off, uint16_t type, OutputSection *os,
diff --git a/lld/test/COFF/reloc-mips.test b/lld/test/COFF/reloc-mips.test
index 80e668d8dbf2f..52582b6076419 100644
--- a/lld/test/COFF/reloc-mips.test
+++ b/lld/test/COFF/reloc-mips.test
@@ -1,65 +1,56 @@
-# REQUIRES: mips, llvm-windres
-
-# RUN: rm -rf %t && split-file %s %t && cd %t
-# RUN: llc -filetype=obj main.ll -mtriple mipsel-windows-gnu -o main.obj
-# RUN: llvm-windres main.rc rc.obj --target=mipsel-windows-gnu
-# RUN: llvm-objdump main.obj rc.obj --reloc -S | FileCheck %s
-# RUN: lld-link main.obj rc.obj /out:main.exe /subsystem:console,4 /entry:_start /base:0x30000 && llvm-objdump -sS main.exe | FileCheck %s --check-prefix=CHECK-LOW
-# RUN: lld-link main.obj rc.obj /out:main.exe /subsystem:console,4 /entry:_start /base:0x10000000 && llvm-objdump -sS main.exe | FileCheck %s --check-prefix=CHECK-HIGH
-
-# CHECK: lui $1, 0x0 <_start>
-# CHECK-NEXT: IMAGE_REL_MIPS_REFHI i
-# CHECK-NEXT: IMAGE_REL_MIPS_PAIR
-# CHECK-NEXT: sw $zero, 0x0($1)
-# CHECK-NEXT: IMAGE_REL_MIPS_REFLO i
-
-# CHECK: j 0x18 <_start+0x18>
-# CHECK-NEXT: IMAGE_REL_MIPS_JMPADDR .text
-
-# CHECK-LOW: Contents of section .rdata:
-# CHECK-LOW: 32000 00300300
-# CHECK-LOW: Contents of section .rsrc:
-# CHECK-LOW: 34040 09040000 48000000 60400000 15000000
-# CHECK-LOW: 00031000 <.text>:
-# CHECK-LOW: lui $1, 0x3
-# CHECK-LOW: sw $zero, 0x3000($1)
-# CHECK-LOW: j 0x31018 <.text+0x18>
-
-# CHECK-HIGH: Contents of section .rdata:
-# CHECK-HIGH: 10002000 00300010
-# CHECK-HIGH: Contents of section .rsrc:
-# CHECK-HIGH: 10004040 09040000 48000000 60400000 15000000
-# CHECK-HIGH: 10001000 <.text>:
-# CHECK-HIGH: lui $1, 0x1000
-# CHECK-HIGH: sw $zero, 0x3000($1)
-# CHECK-HIGH: j 0x1018 <.text+0x18>
-
-#--- main.ll
-target datalayout = "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"
-target triple = "mipsel-unknown-windows-gnu"
-
-%struct.foo = type { ptr }
-
- at i = internal global i32 0, align 4
- at __const._start.f = private unnamed_addr constant %struct.foo { ptr @i }, align 4
-
-; Function Attrs: noinline nounwind optnone
-define dso_local void @_start() {
-entry:
- %f = alloca %struct.foo, align 4
- store i32 0, ptr @i, align 4
- call void @llvm.memcpy.p0.p0.i32(ptr align 4 %f, ptr align 4 @__const._start.f, i32 4, i1 false)
- br label %for.cond
-
-for.cond: ; preds = %for.cond, %entry
- br label %for.cond
-}
-
-; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-declare void @llvm.memcpy.p0.p0.i32(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i32, i1 immarg) #0
-
-attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
-#--- main.rc
-42 RCDATA {
- "a long string of data"
-}
+# REQUIRES: mips
+
+# RUN: llvm-mc -filetype=obj -triple=mipsel-windows %s -o %t.obj
+# RUN: lld-link %t.obj /out:%t.exe /subsystem:console,4 /entry:_start /base:0x30000 && llvm-objdump -sS %t.exe | FileCheck %s --check-prefix=CHECK-LOW
+# RUN: lld-link %t.obj /out:%t.exe /subsystem:console,4 /entry:_start /base:0x10000000 && llvm-objdump -sS %t.exe | FileCheck %s --check-prefix=CHECK-HIGH
+
+# CHECK-LOW: Contents of section .text:
+# CHECK-LOW: 31000 00000000 00000000 00000000 02c40008 ................
+# CHECK-LOW: 31010 00000000 00000000 ........
+# CHECK-LOW: Contents of section data_img:
+# CHECK-LOW: 32000 00000000 04200300 01000000 0000 ..... ........
+# CHECK-LOW: Contents of section text_ref:
+# CHECK-LOW: 33000 0300013c 002020ac ...<. .
+
+# CHECK-LOW: Disassembly of section .text:
+# CHECK-LOW: 3100c: 02 c4 00 08 j 0x31008 <.text+0x8>
+# CHECK-LOW: Disassembly of section text_ref:
+# CHECK-LOW: 33000: 03 00 01 3c lui $1, 0x3
+# CHECK-LOW: 33004: 00 20 20 ac sw $zero, 0x2000($1)
+
+# CHECK-HIGH: Contents of section .text:
+# CHECK-HIGH: 10001000 00000000 00000000 00000000 02040008 ................
+# CHECK-HIGH: 10001010 00000000 00000000 ........
+# CHECK-HIGH: Contents of section data_img:
+# CHECK-HIGH: 10002000 00000000 04200010 01000000 0000 ..... ........
+# CHECK-HIGH: Contents of section text_ref:
+# CHECK-HIGH: 10003000 0010013c 002020ac ...<. .
+
+# CHECK-HIGH: Disassembly of section .text:
+# CHECK-HIGH: 1000100c: 02 04 00 08 j 0x1008 <.text+0x8>
+# CHECK-HIGH: Disassembly of section text_ref:
+# CHECK-HIGH: 10003000: 00 10 01 3c lui $1, 0x1000 <.text>
+# CHECK-HIGH: 10003004: 00 20 20 ac sw $zero, 0x2000($1)
+
+.section "data_imgrel","ax"
+i:
+.long 0
+.Lfoo:
+.rva .Lfoo
+.secidx .Ljmp_target
+.secrel32 .Ljmp_target
+
+.section .text
+.globl _start
+_start:
+ nop
+ nop
+.Ljmp_target:
+ nop
+ j .Ljmp_target
+ nop
+
+.section "text_ref","ax"
+ .set noat
+ lui $1, %hi(i)
+ sw $zero, %lo(i)($1)
diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py
index b4ec53ed0a3b1..39c3d0aa36bfb 100644
--- a/lld/test/lit.cfg.py
+++ b/lld/test/lit.cfg.py
@@ -62,7 +62,6 @@
"llvm-dwarfdump",
"llvm-readelf",
"llvm-readobj",
- "llvm-windres",
"obj2yaml",
"yaml2obj",
"opt",
More information about the llvm-commits
mailing list