From 7b5d234558f20fc1fb1efe6b1f42982125f232bc Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 11:13:19 -0400
Subject: [PATCH] jit: add support for relr-type relocations

---
 src/common/elf.h                         |  8 ++++++
 src/core/hle/service/jit/jit_context.cpp | 36 ++++++++++++++++++++++--
 2 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/src/common/elf.h b/src/common/elf.h
index 14a5e9597b..0b728dc54f 100644
--- a/src/common/elf.h
+++ b/src/common/elf.h
@@ -211,6 +211,11 @@ struct Elf64_Rela {
     Elf64_Sxword r_addend; /* Addend */
 };
 
+/* RELR relocation table entry */
+
+using Elf32_Relr = Elf32_Word;
+using Elf64_Relr = Elf64_Xword;
+
 /* How to extract and insert information held in the r_info field.  */
 
 static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
@@ -328,6 +333,9 @@ constexpr u32 ElfDtFiniArray = 26;   /* Array with addresses of fini fct */
 constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
 constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
 constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
+constexpr u32 ElfDtRelrsz = 35;      /* Size of RELR relative relocations */
+constexpr u32 ElfDtRelr = 36;        /* Address of RELR relative relocations */
+constexpr u32 ElfDtRelrent = 37;     /* Size of one RELR relative relocation */
 
 } // namespace ELF
 } // namespace Common
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp
index 4ed3f02e2e..0090e8568d 100644
--- a/src/core/hle/service/jit/jit_context.cpp
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -156,6 +156,8 @@ public:
 
     bool LoadNRO(std::span<const u8> data) {
         local_memory.clear();
+
+        relocbase = local_memory.size();
         local_memory.insert(local_memory.end(), data.begin(), data.end());
 
         if (FixupRelocations()) {
@@ -181,8 +183,8 @@ public:
         // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
         // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
         VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
-        VAddr rela_dyn = 0;
-        size_t num_rela = 0;
+        VAddr rela_dyn = 0, relr_dyn = 0;
+        size_t num_rela = 0, num_relr = 0;
         while (true) {
             const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
             dynamic_offset += sizeof(Elf64_Dyn);
@@ -196,6 +198,12 @@ public:
             if (dyn.d_tag == ElfDtRelasz) {
                 num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
             }
+            if (dyn.d_tag == ElfDtRelr) {
+                relr_dyn = dyn.d_un.d_ptr;
+            }
+            if (dyn.d_tag == ElfDtRelrsz) {
+                num_relr = dyn.d_un.d_val / sizeof(Elf64_Relr);
+            }
         }
 
         for (size_t i = 0; i < num_rela; i++) {
@@ -207,6 +215,29 @@ public:
             callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
         }
 
+        VAddr relr_where = 0;
+        for (size_t i = 0; i < num_relr; i++) {
+            const auto relr{callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr))};
+            const auto incr{[&](VAddr where) {
+                callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
+            }};
+
+            if ((relr & 1) == 0) {
+                // where pointer
+                relr_where = relocbase + relr;
+                incr(relr_where);
+                relr_where += sizeof(Elf64_Addr);
+            } else {
+                // bitmap
+                for (int bit = 1; bit < 64; bit++) {
+                    if ((relr & (1ULL << bit)) != 0) {
+                        incr(relr_where + i * sizeof(Elf64_Addr));
+                    }
+                }
+                relr_where += 63 * sizeof(Elf64_Addr);
+            }
+        }
+
         return true;
     }
 
@@ -313,6 +344,7 @@ public:
     Core::Memory::Memory& memory;
     VAddr top_of_stack;
     VAddr heap_pointer;
+    VAddr relocbase;
 };
 
 void DynarmicCallbacks64::CallSVC(u32 swi) {