[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