:: commit 178f3cea0eb32a587f4efae8be61cfcaf3da783b

mintsuki <mintsuki@protonmail.com> — 2021-07-15 11:34

parents: aec537f519

elf: Return permission ranges

diff --git a/stage23/lib/elf.c b/stage23/lib/elf.c
index 00b73aa1..7eabee7c 100644
--- a/stage23/lib/elf.c
+++ b/stage23/lib/elf.c
@@ -292,7 +292,99 @@ int elf32_load_section(uint8_t *elf, void *buffer, const char *name, size_t limi
     return 2;
 }
 
-int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_slide, uint32_t alloc_type, bool kaslr, bool use_paddr) {
+static uint64_t elf64_min_align(uint8_t *elf, bool use_paddr) {
+    uint64_t ret = 0;
+
+    struct elf64_hdr hdr;
+    memcpy(&hdr, elf + (0), sizeof(struct elf64_hdr));
+
+    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_LOAD)
+            continue;
+
+        uint64_t load_addr = 0;
+
+        if (use_paddr) {
+            load_addr = phdr.p_paddr;
+        } else {
+            load_addr = phdr.p_vaddr;
+        }
+
+        if (load_addr % 0x200000 == 0) {
+            ret = 0x200000;
+            continue;
+        }
+
+        if (load_addr % 0x1000 == 0) {
+            ret = 0x1000;
+            continue;
+        }
+
+        // We don't do kernels that don't align their load addresses to 4K at least.
+        panic("elf: The executable contains non-4KiB aligned load addresses");
+    }
+
+    if (ret == 0) {
+        panic("elf: Executable has no loadable segments");
+    }
+
+    return ret;
+}
+
+static void elf64_get_ranges(uint8_t *elf, uint64_t slide, uint64_t min_align, bool use_paddr, struct elf_range **_ranges, uint64_t *_ranges_count) {
+    struct elf64_hdr hdr;
+    memcpy(&hdr, elf + (0), sizeof(struct elf64_hdr));
+
+    uint64_t ranges_count = 0;
+
+    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_LOAD)
+            continue;
+
+        ranges_count++;
+    }
+
+    struct elf_range *ranges = ext_mem_alloc(ranges_count * sizeof(struct elf_range));
+
+    size_t r = 0;
+    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_LOAD)
+            continue;
+
+        uint64_t load_addr = 0;
+
+        if (use_paddr) {
+            load_addr = phdr.p_paddr;
+        } else {
+            load_addr = phdr.p_vaddr;
+        }
+
+        load_addr += slide;
+
+        ranges[r].base = load_addr;
+        ranges[r].length = ALIGN_UP(phdr.p_memsz, min_align);
+        ranges[r].permissions = phdr.p_flags & 0b111;
+
+        r++;
+    }
+
+    *_ranges_count = ranges_count;
+    *_ranges = ranges;
+}
+
+int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_slide, uint32_t alloc_type, bool kaslr, bool use_paddr, struct elf_range **ranges, uint64_t *ranges_count) {
     struct elf64_hdr hdr;
     memcpy(&hdr, elf + (0), sizeof(struct elf64_hdr));
 
@@ -319,6 +411,8 @@ int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_sl
     uint64_t entry = hdr.entry;
     bool entry_adjusted = false;
 
+    uint64_t min_align = elf64_min_align(elf, use_paddr);
+
     if (!elf64_is_relocatable(elf, &hdr)) {
         simulation = false;
         goto final;
@@ -353,14 +447,16 @@ final:
 
         load_addr += slide;
 
+        uint64_t load_size = ALIGN_UP(phdr.p_memsz, min_align);
+
         if (top) {
-            uint64_t this_top = load_addr + phdr.p_memsz;
+            uint64_t this_top = load_addr + load_size;
             if (this_top > *top) {
                 *top = this_top;
             }
         }
 
-        if (!memmap_alloc_range((size_t)load_addr, (size_t)phdr.p_memsz, alloc_type, true, false, simulation, false)) {
+        if (!memmap_alloc_range((size_t)load_addr, (size_t)load_size, alloc_type, true, false, simulation, false)) {
             if (++try_count == max_simulated_tries || simulation == false)
                 return -1;
             if (!kaslr)
@@ -370,7 +466,7 @@ final:
 
         memcpy((void *)(uintptr_t)load_addr, elf + (phdr.p_offset), phdr.p_filesz);
 
-        size_t to_zero = (size_t)(phdr.p_memsz - phdr.p_filesz);
+        size_t to_zero = (size_t)(load_size - phdr.p_filesz);
 
         if (to_zero) {
             void *ptr = (void *)(uintptr_t)(load_addr + phdr.p_filesz);
@@ -398,6 +494,10 @@ final:
     if (_slide)
         *_slide = slide;
 
+    if (ranges_count != NULL && ranges != NULL) {
+        elf64_get_ranges(elf, slide, min_align, use_paddr, ranges, ranges_count);
+    }
+
     return 0;
 }
 
diff --git a/stage23/lib/elf.h b/stage23/lib/elf.h
index 6423c6b2..c4d0e362 100644
--- a/stage23/lib/elf.h
+++ b/stage23/lib/elf.h
@@ -7,9 +7,19 @@
 
 #define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000)
 
+#define ELF_PF_X 1
+#define ELF_PF_W 2
+#define ELF_PF_R 4
+
+struct elf_range {
+    uint64_t base;
+    uint64_t length;
+    uint64_t permissions;
+};
+
 int elf_bits(uint8_t *elf);
 
-int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *slide, uint32_t alloc_type, bool kaslr, bool use_paddr);
+int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_slide, uint32_t alloc_type, bool kaslr, bool use_paddr, struct elf_range **ranges, uint64_t *ranges_count);
 int elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide);
 
 int elf32_load(uint8_t *elf, uint32_t *entry_point, uint32_t *top, uint32_t alloc_type);
diff --git a/stage23/protos/multiboot1.c b/stage23/protos/multiboot1.c
index 6b815cdd..4541be13 100644
--- a/stage23/protos/multiboot1.c
+++ b/stage23/protos/multiboot1.c
@@ -99,7 +99,7 @@ void multiboot1_load(char *config, char *cmdline) {
                 break;
             case 64: {
                 uint64_t e, t;
-                if (elf64_load(kernel, &e, &t, NULL, MEMMAP_KERNEL_AND_MODULES, false, true))
+                if (elf64_load(kernel, &e, &t, NULL, MEMMAP_KERNEL_AND_MODULES, false, true, NULL, NULL))
                     panic("multiboot1: ELF64 load failure");
                 entry_point = e;
                 kernel_top = t;
diff --git a/stage23/protos/stivale.c b/stage23/protos/stivale.c
index d559d743..79e4a975 100644
--- a/stage23/protos/stivale.c
+++ b/stage23/protos/stivale.c
@@ -120,7 +120,8 @@ void stivale_load(char *config, char *cmdline) {
 
             if (!loaded_by_anchor) {
                 if (elf64_load(kernel, &entry_point, NULL, &slide,
-                               STIVALE_MMAP_KERNEL_AND_MODULES, kaslr, false))
+                               STIVALE_MMAP_KERNEL_AND_MODULES, kaslr, false,
+                               NULL, NULL))
                     panic("stivale: ELF64 load failure");
 
                 ret = elf64_load_section(kernel, &stivale_hdr, ".stivalehdr",
diff --git a/stage23/protos/stivale2.c b/stage23/protos/stivale2.c
index b2e697ef..af06d931 100644
--- a/stage23/protos/stivale2.c
+++ b/stage23/protos/stivale2.c
@@ -115,7 +115,8 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
 
             if (!loaded_by_anchor) {
                 if (elf64_load(kernel, &entry_point, NULL, &slide,
-                               STIVALE2_MMAP_KERNEL_AND_MODULES, kaslr, false))
+                               STIVALE2_MMAP_KERNEL_AND_MODULES, kaslr, false,
+                               NULL, NULL))
                     panic("stivale2: ELF64 load failure");
 
                 ret = elf64_load_section(kernel, &stivale2_hdr, ".stivale2hdr",
tab: 248 wrap: offon