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",
