|
15 | 15 | #include <heap.h> |
16 | 16 | #include <filesystems/vfs.h> |
17 | 17 | #include <paging.h> |
| 18 | +#include <userland.h> |
| 19 | + |
| 20 | +static uint32_t* elf_vfs_pos_ptr(vfs_file_t* file) |
| 21 | +{ |
| 22 | + if (!file || !file->mnt) |
| 23 | + return NULL; |
| 24 | + |
| 25 | + switch (file->mnt->type) { |
| 26 | + case FS_FAT16: |
| 27 | + return &file->f.fat16.pos; |
| 28 | + case FS_FAT32: |
| 29 | + return &file->f.fat32.pos; |
| 30 | + case FS_ISO9660: |
| 31 | + return &file->f.iso9660.pos; |
| 32 | + default: |
| 33 | + return NULL; |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +static int elf_vfs_seek(vfs_file_t* file, uint32_t offset) |
| 38 | +{ |
| 39 | + uint32_t* pos = elf_vfs_pos_ptr(file); |
| 40 | + if (!pos) |
| 41 | + return -1; |
| 42 | + |
| 43 | + if (file->mnt->type == FS_FAT32) { |
| 44 | + fat32_file_t* fat32 = &file->f.fat32; |
| 45 | + uint32_t cluster_size = fat32->fs->sectors_per_cluster * FAT32_SECTOR_SIZE; |
| 46 | + uint32_t cluster = fat32->start_cluster; |
| 47 | + uint32_t steps = cluster_size ? (offset / cluster_size) : 0; |
| 48 | + |
| 49 | + while (steps > 0 && cluster < FAT32_CLUSTER_EOC) { |
| 50 | + cluster = fat32_read_fat(fat32->fs, cluster); |
| 51 | + steps--; |
| 52 | + } |
| 53 | + |
| 54 | + fat32->current_cluster = cluster; |
| 55 | + } |
| 56 | + |
| 57 | + *pos = offset; |
| 58 | + return 0; |
| 59 | +} |
| 60 | + |
| 61 | +static int elf_vfs_read_exact(vfs_file_t* file, uint32_t offset, void* buf, uint32_t size) |
| 62 | +{ |
| 63 | + if (elf_vfs_seek(file, offset) != 0) |
| 64 | + return -1; |
| 65 | + |
| 66 | + int rd = vfs_read(file, (uint8_t*)buf, size); |
| 67 | + return (rd >= 0 && (uint32_t)rd == size) ? 0 : -1; |
| 68 | +} |
| 69 | + |
| 70 | +static int elf_vfs_read_exact_path(const char* path, uint32_t offset, void* buf, uint32_t size) |
| 71 | +{ |
| 72 | + vfs_file_t file; |
| 73 | + if (vfs_open(path, VFS_RDONLY, &file) != 0) |
| 74 | + return -1; |
| 75 | + |
| 76 | + int rc = elf_vfs_read_exact(&file, offset, buf, size); |
| 77 | + vfs_close(&file); |
| 78 | + return rc; |
| 79 | +} |
| 80 | + |
| 81 | +static uint64_t elf_runtime_addr_for_offset(Elf64_Phdr* headers, uint16_t phnum, uint64_t file_offset) |
| 82 | +{ |
| 83 | + for (uint16_t i = 0; i < phnum; ++i) { |
| 84 | + Elf64_Phdr* ph = &headers[i]; |
| 85 | + if (ph->p_type != PT_LOAD || ph->p_filesz == 0) |
| 86 | + continue; |
| 87 | + |
| 88 | + uint64_t seg_start = ph->p_offset; |
| 89 | + uint64_t seg_end = ph->p_offset + ph->p_filesz; |
| 90 | + if (file_offset >= seg_start && file_offset < seg_end) |
| 91 | + return ph->p_vaddr + (file_offset - ph->p_offset); |
| 92 | + } |
| 93 | + |
| 94 | + return 0; |
| 95 | +} |
| 96 | + |
| 97 | +static uint64_t elf_stage_phdrs_for_user(Elf64_Phdr* headers, uint64_t phdr_bytes) |
| 98 | +{ |
| 99 | + if (!headers || phdr_bytes == 0) |
| 100 | + return 0; |
| 101 | + |
| 102 | + uint64_t base = USER_PHDR_VADDR; |
| 103 | + uint64_t aligned = (phdr_bytes + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); |
| 104 | + |
| 105 | + for (uint64_t off = 0; off < aligned; off += PAGE_SIZE) { |
| 106 | + uint64_t phys = allocate_page(); |
| 107 | + map_user_page(base + off, phys, USER_DATA_FLAGS); |
| 108 | + } |
| 109 | + |
| 110 | + memset((void*)base, 0, aligned); |
| 111 | + memcpy((void*)base, headers, phdr_bytes); |
| 112 | + return base; |
| 113 | +} |
| 114 | + |
| 115 | +static int elf_validate_header(const Elf64_Ehdr* header, uint64_t file_size) |
| 116 | +{ |
| 117 | + if (memcmp(&header->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 || header->e_ident[EI_CLASS] != ELFCLASS64 || |
| 118 | + header->e_ident[EI_DATA] != ELFDATA2LSB || header->e_type != ET_EXEC || |
| 119 | + header->e_machine != EM_X86_64 || header->e_version != EV_CURRENT) |
| 120 | + { |
| 121 | + error("Not a valid ELF file to load!", __FILE__); |
| 122 | + return -1; |
| 123 | + } |
| 124 | + |
| 125 | + if (header->e_phoff + ((uint64_t)header->e_phnum * header->e_phentsize) > file_size || |
| 126 | + header->e_phentsize != sizeof(Elf64_Phdr)) { |
| 127 | + eprintf("elf: invalid program header table"); |
| 128 | + return -1; |
| 129 | + } |
| 130 | + |
| 131 | + return 0; |
| 132 | +} |
18 | 133 |
|
19 | 134 | static uint32_t* elf_vfs_pos_ptr(vfs_file_t* file) |
20 | 135 | { |
@@ -255,7 +370,10 @@ void* elf_load_from_memory_ex(void* file_base_address, uint64_t file_size, elf_i |
255 | 370 |
|
256 | 371 | if (info) { |
257 | 372 | info->entry = header.e_entry; |
258 | | - info->phdr_addr = elf_runtime_addr_for_offset(program_headers_start, header.e_phnum, header.e_phoff); |
| 373 | + info->phdr_addr = elf_stage_phdrs_for_user(program_headers_start, |
| 374 | + (uint64_t)header.e_phnum * header.e_phentsize); |
| 375 | + if (info->phdr_addr == 0) |
| 376 | + info->phdr_addr = elf_runtime_addr_for_offset(program_headers_start, header.e_phnum, header.e_phoff); |
259 | 377 | info->phentsize = header.e_phentsize; |
260 | 378 | info->phnum = header.e_phnum; |
261 | 379 | } |
@@ -338,7 +456,9 @@ void* elf_load_from_vfs_ex(const char* path, elf_image_info_t* info) |
338 | 456 |
|
339 | 457 | if (info) { |
340 | 458 | info->entry = header.e_entry; |
341 | | - info->phdr_addr = elf_runtime_addr_for_offset(program_headers, header.e_phnum, header.e_phoff); |
| 459 | + info->phdr_addr = elf_stage_phdrs_for_user(program_headers, phdr_bytes); |
| 460 | + if (info->phdr_addr == 0) |
| 461 | + info->phdr_addr = elf_runtime_addr_for_offset(program_headers, header.e_phnum, header.e_phoff); |
342 | 462 | info->phentsize = header.e_phentsize; |
343 | 463 | info->phnum = header.e_phnum; |
344 | 464 | } |
|
0 commit comments