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 #include -#include +#include #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;