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;
