:: commit 26acbb8bd9d15db22f766b63d6b8d6244954e70c

mintsuki <mintsuki@protonmail.com> — 2022-03-08 20:05

parents: f31ed9c495

elf: Use PHDRs instead of sections for resolving relocations

Co-authored-by: Connor Horman <chorman64@gmail.com>
diff --git a/common/lib/elf.c b/common/lib/elf.c
index d8601ade..4eb6e2f0 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -9,9 +9,16 @@
 #include <fs/file.h>
 
 #define PT_LOAD     0x00000001
+#define PT_DYNAMIC  0x00000002
 #define PT_INTERP   0x00000003
 #define PT_PHDR     0x00000006
 
+#define DT_NULL     0x00000000
+#define DT_NEEDED   0x00000001
+#define DT_RELA     0x00000007
+#define DT_RELASZ   0x00000008
+#define DT_RELAENT  0x00000009
+
 #define ABI_SYSV    0x00
 #define ARCH_X86_64 0x3e
 #define ARCH_X86_32 0x03
@@ -26,7 +33,6 @@
 #define EI_VERSION  6
 #define EI_OSABI    7
 
-
 struct elf32_hdr {
     uint8_t  ident[16];
     uint16_t type;
@@ -86,6 +92,11 @@ struct elf64_rela {
     uint64_t r_addend;
 };
 
+struct elf64_dyn {
+    uint64_t d_tag;
+    uint64_t d_un;
+};
+
 int elf_bits(uint8_t *elf) {
     struct elf64_hdr hdr;
     memcpy(&hdr, elf + (0), 20);
@@ -106,46 +117,75 @@ int elf_bits(uint8_t *elf) {
 }
 
 static bool elf64_is_relocatable(uint8_t *elf, struct elf64_hdr *hdr) {
-    // Find RELA sections
-    for (uint16_t i = 0; i < hdr->sh_num; i++) {
-        struct elf64_shdr section;
-        memcpy(&section, elf + (hdr->shoff + i * sizeof(struct elf64_shdr)),
-                    sizeof(struct elf64_shdr));
-
-        if (section.sh_type != SHT_RELA)
-            continue;
-
-        if (section.sh_entsize != sizeof(struct elf64_rela)) {
-            print("elf: Unknown sh_entsize for RELA section!\n");
-            continue;
-        }
-
-        return true;
+    // Find DYN segment
+    for (uint16_t i = 0; i < hdr->ph_num; i++) {
+        struct elf64_phdr phdr;
+        memcpy(&phdr, elf + (hdr->phoff + i * sizeof(struct elf64_phdr)),
+               sizeof(struct elf64_phdr));
 
+        if (phdr.p_type == PT_DYNAMIC)
+            return true;
     }
 
     return false;
 }
 
 static int elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *buffer, uint64_t vaddr, size_t size, uint64_t slide) {
-    // Find RELA sections
-    for (uint16_t i = 0; i < hdr->sh_num; i++) {
-        struct elf64_shdr section;
-        memcpy(&section, elf + (hdr->shoff + i * sizeof(struct elf64_shdr)),
-                    sizeof(struct elf64_shdr));
+    // Find DYN segment
+    for (uint16_t i = 0; i < hdr->ph_num; i++) {
+        struct elf64_phdr phdr;
+        memcpy(&phdr, elf + (hdr->phoff + i * sizeof(struct elf64_phdr)),
+               sizeof(struct elf64_phdr));
 
-        if (section.sh_type != SHT_RELA)
+        if (phdr.p_type != PT_DYNAMIC)
             continue;
 
-        if (section.sh_entsize != sizeof(struct elf64_rela)) {
+        uint64_t rela_offset = 0;
+        uint64_t rela_size = 0;
+        uint64_t rela_ent = 0;
+        for (uint16_t j = 0; j < phdr.p_filesz / sizeof(struct elf64_dyn); j++) {
+            struct elf64_dyn dyn;
+            memcpy(&dyn, elf + (phdr.p_offset + j * sizeof(struct elf64_dyn)),
+                   sizeof(struct elf64_dyn));
+
+            switch (dyn.d_tag) {
+                case DT_RELA:
+                    rela_offset = dyn.d_un;
+                    break;
+                case DT_RELAENT:
+                    rela_ent = dyn.d_un;
+                    break;
+                case DT_RELASZ:
+                    rela_size = dyn.d_un;
+                    break;
+            }
+        }
+
+        if (rela_offset == 0) {
+            break;
+        }
+
+        if (rela_ent != sizeof(struct elf64_rela)) {
             print("elf: Unknown sh_entsize for RELA section!\n");
             return 1;
         }
 
+        for (uint16_t j = 0; j < hdr->ph_num; j++) {
+            struct elf64_phdr _phdr;
+            memcpy(&_phdr, elf + (hdr->phoff + j * sizeof(struct elf64_phdr)),
+                   sizeof(struct elf64_phdr));
+
+            if (_phdr.p_vaddr <= rela_offset && _phdr.p_vaddr + _phdr.p_filesz > rela_offset) {
+                rela_offset -= _phdr.p_vaddr;
+                rela_offset += _phdr.p_offset;
+                break;
+            }
+        }
+
         // This is a RELA header, get and apply all relocations
-        for (uint64_t offset = 0; offset < section.sh_size; offset += section.sh_entsize) {
+        for (uint64_t offset = 0; offset < rela_size; offset += rela_ent) {
             struct elf64_rela relocation;
-            memcpy(&relocation, elf + (section.sh_offset + offset), sizeof(relocation));
+            memcpy(&relocation, elf + (rela_offset + offset), sizeof(struct elf64_rela));
 
             switch (relocation.r_info) {
                 case R_X86_64_RELATIVE: {
@@ -169,6 +209,8 @@ static int elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *bu
                     return 1;
             }
         }
+
+        break;
     }
 
     return 0;
tab: 248 wrap: offon