:: commit e436b763c21c4747c45ac92f4c39580f85dc3adf

mintsuki <mintsuki@protonmail.com> — 2022-06-29 06:16

parents: 4e0ec6d544

elf: Add elf*_load_elsewhere() functions

diff --git a/common/lib/blib.h b/common/lib/blib.h
index fe278107..19dcb08c 100644
--- a/common/lib/blib.h
+++ b/common/lib/blib.h
@@ -87,4 +87,10 @@ noreturn void common_spinup(void *fnptr, int args, ...);
 
 #define no_unwind __attribute__((section(".no_unwind")))
 
+struct elsewhere_range {
+    uint64_t elsewhere;
+    uint64_t target;
+    uint64_t length;
+};
+
 #endif
diff --git a/common/lib/elf.c b/common/lib/elf.c
index 8d21e1d4..74d3bf86 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -764,3 +764,151 @@ int elf32_load(uint8_t *elf, uint32_t *entry_point, uint32_t *top, uint32_t allo
 
     return 0;
 }
+
+bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
+                          struct elsewhere_range **ranges,
+                          size_t *ranges_count) {
+    struct elf32_hdr *hdr = (void *)elf;
+
+    if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
+        printv("elf: Not a valid ELF file.\n");
+        return false;
+    }
+
+    if (hdr->ident[EI_DATA] != BITS_LE) {
+        printv("elf: Not a Little-endian ELF file.\n");
+        return false;
+    }
+
+    if (hdr->machine != ARCH_X86_32) {
+        printv("elf: Not an x86_32 ELF file.\n");
+        return false;
+    }
+
+    *entry_point = hdr.entry;
+    bool entry_adjusted = false;
+
+    if (hdr->phdr_size < sizeof(struct elf32_phdr)) {
+        panic(true, "elf: phdr_size < sizeof(struct elf32_phdr)");
+    }
+
+    *ranges_count = 0;
+    for (uint16_t i = 0; i < hdr->ph_num; i++) {
+        struct elf32_phdr *phdr = (void *)elf + (hdr.phoff + i * hdr.phdr_size);
+
+        if (phdr->p_type != PT_LOAD)
+            continue;
+
+        *ranges_count += 1;
+    }
+
+    *ranges = ext_mem_alloc(sizeof(struct elsewhere_range) * *ranges_count);
+
+    size_t cur_entry = 0;
+
+    for (uint16_t i = 0; i < hdr->ph_num; i++) {
+        struct elf32_phdr *phdr = (void *)elf + (hdr.phoff + i * hdr.phdr_size);
+
+        if (phdr->p_type != PT_LOAD)
+            continue;
+
+        // Sanity checks
+        if (phdr->p_filesz > phdr.p_memsz) {
+            panic(true, "elf: p_filesz > p_memsz");
+        }
+
+        void *elsewhere = ext_mem_alloc(phdr.p_memsz);
+
+        memcpy(elsewhere, elf + phdr.p_offset, phdr.p_filesz);
+
+        if (!entry_adjusted
+         && *entry_point >= phdr.p_vaddr
+         && *entry_point < (phdr.p_vaddr + phdr.p_memsz)) {
+            *entry_point -= phdr.p_vaddr;
+            *entry_point += phdr.p_paddr;
+            entry_adjusted = true;
+        }
+
+        *entry[cur_entry].elsewhere = elsewhere;
+        *entry[cur_entry].target = phdr.p_paddr;
+        *entry[cur_entry].length = phdr.p_memsz;
+
+        cur_entry++;
+    }
+
+    return true;
+}
+
+bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
+                          struct elsewhere_range **ranges,
+                          size_t *ranges_count) {
+    struct elf64_hdr *hdr = (void *)elf;
+
+    if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
+        printv("elf: Not a valid ELF file.\n");
+        return false;
+    }
+
+    if (hdr->ident[EI_DATA] != BITS_LE) {
+        printv("elf: Not a Little-endian ELF file.\n");
+        return false;
+    }
+
+    if (hdr->machine != ARCH_X86_64) {
+        printv("elf: Not an x86_64 ELF file.\n");
+        return false;
+    }
+
+    *entry_point = hdr.entry;
+    bool entry_adjusted = false;
+
+    if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
+        panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
+    }
+
+    *ranges_count = 0;
+    for (uint16_t i = 0; i < hdr->ph_num; i++) {
+        struct elf64_phdr *phdr = (void *)elf + (hdr.phoff + i * hdr.phdr_size);
+
+        if (phdr->p_type != PT_LOAD)
+            continue;
+
+        *ranges_count += 1;
+    }
+
+    *ranges = ext_mem_alloc(sizeof(struct elsewhere_range) * *ranges_count);
+
+    size_t cur_entry = 0;
+
+    for (uint16_t i = 0; i < hdr->ph_num; i++) {
+        struct elf64_phdr *phdr = (void *)elf + (hdr.phoff + i * hdr.phdr_size);
+
+        if (phdr->p_type != PT_LOAD)
+            continue;
+
+        // Sanity checks
+        if (phdr->p_filesz > phdr.p_memsz) {
+            panic(true, "elf: p_filesz > p_memsz");
+        }
+
+        void *elsewhere = ext_mem_alloc(phdr.p_memsz);
+
+        memcpy(elsewhere, elf + phdr.p_offset, phdr.p_filesz);
+
+        if (!entry_adjusted
+         && *entry_point >= phdr.p_vaddr
+         && *entry_point < (phdr.p_vaddr + phdr.p_memsz)) {
+            *entry_point -= phdr.p_vaddr;
+            *entry_point += phdr.p_paddr;
+            entry_adjusted = true;
+        }
+
+        *entry[cur_entry].elsewhere = elsewhere;
+        *entry[cur_entry].target = phdr.p_paddr;
+        *entry[cur_entry].length = phdr.p_memsz;
+
+        cur_entry++;
+    }
+
+    return true;
+}
diff --git a/common/lib/elf.h b/common/lib/elf.h
index c4ee5817..50395d72 100644
--- a/common/lib/elf.h
+++ b/common/lib/elf.h
@@ -3,7 +3,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
-#include <fs/file.h>
+#include <lib/blib.h>
 
 #define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000)
 
@@ -34,6 +34,13 @@ int elf32_load(uint8_t *elf, uint32_t *entry_point, uint32_t *top, uint32_t allo
 int elf32_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit);
 struct elf_section_hdr_info* elf32_section_hdr_info(uint8_t *elf);
 
+bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
+                          struct elsewhere_range **ranges,
+                          size_t *ranges_count);
+bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
+                          struct elsewhere_range **ranges,
+                          size_t *ranges_count);
+
 struct elf64_hdr {
     uint8_t  ident[16];
     uint16_t type;
tab: 248 wrap: offon