From ff66c8d89f6e391bb91f53e0377e8d5f809ccf43 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Thu, 19 Feb 2026 19:29:03 +0000 Subject: [PATCH] lib: add liblaunch elf loader library --- lib/liblaunch/CMakeLists.txt | 21 ++ lib/liblaunch/elf.c | 547 +++++++++++++++++++++++++++++++++ lib/liblaunch/elf.h | 314 +++++++++++++++++++ lib/liblaunch/include/launch.h | 73 +++++ lib/liblaunch/launch.c | 141 +++++++++ 5 files changed, 1096 insertions(+) create mode 100644 lib/liblaunch/CMakeLists.txt create mode 100644 lib/liblaunch/elf.c create mode 100644 lib/liblaunch/elf.h create mode 100644 lib/liblaunch/include/launch.h create mode 100644 lib/liblaunch/launch.c diff --git a/lib/liblaunch/CMakeLists.txt b/lib/liblaunch/CMakeLists.txt new file mode 100644 index 0000000..bccb93b --- /dev/null +++ b/lib/liblaunch/CMakeLists.txt @@ -0,0 +1,21 @@ +file(GLOB sources + ${CMAKE_CURRENT_SOURCE_DIR}/*.c) +file(GLOB headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/launch.h + ${CMAKE_CURRENT_SOURCE_DIR}/*.h) + +set(public_include_dirs + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +rosetta_add_library( + NAME liblaunch STATIC + PUBLIC_INCLUDE_DIRS ${public_include_dirs} + SOURCES ${sources} + HEADERS ${headers}) + +sysroot_add_library( + NAME liblaunch + HEADER_DIR /usr/include + LIB_DIR /usr/lib) + +target_link_libraries(liblaunch libmango ulibc) diff --git a/lib/liblaunch/elf.c b/lib/liblaunch/elf.c new file mode 100644 index 0000000..e3e3f18 --- /dev/null +++ b/lib/liblaunch/elf.c @@ -0,0 +1,547 @@ +#include "elf.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define NEEDS_NOTHING 0 +#define NEEDS_VDSO 1 +#define NEEDS_MORE 2 + +#define ACL (PF_R | PF_W | PF_X) +#define ACCESS(x) ((x) & ACL) + +/* TODO in case we ever support ELF32 images */ +#define elf_class_bits(x) (64) + +#define PAGE_SIZE (page_size()) +#define PAGE_MASK (page_size() - 1) +#define PAGE_OFFSET(v) ((v) & (PAGE_SIZE - 1)) +#define PAGE_ALIGN_DOWN(v) (v) &= ~(PAGE_SIZE - 1) +#define PAGE_ALIGN_UP(v) \ + do { \ + if ((v) & (PAGE_SIZE - 1)) { \ + v &= ~(PAGE_SIZE - 1); \ + v += PAGE_SIZE; \ + } \ + } while (0) + +#undef DEBUG_LOG + +static size_t page_size(void) +{ + static size_t pagesz = 0; + if (pagesz == 0) { + kern_config_get(KERN_CFG_PAGE_SIZE, &pagesz, sizeof pagesz); + } + + return pagesz; +} + +static enum launch_status elf_validate_ehdr(elf_ehdr_t *hdr) +{ + if (hdr->e_ident[EI_MAG0] != ELF_MAG0) { + return LAUNCH_ERR_INVALID_EXECUTABLE; + } + + if (hdr->e_ident[EI_MAG1] != ELF_MAG1) { + return LAUNCH_ERR_INVALID_EXECUTABLE; + } + + if (hdr->e_ident[EI_MAG2] != ELF_MAG2) { + return LAUNCH_ERR_INVALID_EXECUTABLE; + } + + if (hdr->e_ident[EI_MAG3] != ELF_MAG3) { + return LAUNCH_ERR_INVALID_EXECUTABLE; + } + + if (hdr->e_ident[EI_CLASS] != ELFCLASS64) { + return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE; + } + + if (hdr->e_machine != EM_X86_64) { + return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE; + } + + if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) { + return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE; + } + + if (hdr->e_ident[EI_VERSION] != EV_CURRENT) { + return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE; + } + + return LAUNCH_OK; +} + +static enum launch_status read_header(struct elf_image *image) +{ + size_t nr_read = 0; + vm_object_read( + image->e_image, + &image->e_hdr, + 0, + sizeof image->e_hdr, + &nr_read); + if (nr_read != sizeof image->e_hdr) { + return LAUNCH_ERR_INVALID_EXECUTABLE; + } + + return elf_validate_ehdr(&image->e_hdr); +} + +static enum launch_status parse_phdr(struct elf_image *image) +{ + elf_phdr_t phdr; + size_t r = 0; + image->e_total_size = 0; + image->e_data_size = 0; + + for (size_t i = 0; i < image->e_hdr.e_phnum; i++) { + off_t offset + = image->e_hdr.e_phoff + (i * image->e_hdr.e_phentsize); + kern_status_t status = vm_object_read( + image->e_image, + &phdr, + offset, + sizeof phdr, + &r); + + if (status != KERN_OK || r != sizeof phdr) { + return LAUNCH_ERR_INVALID_EXECUTABLE; + } + + switch (phdr.p_type) { + case PT_DYNAMIC: + image->e_dynamic = phdr; + break; + case PT_LOAD: + if (phdr.p_vaddr & (PAGE_SIZE - 1)) { + phdr.p_vaddr &= (PAGE_SIZE - 1); + } + + if (phdr.p_memsz & (PAGE_SIZE - 1)) { + phdr.p_memsz &= (PAGE_SIZE - 1); + phdr.p_memsz += PAGE_SIZE; + } + + image->e_total_size + = MAX(image->e_total_size, + phdr.p_vaddr + phdr.p_memsz); + break; + case PT_INTERP: { + size_t r = 0; + vm_object_read( + image->e_image, + image->e_interp, + phdr.p_offset, + MIN(sizeof image->e_interp - 1, phdr.p_filesz), + &r); + image->e_interp[r] = 0; + break; + } + default: + break; + } + + if (phdr.p_flags & PF_W) { + image->e_data_size + = MAX(image->e_data_size, + phdr.p_vaddr + phdr.p_memsz); + } + } + + return LAUNCH_OK; +} + +static kern_status_t create_exec_regions(struct elf_image *image) +{ + kern_status_t status = KERN_OK; + if (image->e_local_space != KERN_HANDLE_INVALID) { + status = vm_region_create( + image->e_local_space, + NULL, + 0, + VM_REGION_ANY_OFFSET, + image->e_total_size, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC + | VM_PROT_USER, + &image->e_local_exec, + &image->e_local_base); + } + + if (status != KERN_OK) { + return status; + } + + if (image->e_remote_space != KERN_HANDLE_INVALID) { + status = vm_region_create( + image->e_remote_space, + NULL, + 0, + VM_REGION_ANY_OFFSET, + image->e_total_size, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC + | VM_PROT_USER, + &image->e_remote_exec, + &image->e_remote_base); + } + + if (status != KERN_OK) { + /* TODO cleanup e_local_exec */ + return status; + } + + return KERN_OK; +} + +static enum launch_status map_executable(struct elf_image *image) +{ + elf_phdr_t phdr; + size_t r = 0; + image->e_total_size = 0; + image->e_data_size = 0; + + size_t data_offset = 0; + + for (size_t i = 0; i < image->e_hdr.e_phnum; i++) { + off_t phdr_offset + = image->e_hdr.e_phoff + (i * image->e_hdr.e_phentsize); + kern_status_t status = vm_object_read( + image->e_image, + &phdr, + phdr_offset, + sizeof phdr, + &r); + + if (status != KERN_OK || r != sizeof phdr) { + return LAUNCH_ERR_INVALID_EXECUTABLE; + } + + if (phdr.p_type != PT_LOAD) { + continue; + } + + kern_handle_t vmo = image->e_image; + vm_prot_t prot = VM_PROT_USER; + size_t offset = phdr.p_offset; + + phdr.p_flags &PF_R && (prot |= VM_PROT_READ); + phdr.p_flags &PF_W && (prot |= VM_PROT_WRITE); + phdr.p_flags &PF_X && (prot |= VM_PROT_EXEC); + if (phdr.p_flags & PF_W) { + vmo = image->e_data; + offset = data_offset; + + status = vm_object_copy( + image->e_data, + data_offset + (phdr.p_offset & PAGE_MASK), + image->e_image, + phdr.p_offset, + phdr.p_filesz, + NULL); + } + + if (status != KERN_OK) { + return LAUNCH_ERR_IMAGE_DATA_LOAD_FAILED; + } + + if (image->e_local_exec != KERN_HANDLE_INVALID) { + status = vm_region_map_relative( + image->e_local_exec, + phdr.p_vaddr, + vmo, + offset, + phdr.p_memsz, + prot, + NULL); + } + + if (status != KERN_OK) { + return LAUNCH_ERR_MEMORY_MAP_FAILED; + } + + if (image->e_remote_exec != KERN_HANDLE_INVALID) { + status = vm_region_map_relative( + image->e_remote_exec, + phdr.p_vaddr, + vmo, + offset, + phdr.p_memsz, + prot, + NULL); + } + + if (status != KERN_OK) { + return LAUNCH_ERR_MEMORY_MAP_FAILED; + } + + if (phdr.p_flags & PF_W) { + data_offset += phdr.p_memsz; + if (data_offset & (PAGE_SIZE - 1)) { + data_offset &= (PAGE_SIZE - 1); + data_offset += PAGE_SIZE; + } + } + } + + return LAUNCH_OK; +} + +static elf_sym_t *get_dynsym(struct elf_image *image, size_t index) +{ + return (elf_sym_t *)(image->e_local_base + image->e_dynsym + + (index * image->e_dynsym_entsize)); +} + +static enum launch_status do_rela(struct elf_image *image, elf_rela_t *rela) +{ + int type = ELF64_R_TYPE(rela->r_info); + elf_sym_t *sym = NULL; + + switch (type) { + case R_X86_64_JUMP_SLOT: + sym = get_dynsym(image, ELF64_R_SYM(rela->r_info)); + *(uint64_t *)(image->e_local_base + rela->r_offset) + = image->e_remote_base + sym->st_value + rela->r_addend; + kern_tracef( + "JUMP_SLOT: offset=%zx, symbol=%zu, addend=%zx", + rela->r_offset, + ELF64_R_SYM(rela->r_info), + rela->r_addend); + break; + default: + kern_trace("Unknown relocation type"); + return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE; + } + + return LAUNCH_OK; +} + +static enum launch_status do_rela_list( + struct elf_image *image, + off_t offset, + size_t size, + size_t entsize) +{ + kern_tracef( + "do_rela_list(%p, %d, %d, %d)", + image, + offset, + size, + entsize); + size_t entries = size / entsize; + elf_rela_t *rela = (elf_rela_t *)(image->e_local_base + offset); + enum launch_status status = LAUNCH_OK; + + for (size_t i = 0; i < entries; i++) { + status = do_rela(image, rela); + + if (status != LAUNCH_OK) { + break; + } + + rela = (elf_rela_t *)((char *)rela + entsize); + } + + return LAUNCH_OK; +} + +static enum launch_status do_rel( + struct elf_image *image, + off_t offset, + size_t size, + size_t entsize) + +{ + return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE; +} + +static enum launch_status relocate(struct elf_image *image) +{ + elf_dyn_t *dyn + = (elf_dyn_t *)(image->e_local_base + image->e_dynamic.p_vaddr); + + enum { + RT_REL, + RT_RELA, + RT_PLTREL, + RT_COUNT, + }; + + int pltrel_type = DT_NULL; + off_t offsets[RT_COUNT] = {0}; + size_t sizes[RT_COUNT] = {0}, entsizes[RT_COUNT] = {0}; + + size_t nr_dyn = image->e_dynamic.p_filesz / sizeof *dyn; + for (size_t i = 0; i < nr_dyn; i++) { + switch (dyn[i].d_tag) { + case DT_SYMTAB: + image->e_dynsym = dyn[i].d_un.d_ptr; + break; + case DT_SYMENT: + image->e_dynsym_entsize = dyn[i].d_un.d_val; + break; + case DT_REL: + offsets[RT_REL] = dyn[i].d_un.d_ptr; + break; + case DT_RELSZ: + sizes[RT_REL] = dyn[i].d_un.d_val; + break; + case DT_RELENT: + entsizes[RT_REL] = dyn[i].d_un.d_val; + break; + case DT_RELA: + offsets[RT_RELA] = dyn[i].d_un.d_ptr; + break; + case DT_RELASZ: + sizes[RT_RELA] = dyn[i].d_un.d_val; + break; + case DT_RELAENT: + entsizes[RT_RELA] = dyn[i].d_un.d_val; + break; + case DT_PLTREL: + pltrel_type = dyn[i].d_un.d_val; + switch (pltrel_type) { + case DT_REL: + entsizes[RT_PLTREL] = 0; + break; + case DT_RELA: + entsizes[RT_PLTREL] = sizeof(elf_rela_t); + break; + default: + break; + } + break; + case DT_JMPREL: + offsets[RT_PLTREL] = dyn[i].d_un.d_ptr; + break; + case DT_PLTRELSZ: + sizes[RT_PLTREL] = dyn[i].d_un.d_val; + break; + default: + break; + } + + if (dyn[i].d_tag == DT_NULL) { + break; + } + } + + enum launch_status status = LAUNCH_OK; + if (offsets[RT_RELA] && sizes[RT_RELA] && entsizes[RT_RELA]) { + kern_trace("RELA"); + status = do_rela_list( + image, + offsets[RT_RELA], + sizes[RT_RELA], + entsizes[RT_RELA]); + + if (status != LAUNCH_OK) { + return status; + } + } + + if (offsets[RT_PLTREL] && entsizes[RT_PLTREL]) { + kern_trace("PLTREL"); + if (pltrel_type == DT_REL) { + status = do_rel( + image, + offsets[RT_PLTREL], + sizes[RT_PLTREL], + entsizes[RT_PLTREL]); + } else { + status = do_rela_list( + image, + offsets[RT_PLTREL], + sizes[RT_PLTREL], + entsizes[RT_PLTREL]); + } + + if (status != LAUNCH_OK) { + return status; + } + } + + return LAUNCH_OK; +} + +void elf_image_init(struct elf_image *out) +{ + memset(out, 0x0, sizeof(*out)); + + out->e_image = KERN_HANDLE_INVALID; + out->e_data = KERN_HANDLE_INVALID; + out->e_local_space = KERN_HANDLE_INVALID; + out->e_remote_space = KERN_HANDLE_INVALID; + out->e_local_exec = KERN_HANDLE_INVALID; + out->e_remote_exec = KERN_HANDLE_INVALID; +} + +enum launch_status elf_image_load( + struct elf_image *image, + kern_handle_t exec_object, + kern_handle_t local_space, + kern_handle_t remote_space) +{ + image->e_image = exec_object; + image->e_local_space = local_space; + image->e_remote_space = remote_space; + + enum launch_status status = read_header(image); + if (status != LAUNCH_OK) { + return status; + } + + status = parse_phdr(image); + if (status != LAUNCH_OK) { + return status; + } + + if (image->e_interp[0] != 0) { + return LAUNCH_ERR_INTERPRETER_REQUIRED; + } + + kern_status_t kstatus = vm_object_create( + ".data", + 5, + image->e_data_size, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER, + &image->e_data); + if (kstatus != KERN_OK) { + return LAUNCH_ERR_NO_MEMORY; + } + + status = create_exec_regions(image); + if (status != KERN_OK) { + return LAUNCH_ERR_NO_MEMORY; + } + + status = map_executable(image); + if (status != LAUNCH_OK) { + return status; + } + + status = relocate(image); + if (status != LAUNCH_OK) { + return status; + } + + return LAUNCH_OK; +} + +void elf_image_cleanup(struct elf_image *image) +{ + vm_region_unmap_relative(image->e_local_exec, 0, image->e_total_size); + kern_handle_close(image->e_data); + kern_handle_close(image->e_local_exec); + kern_handle_close(image->e_remote_exec); +} diff --git a/lib/liblaunch/elf.h b/lib/liblaunch/elf.h new file mode 100644 index 0000000..5f961a0 --- /dev/null +++ b/lib/liblaunch/elf.h @@ -0,0 +1,314 @@ +#ifndef USERBOOT_ELF_H_ +#define USERBOOT_ELF_H_ + +#include +#include +#include + +#define ELF_LOAD_ERR -1 +#define ELF_LOADED_EXEC 0 +#define ELF_LOADED_INTERP 1 + +#define ELF_MAG0 0x7f +#define ELF_MAG1 'E' +#define ELF_MAG2 'L' +#define ELF_MAG3 'F' +#define ELF_NIDENT 16 + +#define SHT_NONE 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_DYNAMIC 6 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_DYNSYM 11 + +/** Little endian. */ +#define ELFDATA2LSB (1) + +/** 64-bit. */ +#define ELFCLASS64 (2) + +/** x86_64 machine type. */ +#define EM_X86_64 (62) + +/** ELF current version. */ +#define EV_CURRENT (1) + +/** Dynamic section tags. */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_JMPREL 23 +#define DT_GNU_HASH 0x6ffffef5 +#define DT_AUXILIARY 0x7ffffffd + +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_GOT32X 43 + +#define R_X86_64_64 1 +#define R_X86_64_PC32 2 +#define R_X86_64_GOT32 3 +#define R_X86_64_PLT32 4 +#define R_X86_64_COPY 5 +#define R_X86_64_GLOB_DAT 6 +#define R_X86_64_JUMP_SLOT 7 +#define R_X86_64_RELATIVE 8 +#define R_X86_64_GOTPCREL 9 +#define R_X86_64_32 10 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +/* Section flags */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 + +#define SHN_UNDEF 0 + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((elf_word_t)(i)) +#define ELF64_ST_BIND(i) ((i) >> 4) +#define ELF64_ST_TYPE(i) ((i) & 0xf) + +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_NUM 3 + +typedef uint64_t elf_addr_t; +typedef uint64_t elf_off_t; +typedef uint16_t elf_half_t; +typedef uint32_t elf_word_t; +typedef int32_t elf_sword_t; +typedef uint64_t elf_xword_t; +typedef int64_t elf_sxword_t; + +/** + * ELF file header. + */ +typedef struct { + uint8_t e_ident[ELF_NIDENT]; + elf_half_t e_type; + elf_half_t e_machine; + elf_word_t e_version; + elf_addr_t e_entry; + elf_off_t e_phoff; + elf_off_t e_shoff; + elf_word_t e_flags; + elf_half_t e_ehsize; + elf_half_t e_phentsize; + elf_half_t e_phnum; + elf_half_t e_shentsize; + elf_half_t e_shnum; + elf_half_t e_shstrndx; +} elf_ehdr_t; + +/** + * ELF section header. + */ +typedef struct { + elf_word_t sh_name; + elf_word_t sh_type; + elf_xword_t sh_flags; + elf_addr_t sh_addr; + elf_off_t sh_offset; + elf_xword_t sh_size; + elf_word_t sh_link; + elf_word_t sh_info; + elf_xword_t sh_addralign; + elf_xword_t sh_entsize; +} elf_shdr_t; + +/** + * ELF symbol. + */ +typedef struct { + elf_word_t st_name; + unsigned char st_info; + unsigned char st_other; + elf_half_t st_shndx; + elf_addr_t st_value; + elf_xword_t st_size; +} elf_sym_t; + +/** + * ELF program header. + */ +typedef struct { + elf_word_t p_type; + elf_word_t p_flags; + elf_off_t p_offset; + elf_addr_t p_vaddr; + elf_addr_t p_paddr; + elf_xword_t p_filesz; + elf_xword_t p_memsz; + elf_xword_t p_align; +} elf_phdr_t; + +/** + * Extended ELF relocation information. + */ +typedef struct { + elf_addr_t r_offset; + elf_xword_t r_info; + elf_sxword_t r_addend; +} elf_rela_t; + +/** + * Dynamic section entries + */ +typedef struct { + elf_sxword_t d_tag; + union { + elf_xword_t d_val; + elf_addr_t d_ptr; + } d_un; +} elf_dyn_t; + +/** + * Section header types. + */ +enum elf_stype { + ST_NONE = 0, + ST_PROGBITS = 1, + ST_SYMTAB = 2, + ST_STRTAB = 3, + ST_NOBITS = 8, + ST_REL = 9 +}; + +/** + * Program header types. + */ +enum elf_ptype { + PT_NULL = 0, + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_INTERP = 3, + PT_NOTE = 4, + PT_SHLIB = 5, + PT_PHDR = 6 +}; + +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 + +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7FFFFFFF + +/** + * ELF identification byte locations. + */ +enum elf_ident { + EI_MAG0 = 0, + EI_MAG1 = 1, + EI_MAG2 = 2, + EI_MAG3 = 3, + EI_CLASS = 4, + EI_DATA = 5, + EI_VERSION = 6, + EI_OSABI = 7, + EI_ABIVERSION = 8, + EI_PAD = 9 +}; + +enum elf_type { + ET_NONE = 0, + ET_REL = 1, + ET_EXEC = 2, + ET_DYN = 3, +}; + +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#define AT_NOTELF 10 +#define AT_UID 11 +#define AT_EUID 12 +#define AT_GID 13 +#define AT_EGID 14 +#define AT_CLKTCK 17 +#define AT_PLATFORM 15 +#define AT_HWCAP 16 +#define AT_FPUCW 18 +#define AT_DCACHEBSIZE 19 +#define AT_ICACHEBSIZE 20 +#define AT_UCACHEBSIZE 21 +#define AT_IGNOREPPC 22 +#define AT_SECURE 23 +#define AT_BASE_PLATFORM 24 +#define AT_RANDOM 25 +#define AT_HWCAP2 26 +#define AT_EXECFN 31 +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 +#define AT_ENTRY_COUNT 38 + +struct bootdata; +struct bootfs_file; + +struct elf_image { + kern_handle_t e_image, e_data; + kern_handle_t e_local_space, e_remote_space; + kern_handle_t e_local_exec, e_remote_exec; + virt_addr_t e_local_base, e_remote_base; + elf_ehdr_t e_hdr; + elf_phdr_t e_dynamic; + off_t e_dynsym; + size_t e_dynsym_entsize; + + char e_interp[256]; + size_t e_total_size, e_data_size; +}; + +extern void elf_image_init(struct elf_image *out); +extern enum launch_status elf_image_load( + struct elf_image *image, + kern_handle_t exec_object, + kern_handle_t local_space, + kern_handle_t remote_space); + +extern void elf_image_cleanup(struct elf_image *image); + +#endif diff --git a/lib/liblaunch/include/launch.h b/lib/liblaunch/include/launch.h new file mode 100644 index 0000000..4364b86 --- /dev/null +++ b/lib/liblaunch/include/launch.h @@ -0,0 +1,73 @@ +#ifndef LAUNCH_H_ +#define LAUNCH_H_ + +#include + +enum launch_status { + LAUNCH_OK, + /* a memory allocation failed */ + LAUNCH_ERR_NO_MEMORY, + /* executable file is corrupt or of an unrecognised format. */ + LAUNCH_ERR_INVALID_EXECUTABLE, + /* executable file IS valid and IS of a recognised format, but is + * not supported by this machine (different class, architecture, + * version, etc). + */ + LAUNCH_ERR_UNSUPPORTED_EXECUTABLE, + /* a particular dependency of the executable could not be resolved. */ + LAUNCH_ERR_CANNOT_RESOLVE_DEPENDENCY, + LAUNCH_ERR_MEMORY_MAP_FAILED, + LAUNCH_ERR_IMAGE_DATA_LOAD_FAILED, + LAUNCH_ERR_INTERPRETER_REQUIRED, + LAUNCH_ERR_TASK_CREATION_FAILED, + LAUNCH_ERR_THREAD_CREATION_FAILED, +}; + +enum launch_flags { + LAUNCH_F_NONE = 0, +}; + +struct launch_ctx; + +typedef enum launch_status (*launch_resolve_library_function)( + struct launch_ctx *, + const char *, + kern_handle_t *, + void *); + +struct launch_ctx { + launch_resolve_library_function ctx_resolve_library; +}; + +struct launch_parameters { + kern_handle_t p_parent_task; + kern_handle_t p_local_address_space; + kern_handle_t p_executable; + + const char *p_task_name; + + int p_argc; + const char **p_argv; + + int p_envc; + const char **p_envp; + + void *p_resolver_arg; +}; + +struct launch_result { + kern_handle_t r_task; + kern_handle_t r_thread; + kern_handle_t r_address_space; +}; + +extern enum launch_status launch_ctx_init(struct launch_ctx *ctx); +extern void launch_ctx_cleanup(struct launch_ctx *ctx); + +extern enum launch_status launch_ctx_execute( + struct launch_ctx *ctx, + const struct launch_parameters *params, + enum launch_flags flags, + struct launch_result *result); + +#endif diff --git a/lib/liblaunch/launch.c b/lib/liblaunch/launch.c new file mode 100644 index 0000000..eb81d51 --- /dev/null +++ b/lib/liblaunch/launch.c @@ -0,0 +1,141 @@ +#include "elf.h" + +#include +#include +#include +#include +#include +#include +#include + +#define STACK_SIZE 0x10000 + +enum launch_status launch_ctx_init(struct launch_ctx *ctx) +{ + memset(ctx, 0x0, sizeof *ctx); + return LAUNCH_OK; +} + +void launch_ctx_cleanup(struct launch_ctx *ctx) +{ +} + +static kern_handle_t get_library( + struct launch_ctx *ctx, + const char *name, + void *arg) +{ + enum launch_status status = LAUNCH_ERR_CANNOT_RESOLVE_DEPENDENCY; + kern_handle_t result = KERN_HANDLE_INVALID; + if (ctx->ctx_resolve_library) { + status = ctx->ctx_resolve_library(ctx, name, &result, arg); + } + + return result; +} + +enum launch_status launch_ctx_execute( + struct launch_ctx *ctx, + const struct launch_parameters *params, + enum launch_flags flags, + struct launch_result *result) +{ + kern_status_t kstatus; + kern_handle_t stack_vmo; + + kstatus = vm_object_create( + "stack", + 5, + STACK_SIZE, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER, + &stack_vmo); + + size_t name_len = params->p_task_name ? strlen(params->p_task_name) : 0; + kern_handle_t remote_task = KERN_HANDLE_INVALID, + remote_address_space = KERN_HANDLE_INVALID; + kstatus = task_create( + params->p_parent_task, + params->p_task_name, + name_len, + &remote_task, + &remote_address_space); + if (kstatus != KERN_OK) { + kern_handle_close(stack_vmo); + return LAUNCH_ERR_TASK_CREATION_FAILED; + } + + struct elf_image image; + elf_image_init(&image); + + enum launch_status status = elf_image_load( + &image, + params->p_executable, + params->p_local_address_space, + remote_address_space); + + if (status == LAUNCH_ERR_INTERPRETER_REQUIRED) { + kern_handle_t interp = get_library( + ctx, + image.e_interp, + params->p_resolver_arg); + if (interp == KERN_HANDLE_INVALID) { + elf_image_cleanup(&image); + kern_handle_close(stack_vmo); + kern_handle_close(remote_address_space); + kern_handle_close(remote_task); + return status; + } + + elf_image_init(&image); + status = elf_image_load( + &image, + interp, + params->p_local_address_space, + remote_address_space); + } + + if (status != LAUNCH_OK) { + elf_image_cleanup(&image); + kern_handle_close(stack_vmo); + kern_handle_close(remote_address_space); + kern_handle_close(remote_task); + return status; + } + + virt_addr_t stack_buf; + kstatus = vm_region_map_relative( + remote_address_space, + VM_REGION_ANY_OFFSET, + stack_vmo, + 0, + STACK_SIZE, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER, + &stack_buf); + kern_handle_close(stack_vmo); + + if (kstatus != KERN_OK) { + elf_image_cleanup(&image); + kern_handle_close(remote_address_space); + kern_handle_close(remote_task); + return LAUNCH_ERR_MEMORY_MAP_FAILED; + } + + virt_addr_t ip = image.e_hdr.e_entry + image.e_remote_base; + virt_addr_t sp = stack_buf + STACK_SIZE; + + kern_handle_t thread; + kstatus = task_create_thread(remote_task, ip, sp, NULL, 0, &thread); + if (kstatus != KERN_OK) { + elf_image_cleanup(&image); + kern_handle_close(remote_address_space); + kern_handle_close(remote_task); + return LAUNCH_ERR_THREAD_CREATION_FAILED; + } + + thread_start(thread); + + kern_handle_close(thread); + elf_image_cleanup(&image); + + return LAUNCH_OK; +}