:: commit 6b1e6d88ddba75653a212b1cbab81ec0e4eed5de

mintsuki <mintsuki@protonmail.com> — 2024-06-02 22:03

parents: a4171f1e82

lib/elf: Add support for GLOB_DAT, JUMP_SLOT, and 64 relocation types

diff --git a/common/lib/elf.c b/common/lib/elf.c
index 266ce622..8f0153f2 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -25,6 +25,10 @@
 #define DT_RELA     0x00000007
 #define DT_RELASZ   0x00000008
 #define DT_RELAENT  0x00000009
+#define DT_SYMTAB   0x00000006
+#define DT_SYMENT   0x0000000b
+#define DT_PLTRELSZ 0x00000002
+#define DT_JMPREL   0x00000017
 #define DT_FLAGS_1  0x6ffffffb
 
 #define DF_1_PIE    0x08000000
@@ -40,6 +44,13 @@
 #define R_X86_64_RELATIVE  0x00000008
 #define R_AARCH64_RELATIVE 0x00000403
 #define R_RISCV_RELATIVE   0x00000003
+#define R_X86_64_GLOB_DAT  0x00000006
+#define R_AARCH64_GLOB_DAT 0x00000401
+#define R_X86_64_JUMP_SLOT 0x00000007
+#define R_AARCH64_JUMP_SLOT 0x00000402
+#define R_RISCV_JUMP_SLOT  0x00000005
+#define R_X86_64_64        0x00000001
+#define R_RISCV_64         0x00000002
 
 /* Indices into identification array */
 #define EI_CLASS    4
@@ -192,6 +203,12 @@ static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *b
         if (phdr->p_type != PT_DYNAMIC)
             continue;
 
+        uint64_t symtab_offset = 0;
+        uint64_t symtab_ent = 0;
+
+        uint64_t dt_pltrelsz = 0;
+        uint64_t dt_jmprel = 0;
+
         uint64_t rela_offset = 0;
         uint64_t rela_size = 0;
         uint64_t rela_ent = 0;
@@ -208,6 +225,18 @@ static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *b
                 case DT_RELASZ:
                     rela_size = dyn->d_un;
                     break;
+                case DT_SYMTAB:
+                    symtab_offset = dyn->d_un;
+                    break;
+                case DT_SYMENT:
+                    symtab_ent = dyn->d_un;
+                    break;
+                case DT_PLTRELSZ:
+                    dt_pltrelsz = dyn->d_un;
+                    break;
+                case DT_JMPREL:
+                    dt_jmprel = dyn->d_un;
+                    break;
             }
         }
 
@@ -230,9 +259,54 @@ static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *b
             }
         }
 
-        // This is a RELA header, get and apply all relocations
-        for (uint64_t offset = 0; offset < rela_size; offset += rela_ent) {
-            struct elf64_rela *relocation = (void *)elf + (rela_offset + offset);
+        for (uint16_t j = 0; j < hdr->ph_num; j++) {
+            struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + j * hdr->phdr_size);
+
+            if (_phdr->p_vaddr <= symtab_offset && _phdr->p_vaddr + _phdr->p_filesz > symtab_offset) {
+                symtab_offset -= _phdr->p_vaddr;
+                symtab_offset += _phdr->p_offset;
+                break;
+            }
+        }
+
+        if (dt_jmprel != 0) {
+            for (uint16_t j = 0; j < hdr->ph_num; j++) {
+                struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + j * hdr->phdr_size);
+
+                if (_phdr->p_vaddr <= dt_jmprel && _phdr->p_vaddr + _phdr->p_filesz > dt_jmprel) {
+                    dt_jmprel -= _phdr->p_vaddr;
+                    dt_jmprel += _phdr->p_offset;
+                    break;
+                }
+            }
+        }
+
+        size_t relocs_i = rela_size / rela_ent + dt_pltrelsz / rela_ent;
+        struct elf64_rela **relocs = ext_mem_alloc(relocs_i * sizeof(struct elf64_rela *));
+
+        for (uint64_t j = 0, offset = 0; offset < rela_size; offset += rela_ent) {
+            relocs[j++] = (void *)elf + (rela_offset + offset);
+        }
+
+        if (dt_jmprel != 0) {
+            for (uint64_t j = rela_size / rela_ent, offset = 0; offset < dt_pltrelsz; offset += rela_ent) {
+                relocs[j++] = (void *)elf + (dt_jmprel + offset);
+            }
+        }
+
+        for (size_t j = 0; j < relocs_i; j++) {
+            struct elf64_rela *relocation = relocs[j];
+
+            // Relocation is before buffer
+            if (relocation->r_addr < vaddr)
+                continue;
+
+            // Relocation is after buffer
+            if (vaddr + size < relocation->r_addr + 8)
+                continue;
+
+            // It's inside it, calculate where it is
+            uint64_t *ptr = (uint64_t *)((buffer - vaddr) + relocation->r_addr);
 
             switch (relocation->r_info) {
 #if defined (__x86_64__) || defined (__i386__)
@@ -241,25 +315,37 @@ static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *b
                 case R_AARCH64_RELATIVE:
 #elif defined (__riscv64)
                 case R_RISCV_RELATIVE:
-#else
-#error Unknown architecture
 #endif
                 {
-                    // Relocation is before buffer
-                    if (relocation->r_addr < vaddr)
-                        continue;
-
-                    // Relocation is after buffer
-                    if (vaddr + size < relocation->r_addr + 8)
-                        continue;
-
-                    // It's inside it, calculate where it is
-                    uint64_t *ptr = (uint64_t *)((uint8_t *)buffer - vaddr + relocation->r_addr);
-
-                    // Write the relocated value
                     *ptr = slide + relocation->r_addend;
                     break;
                 }
+#if defined (__x86_64__) || defined (__i386__)
+                case R_X86_64_GLOB_DAT:
+                case R_X86_64_JUMP_SLOT:
+#elif defined (__aarch64__)
+                case R_AARCH64_GLOB_DAT:
+                case R_AARCH64_JUMP_SLOT:
+#elif defined (__riscv64)
+                case R_RISCV_JUMP_SLOT:
+#endif
+                {
+                    struct elf64_sym *s = (void *)elf + symtab_offset + symtab_ent * relocation->r_symbol;
+                    *ptr = slide + s->st_value;
+                    break;
+                }
+#if defined (__x86_64__) || defined (__i386__) || defined (__riscv64)
+#if defined (__x86_64__) || defined (__i386__)
+                case R_X86_64_64:
+#elif defined (__riscv64)
+                case R_RISCV_64:
+#endif
+                {
+                    struct elf64_sym *s = (void *)elf + symtab_offset + symtab_ent * relocation->r_symbol;
+                    *ptr = slide + s->st_value + relocation->r_addend;
+                    break;
+                }
+#endif
                 default: {
                     print("elf: Unknown RELA type: %x\n", relocation->r_info);
                     return false;
@@ -267,6 +353,8 @@ static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *b
             }
         }
 
+        pmm_free(relocs, relocs_i * sizeof(struct elf64_rela *));
+
         break;
     }
 
tab: 248 wrap: offon