From 361a0968658cac60704a042cd6da1d66a9846ff6 Mon Sep 17 00:00:00 2001 From: Hubert Badocha Date: Fri, 27 Oct 2023 18:01:22 +0200 Subject: [PATCH] process/load: Add 32bit interpreter support JIRA: RTOS-664 --- include/auxv.h | 41 +++++++++ proc/elf.h | 115 +++++++++++++---------- proc/process.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 340 insertions(+), 62 deletions(-) create mode 100644 include/auxv.h diff --git a/include/auxv.h b/include/auxv.h new file mode 100644 index 000000000..d01e4c970 --- /dev/null +++ b/include/auxv.h @@ -0,0 +1,41 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Auxiliary vector definitions + * + * Copyright 2024 Phoenix Systems + * Author: Hubert Badocha + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _PHOENIX_AUXV_H_ +#define _PHOENIX_AUXV_H_ + + +#include "types.h" + + +struct auxInfo { + __u32 a_type; /* Type of element. */ + __u64 a_v; /* Value of element. */ +}; + + +#define AT_NULL 0 /* End of auxiliary vector. */ +#define AT_PAGESZ 1 /* Page size. */ +#define AT_BASE 2 /* Base address of interpreter. */ +#define AT_ENTRY 3 /* Entry point address. */ +#define AT_PHDR 4 /* Location of program header table. */ +#define AT_PHENT 5 /* Size of one entry in program header table. */ +#define AT_PHNUM 6 /* Number of entries in program header table. */ + + +#define AUXV_TYPE_COUNT 7 /* Number of auxiliary vector element types. */ + + +#endif diff --git a/proc/elf.h b/proc/elf.h index e0b8bbbe7..1cca08c5b 100644 --- a/proc/elf.h +++ b/proc/elf.h @@ -19,10 +19,10 @@ typedef unsigned short Elf32_Half; -typedef unsigned int Elf32_Word; -typedef unsigned int Elf32_Addr; -typedef unsigned int Elf32_Off; -typedef int Elf32_Sword; +typedef unsigned int Elf32_Word; +typedef unsigned int Elf32_Addr; +typedef unsigned int Elf32_Off; +typedef int Elf32_Sword; typedef u16 Elf64_Half; @@ -33,31 +33,47 @@ typedef s64 Elf64_Sword; typedef u64 Elf64_Xword; -#define EI_NIDENT 16 - -#define SHT_SYMTAB 2 -#define SHT_STRTAB 3 -#define SHT_NOBITS 8 -#define SHT_REL 9 -#define SHT_DYNSYM 11 -#define SHT_LOPROC 0x70000000 -#define SHT_HIPROC 0x7fffffff -#define SHT_LOUSER 0x80000000 -#define SHT_HIUSER 0xffffffff - -#define STT_LOPROC 13 -#define STT_HIPROC 15 - -#define PT_LOAD 1 -#define PT_DYNAMIC 2 -#define PT_INTERP 3 -#define PT_GNU_STACK 0x6474e551 -#define PT_LOPROC 0x70000000 -#define PT_HIPROC 0x7fffffff - -#define PF_X 0x1 -#define PF_W 0x2 -#define PF_R 0x4 +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 + +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +#define EI_NIDENT 16 + +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_PHDR 6 +#define PT_GNU_STACK 0x6474e551 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 #pragma pack(push, 1) @@ -67,8 +83,8 @@ typedef struct { Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; - Elf32_Off e_phoff; - Elf32_Off e_shoff; + Elf32_Off e_phoff; + Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_hsize; Elf32_Half e_phentsize; @@ -84,7 +100,7 @@ typedef struct { Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; - Elf32_Off sh_offset; + Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; @@ -95,7 +111,7 @@ typedef struct { typedef struct { Elf32_Word p_type; - Elf32_Off p_offset; + Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; @@ -106,18 +122,18 @@ typedef struct { typedef struct { - u32 st_name; - Elf32_Addr st_value; - u32 st_size; + u32 st_name; + Elf32_Addr st_value; + u32 st_size; unsigned char st_info; unsigned char st_other; - u16 st_shndx; + u16 st_shndx; } Elf32_Sym; typedef struct { Elf32_Addr r_offset; - u32 r_info; + u32 r_info; } Elf32_Rel; @@ -129,13 +145,13 @@ typedef struct { typedef struct { - unsigned char e_ident [EI_NIDENT]; + unsigned char e_ident[EI_NIDENT]; Elf64_Half e_type; Elf64_Half e_machine; Elf64_Word e_version; Elf64_Addr e_entry; - Elf64_Off e_phoff; - Elf64_Off e_shoff; + Elf64_Off e_phoff; + Elf64_Off e_shoff; Elf64_Word e_flags; Elf64_Half e_ehsize; Elf64_Half e_phentsize; @@ -147,11 +163,11 @@ typedef struct { typedef struct { - Elf64_Word p_type; - Elf64_Word p_flags; - Elf64_Off p_offset; - Elf64_Addr p_vaddr; - Elf64_Addr p_paddr; + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf64_Xword p_align; @@ -174,10 +190,9 @@ typedef struct { #pragma pack(pop) - -#define ELF32_R_SYM(info) ((info)>>8) -#define ELF32_R_TYPE(info) ((unsigned char)(info)) -#define ELF32_R_INFO(sym, type) (((sym)<<8)+(unsigned char)(type)) +#define ELF32_R_SYM(info) ((info) >> 8) +#define ELF32_R_TYPE(info) ((unsigned char)(info)) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + (unsigned char)(type)) #define R_ARM_ABS32 2 diff --git a/proc/process.c b/proc/process.c index 19314efac..f1b04f9ce 100644 --- a/proc/process.c +++ b/proc/process.c @@ -17,6 +17,7 @@ #include "hal/hal.h" #include "include/errno.h" #include "include/signal.h" +#include "include/auxv.h" #include "vm/vm.h" #include "lib/lib.h" #include "posix/posix.h" @@ -481,7 +482,7 @@ static int process_validateElf64(void *iehdr, size_t size) /* TODO - adding error handling and unmapping of already mapped segments */ -int process_load32(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_t size, size_t *ustacksz, hal_tls_t *tls, ptr_t *tbssAddr) +int process_load32(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_t size, size_t *ustacksz, hal_tls_t *tls, ptr_t *tbssAddr, struct auxInfo **auxData) { void *vaddr; size_t memsz, filesz; @@ -496,6 +497,16 @@ int process_load32(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ return -ENOEXEC; } + (*auxData)->a_type = AT_ENTRY; + (*auxData)->a_v = (u64)ehdr->e_entry; + (*auxData)++; + (*auxData)->a_type = AT_PHNUM; + (*auxData)->a_v = (u64)ehdr->e_phnum; + (*auxData)++; + (*auxData)->a_type = AT_PHENT; + (*auxData)->a_v = (u64)ehdr->e_phentsize; + (*auxData)++; + shdr = (void *)((char *)ehdr + ehdr->e_shoff); shstrshdr = shdr + ehdr->e_shstrndx; snameTab = (char *)ehdr + shstrshdr->sh_offset; @@ -519,6 +530,11 @@ int process_load32(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ *ustacksz = round_page(phdr->p_memsz); } + if (phdr->p_type == PT_PHDR) { + (*auxData)->a_type = AT_PHDR; + (*auxData)->a_v = (u64)phdr->p_vaddr; + (*auxData)++; + } if ((phdr->p_type != PT_LOAD) || (phdr->p_vaddr == 0)) { continue; } @@ -647,13 +663,193 @@ int process_load64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ } -int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, void **ustack, void **entry) +int process_getInterpreterPath32(const Elf32_Ehdr *ehdr, const char **interpreterPath) +{ + const Elf32_Phdr *phdrs = (const void *)ehdr + ehdr->e_phoff; + Elf32_Half i; + Elf32_Word letter; + + *interpreterPath = NULL; + + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdrs[i].p_type != PT_INTERP) { + continue; + } + + if (phdrs[i].p_filesz == 0) { + return -EINVAL; + } + + *interpreterPath = (const char *)ehdr + phdrs[i].p_offset; + /* Check that interpreter path is of specified length. */ + for (letter = 0; letter < phdrs[i].p_filesz; letter++) { + if ((*interpreterPath)[letter] == '\0') { + break; + } + } + if (letter != (phdrs[i].p_filesz - 1)) { + return -EINVAL; + } + } + + return EOK; +} + + +int process_doLoadInterpreter32(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_t size, void **baseAddr, void **interpreterEntry) +{ + void *vaddr; + size_t memsz, filesz; + Elf32_Ehdr *ehdr = iehdr; + Elf32_Phdr *phdrs = (void *)ehdr + ehdr->e_phoff; + unsigned i, prot, flags; + Elf32_Off offs, misalign; + Elf32_Word al; + Elf32_Addr loadLow, loadHigh; + size_t loadSize; + + if (process_validateElf32(iehdr, size) < 0) { + return -ENOEXEC; + } + + /* Dynamic linker needs to be mapped in a contignous part of memory. */ + /* Get lowest and highest loaded address to to obtain total size. */ + loadLow = ~(Elf32_Addr)0; + loadHigh = 0; + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdrs[i].p_type == PT_LOAD) { + loadLow = min(loadLow, phdrs[i].p_vaddr); + loadHigh = max(loadHigh, phdrs[i].p_vaddr + phdrs[i].p_memsz); + } + } + + /* No loadable section found. */ + if (loadHigh < loadLow) { + return -ENOEXEC; + } + /* Find a place in vm where interpreter will fit. */ + loadSize = round_page(loadHigh - loadLow); + *baseAddr = vm_mmap(map, (void *)(ptr_t)loadLow, NULL, loadSize, PROT_USER, NULL, -1, MAP_NONE); + if ((*baseAddr) == NULL) { + return -ENOMEM; + } + if (vm_munmap(map, *baseAddr, loadSize) < 0) { + return -ENOMEM; + } + + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdrs[i].p_type != PT_LOAD) { + continue; + } + + al = max(phdrs[i].p_align, 1); + vaddr = ((char *)*baseAddr) + ((phdrs[i].p_vaddr & ~(al - 1)) - loadLow); + offs = phdrs[i].p_offset & ~(al - 1); + misalign = phdrs[i].p_offset & (al - 1); + filesz = (phdrs[i].p_filesz != 0) ? (phdrs[i].p_filesz + misalign) : 0; + memsz = phdrs[i].p_memsz + misalign; + + prot = PROT_USER; + flags = MAP_FIXED; + + if ((phdrs[i].p_flags & PF_R) != 0) { + prot |= PROT_READ; + } + + if ((phdrs[i].p_flags & PF_W) != 0) { + prot |= PROT_WRITE; + } + + if ((phdrs[i].p_flags & PF_X) != 0) { + prot |= PROT_EXEC; + } + + if (filesz != 0) { + if ((prot & PROT_WRITE) != 0) { + flags |= MAP_NEEDSCOPY; + } + if (vm_mmap(map, vaddr, NULL, round_page(filesz), prot, o, base + offs, flags) == NULL) { + return -ENOMEM; + } + } + + if (filesz != memsz) { + if (round_page(memsz) != round_page(filesz)) { + if (vm_mmap(map, vaddr + round_page(filesz), NULL, round_page(memsz) - round_page(filesz), prot, NULL, -1, MAP_FIXED) == NULL) { + return -ENOMEM; + } + } + hal_memset(vaddr + filesz, 0, round_page((ptr_t)vaddr + memsz) - ((ptr_t)vaddr + filesz)); + } + } + + *interpreterEntry = ((char *)(*baseAddr) + (ehdr->e_entry - loadLow)); + + return EOK; +} + + +/* *interpreterEntry is changed only if interpreter is found and loaded properly. */ +int process_loadInterpreter32(vm_map_t *map, vm_object_t *o, off_t base, const void *iehdr, size_t size, void **interpreterEntry, struct auxInfo **auxData) +{ + const Elf32_Ehdr *ehdr = iehdr; + int err; + const char *interpreterPath; + oid_t interpOid; + vm_object_t *interpO; + off_t interpBase = 0; + void *interpIehdr; + size_t interpSize; + void *baseAddr; + + err = process_getInterpreterPath32(ehdr, &interpreterPath); + if (err < 0) { + return err; + } + if (interpreterPath == NULL) { + return EOK; + } + + /* Load interpreter. */ + err = proc_lookup(interpreterPath, NULL, &interpOid); + if (err < 0) { + return err; + } + + err = vm_objectGet(&interpO, interpOid); + if (err < 0) { + return err; + } + interpSize = round_page(interpO->size); + interpIehdr = vm_mmap(process_common.kmap, NULL, NULL, interpSize, PROT_READ, interpO, interpBase, MAP_NONE); + if (interpIehdr == NULL) { + vm_objectPut(interpO); + return -ENOMEM; + } + err = process_doLoadInterpreter32(map, interpO, interpBase, interpIehdr, interpSize, &baseAddr, interpreterEntry); + + vm_munmap(process_common.kmap, interpIehdr, interpSize); + vm_objectPut(interpO); + + if (err < 0) { + return err; + } + + (*auxData)->a_type = AT_BASE; + (*auxData)->a_v = (u64)(ptr_t)baseAddr; + (*auxData)++; + + return EOK; +} + + +int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, void **ustack, void **entry, struct auxInfo **auxData) { void *stack; Elf64_Ehdr *ehdr; vm_map_t *map = process->mapp; size_t ustacksz = SIZE_USTACK; - int err = EOK; + int err; hal_tls_t tlsNew; ptr_t tbssAddr = 0; @@ -670,19 +866,20 @@ int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, vo return -ENOMEM; } - switch (ehdr->e_ident[4]) { - /* 32-bit binary */ - case 1: + switch (ehdr->e_ident[EI_CLASS]) { + case ELFCLASS32: *entry = (void *)(ptr_t)((Elf32_Ehdr *)ehdr)->e_entry; - err = process_load32(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr); + err = process_load32(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr, auxData); + if (err == 0) { + err = process_loadInterpreter32(map, o, base, ehdr, size, entry, auxData); + } break; - /* 64-bit binary */ - case 2: + case ELFCLASS64: *entry = (void *)(ptr_t)ehdr->e_entry; err = process_load64(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr); + /* FIXME: loading interpreter on 64 bit ELFs is not supported */ break; - default: err = -ENOEXEC; } @@ -707,6 +904,25 @@ int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, vo return EOK; } + +static void *process_putauxv(void *stack, struct auxInfo *auxDataBegin, struct auxInfo *auxDataEnd) +{ + size_t sz; + + auxDataEnd->a_type = AT_PAGESZ; + auxDataEnd->a_v = SIZE_PAGE; + auxDataEnd++; + auxDataEnd->a_type = AT_NULL; + auxDataEnd->a_v = 0; + auxDataEnd++; + + sz = ((ptr_t)auxDataEnd - (ptr_t)auxDataBegin); + stack = (char *)stack - SIZE_STACK_ARG(sz); + hal_memcpy(stack, auxDataBegin, sz); + + return stack; +} + #else struct _reloc { @@ -736,7 +952,7 @@ static int process_relocate(struct _reloc *reloc, size_t relocsz, char **addr) } -int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, void **ustack, void **entry) +int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, void **ustack, void **entry, struct auxInfo **auxData) { void *stack, *paddr; Elf32_Ehdr *ehdr; @@ -759,6 +975,8 @@ int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, vo hal_tls_t tlsNew; ptr_t tbssAddr = 0; + (void)auxData; /* FIXME: Aux data is currently only supported on MMU platforms. */ + if (o != VM_OBJ_PHYSMEM) { return -ENOEXEC; } @@ -1095,6 +1313,8 @@ static void process_exec(thread_t *current, process_spawn_t *spawn) { &count, sizeof(count) }, { &cleanupFn, sizeof(cleanupFn) } }; + struct auxInfo auxData[AUXV_TYPE_COUNT]; + struct auxInfo *auxDataEnd = auxData; current->process->argv = spawn->argv; current->process->envp = spawn->envp; @@ -1124,10 +1344,12 @@ static void process_exec(thread_t *current, process_spawn_t *spawn) pmap_switch(current->process->pmapp); if (err == 0) { - err = process_load(current->process, spawn->object, spawn->offset, spawn->size, &stack, &entry); + err = process_load(current->process, spawn->object, spawn->offset, spawn->size, &stack, &entry, &auxDataEnd); } if (err == 0) { + stack = process_putauxv(stack, auxData, auxDataEnd); + stack = process_putargs(stack, &spawn->envp, &count); stack = process_putargs(stack, &spawn->argv, &count); hal_stackPutArgs(&stack, sizeof(args) / sizeof(args[0]), args);