From e72f5f7a902e97429e63263f77af16aca2db7801 Mon Sep 17 00:00:00 2001 From: Hubert Badocha Date: Tue, 30 Jan 2024 15:21:32 +0100 Subject: [PATCH 1/3] vm/mmap: add basic MAP_PRIVATE support JIRA: RTOS-756 --- include/mman.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mman.h b/include/mman.h index a9fd56cf5..f392a2080 100644 --- a/include/mman.h +++ b/include/mman.h @@ -28,7 +28,7 @@ #define MAP_FIXED (1 << 7) /* NOTE: vm uses u8 to store flags, if more flags are needed this type needs to be changed. */ #define MAP_SHARED 0x0 -#define MAP_PRIVATE 0x0 +#define MAP_PRIVATE MAP_NEEDSCOPY #define PROT_NONE 0x0 From e211e34dc9d8dd288708b45d2890756748645dc7 Mon Sep 17 00:00:00 2001 From: Hubert Badocha Date: Fri, 27 Oct 2023 18:01:22 +0200 Subject: [PATCH 2/3] process/load: Add interpreter support JIRA: RTOS-664 --- include/auxv.h | 41 +++++ proc/elf.h | 115 ++++++++------ proc/process.c | 424 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 473 insertions(+), 107 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 55382d1b3..d545ff98b 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)) #endif diff --git a/proc/process.c b/proc/process.c index 02d2ee150..bb4ee0426 100644 --- a/proc/process.c +++ b/proc/process.c @@ -18,6 +18,7 @@ #include "hal/elf.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" @@ -360,10 +361,17 @@ static int process_validateElf32(void *iehdr, size_t size) return -ENOEXEC; } for (i = 0; i < ehdr->e_phnum; i++) { - if (phdr->p_type != PT_LOAD) { - if (process_isPtrValid(iehdr, size, ((char *)ehdr) + phdr[i].p_offset, phdr[i].p_filesz) == 0) { + if (process_isPtrValid(iehdr, size, ((char *)ehdr) + phdr[i].p_offset, phdr[i].p_filesz) == 0) { + return -ENOEXEC; + } + + if (phdr->p_type == PT_INTERP) { + if ((phdr->p_filesz == 0) || (((const char *)ehdr + phdr->p_offset)[phdr->p_filesz - 1] != '\0')) { return -ENOEXEC; } + } + + if (phdr->p_type != PT_LOAD) { continue; } @@ -406,6 +414,25 @@ static int process_validateElf32(void *iehdr, size_t size) } +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; +} + + #ifndef NOMMU @@ -435,10 +462,17 @@ static int process_validateElf64(void *iehdr, size_t size) return -ENOEXEC; } for (i = 0; i < ehdr->e_phnum; i++) { - if (phdr->p_type != PT_LOAD) { - if (process_isPtrValid(iehdr, size, ((char *)ehdr) + phdr[i].p_offset, phdr[i].p_filesz) == 0) { + if (process_isPtrValid(iehdr, size, ((char *)ehdr) + phdr[i].p_offset, phdr[i].p_filesz) == 0) { + return -ENOEXEC; + } + + if (phdr->p_type == PT_INTERP) { + if ((phdr->p_filesz == 0) || (((const char *)ehdr + phdr->p_offset)[phdr->p_filesz - 1] != '\0')) { return -ENOEXEC; } + } + + if (phdr->p_type != PT_LOAD) { continue; } @@ -481,57 +515,118 @@ 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, void **entry, void **baseAddr, struct auxInfo **auxData) { void *vaddr; size_t memsz, filesz; Elf32_Ehdr *ehdr = iehdr; Elf32_Phdr *phdr; Elf32_Shdr *shdr, *shstrshdr; - unsigned i, prot, flags, misalign; + unsigned i, prot, flags, misalign, err; off_t offs; char *snameTab; + Elf32_Addr loadLow, loadHigh, loadOff = 0; + size_t loadSize; if (process_validateElf32(iehdr, size) < 0) { return -ENOEXEC; } - shdr = (void *)((char *)ehdr + ehdr->e_shoff); - shstrshdr = shdr + ehdr->e_shstrndx; - snameTab = (char *)ehdr + shstrshdr->sh_offset; - /* Find .tdata and .tbss sections */ - for (i = 0; i < ehdr->e_shnum; i++, shdr++) { - if (hal_strcmp(&snameTab[shdr->sh_name], ".tdata") == 0) { - tls->tls_base = (ptr_t)shdr->sh_addr; - tls->tdata_sz += shdr->sh_size; + *entry = (void *)ehdr->e_entry; + + /* Interpreter can be loaded anywhere in the memory and the memory map is already populated by the main executable.*/ + if (baseAddr != NULL) { + /* 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, phdr = (void *)ehdr + ehdr->e_phoff; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_LOAD) { + loadLow = min(loadLow, phdr->p_vaddr); + loadHigh = max(loadHigh, phdr->p_vaddr + phdr->p_memsz); + } } - else if (hal_strcmp(&snameTab[shdr->sh_name], ".tbss") == 0) { - *tbssAddr = (ptr_t)shdr->sh_addr; - tls->tbss_sz += shdr->sh_size; + + /* No loadable section found. */ + if (loadHigh < loadLow) { + return -ENOEXEC; } - else if (hal_strcmp(&snameTab[shdr->sh_name], "armtls") == 0) { - tls->arm_m_tls = (ptr_t)shdr->sh_addr; + /* Find a place in vm where interpreter will fit. */ + loadSize = round_page(loadHigh - loadLow); + /* NOTE: _map_find would be better but its not exposed */ + *baseAddr = vm_mmap(map, (void *)(ptr_t)loadLow, NULL, loadSize, PROT_USER, NULL, -1, MAP_NONE); + if ((*baseAddr) == NULL) { + return -ENOMEM; + } + err = vm_munmap(map, *baseAddr, loadSize); + if (err < 0) { + return err; + } + loadOff = ((Elf32_Addr)(*baseAddr) - loadLow); + *entry = (char *)(*entry) + loadOff; + char dupa[32]; + lib_sprintf(dupa, "ENTRY %p %x\n", *entry, loadOff); + hal_consolePrint(ATTR_BOLD, dupa); + } + + if (auxData != NULL) { + (*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)++; + } + + if ((tls != NULL) && (tbssAddr != NULL)) { + shdr = (void *)((char *)ehdr + ehdr->e_shoff); + shstrshdr = shdr + ehdr->e_shstrndx; + snameTab = (char *)ehdr + shstrshdr->sh_offset; + /* Find .tdata and .tbss sections */ + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + if (hal_strcmp(&snameTab[shdr->sh_name], ".tdata") == 0) { + tls->tls_base = (ptr_t)shdr->sh_addr; + tls->tdata_sz += shdr->sh_size; + } + else if (hal_strcmp(&snameTab[shdr->sh_name], ".tbss") == 0) { + *tbssAddr = (ptr_t)shdr->sh_addr; + tls->tbss_sz += shdr->sh_size; + } + else if (hal_strcmp(&snameTab[shdr->sh_name], "armtls") == 0) { + tls->arm_m_tls = (ptr_t)shdr->sh_addr; + } } } for (i = 0, phdr = (void *)ehdr + ehdr->e_phoff; i < ehdr->e_phnum; i++, phdr++) { if ((phdr->p_type == PT_GNU_STACK) && (phdr->p_memsz != 0)) { - *ustacksz = round_page(phdr->p_memsz); + if (ustacksz != NULL) { + *ustacksz = round_page(phdr->p_memsz); + } } - if ((phdr->p_type != PT_LOAD) || (phdr->p_vaddr == 0)) { + if (phdr->p_type == PT_PHDR) { + if (auxData != NULL) { + (*auxData)->a_type = AT_PHDR; + (*auxData)->a_v = (u64)phdr->p_vaddr; + (*auxData)++; + } + } + if ((phdr->p_type != PT_LOAD) || ((phdr->p_vaddr + loadOff) == 0)) { continue; } - vaddr = (void *)((ptr_t)(phdr->p_vaddr & ~(phdr->p_align - 1))); + vaddr = (void *)(((ptr_t)(phdr->p_vaddr & ~(phdr->p_align - 1))) + loadOff); offs = phdr->p_offset & ~(phdr->p_align - 1); misalign = phdr->p_offset & (phdr->p_align - 1); filesz = phdr->p_filesz ? (phdr->p_filesz + misalign) : 0; memsz = phdr->p_memsz + misalign; prot = PROT_USER; - flags = MAP_NONE; + flags = MAP_FIXED; if ((phdr->p_flags & PF_R) != 0) { prot |= PROT_READ; @@ -561,60 +656,194 @@ int process_load32(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ hal_memset(vaddr + filesz, 0, round_page((ptr_t)vaddr + memsz) - ((ptr_t)vaddr + filesz)); } } + return EOK; } +const char *process_getInterpreterPath32(const Elf32_Ehdr *ehdr) +{ + const Elf32_Phdr *phdrs = (const void *)ehdr + ehdr->e_phoff; + Elf32_Half i; + + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdrs[i].p_type == PT_INTERP) { + return (const char *)ehdr + phdrs[i].p_offset; + } + } + + return NULL; +} + + +/* *interpreterEntry is changed only if interpreter is found and loaded properly. */ +int process_loadInterpreter32(vm_map_t *map, const void *iehdr, void **interpreterEntry, struct auxInfo **auxData) +{ + const Elf32_Ehdr *ehdr = iehdr; + int err; + const char *interpreterPath; + oid_t interpOid; + vm_object_t *interpO; + void *interpIehdr; + size_t interpSize; + void *baseAddr; + + /* Assume ehdr is already validated. */ + interpreterPath = process_getInterpreterPath32(ehdr); + 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, 0, MAP_NONE); + if (interpIehdr == NULL) { + vm_objectPut(interpO); + return -ENOMEM; + } + err = process_load32(map, interpO, 0, interpIehdr, interpSize, NULL, NULL, NULL, interpreterEntry, &baseAddr, NULL); + if (err < 0) { + vm_munmap(process_common.kmap, interpIehdr, interpSize); + vm_objectPut(interpO); + return err; + } + err = vm_munmap(process_common.kmap, interpIehdr, interpSize); + if (err < 0) { + vm_objectPut(interpO); + return err; + } + err = 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_load64(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_load64(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, void **entry, void **baseAddr, struct auxInfo **auxData) { void *vaddr; size_t memsz, filesz; Elf64_Ehdr *ehdr = iehdr; Elf64_Phdr *phdr; Elf64_Shdr *shdr, *shstrshdr; - unsigned i, prot, flags, misalign; + unsigned i, prot, flags, misalign, err; off_t offs; char *snameTab; + Elf64_Addr loadLow, loadHigh, loadOff = 0; + size_t loadSize; if (process_validateElf64(iehdr, size) < 0) { return -ENOEXEC; } - shdr = (void *)((char *)ehdr + ehdr->e_shoff); - shstrshdr = shdr + ehdr->e_shstrndx; - snameTab = (char *)ehdr + shstrshdr->sh_offset; - /* Find .tdata and .tbss sections */ - for (i = 0; i < ehdr->e_shnum; i++, shdr++) { - if (hal_strcmp(&snameTab[shdr->sh_name], ".tdata") == 0) { - tls->tls_base = (ptr_t)shdr->sh_addr; - tls->tdata_sz += shdr->sh_size; + *entry = (void *)(ptr_t)ehdr->e_entry; + + /* Interpreter can be loaded anywhere in the memory and the memory map is already populated by the main executable.*/ + if (baseAddr != NULL) { + /* Dynamic linker needs to be mapped in a contignous part of memory. */ + /* Get lowest and highest loaded address to to obtain total size. */ + loadLow = ~(Elf64_Addr)0; + loadHigh = 0; + for (i = 0, phdr = (void *)ehdr + ehdr->e_phoff; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_LOAD) { + loadLow = min(loadLow, phdr->p_vaddr); + loadHigh = max(loadHigh, phdr->p_vaddr + phdr->p_memsz); + } } - else if (hal_strcmp(&snameTab[shdr->sh_name], ".tbss") == 0) { - *tbssAddr = (ptr_t)shdr->sh_addr; - tls->tbss_sz += shdr->sh_size; + + /* No loadable section found. */ + if (loadHigh < loadLow) { + return -ENOEXEC; } - else if (hal_strcmp(&snameTab[shdr->sh_name], "armtls") == 0) { - tls->arm_m_tls = (ptr_t)shdr->sh_addr; + /* Find a place in vm where interpreter will fit. */ + loadSize = round_page(loadHigh - loadLow); + /* NOTE: _map_find would be better but its not exposed */ + *baseAddr = vm_mmap(map, (void *)(ptr_t)loadLow, NULL, loadSize, PROT_USER, NULL, -1, MAP_NONE); + if ((*baseAddr) == NULL) { + return -ENOMEM; + } + err = vm_munmap(map, *baseAddr, loadSize); + if (err < 0) { + return err; + } + loadOff = ((Elf64_Addr)(ptr_t)(*baseAddr) - loadLow); + *entry = (char *)(*entry) + loadOff; + char dupa[32]; + lib_sprintf(dupa, "ENTRY %p %x\n", *entry, loadOff); + hal_consolePrint(ATTR_BOLD, dupa); + } + + if (auxData != NULL) { + (*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)++; + } + + if ((tls != NULL) && (tbssAddr != NULL)) { + shdr = (void *)((char *)ehdr + ehdr->e_shoff); + shstrshdr = shdr + ehdr->e_shstrndx; + snameTab = (char *)ehdr + shstrshdr->sh_offset; + /* Find .tdata and .tbss sections */ + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + if (hal_strcmp(&snameTab[shdr->sh_name], ".tdata") == 0) { + tls->tls_base = (ptr_t)shdr->sh_addr; + tls->tdata_sz += shdr->sh_size; + } + else if (hal_strcmp(&snameTab[shdr->sh_name], ".tbss") == 0) { + *tbssAddr = (ptr_t)shdr->sh_addr; + tls->tbss_sz += shdr->sh_size; + } + else if (hal_strcmp(&snameTab[shdr->sh_name], "armtls") == 0) { + tls->arm_m_tls = (ptr_t)shdr->sh_addr; + } } } for (i = 0, phdr = (void *)ehdr + ehdr->e_phoff; i < ehdr->e_phnum; i++, phdr++) { if ((phdr->p_type == PT_GNU_STACK) && (phdr->p_memsz != 0)) { - *ustacksz = round_page(phdr->p_memsz); + if (ustacksz != NULL) { + *ustacksz = round_page(phdr->p_memsz); + } } - if ((phdr->p_type != PT_LOAD) || (phdr->p_vaddr == 0)) { + if (phdr->p_type == PT_PHDR) { + if (auxData != NULL) { + (*auxData)->a_type = AT_PHDR; + (*auxData)->a_v = (u64)phdr->p_vaddr; + (*auxData)++; + } + } + if ((phdr->p_type != PT_LOAD) || ((phdr->p_vaddr + loadOff) == 0)) { continue; } - vaddr = (void *)((ptr_t)(phdr->p_vaddr & ~((ptr_t)phdr->p_align - 1))); + vaddr = (void *)(((ptr_t)(phdr->p_vaddr & ~(phdr->p_align - 1))) + (ptr_t)loadOff); offs = phdr->p_offset & ~(phdr->p_align - 1); misalign = phdr->p_offset & (phdr->p_align - 1); filesz = phdr->p_filesz ? (phdr->p_filesz + misalign) : 0; memsz = phdr->p_memsz + misalign; prot = PROT_USER; - flags = MAP_NONE; + flags = MAP_FIXED; if ((phdr->p_flags & PF_R) != 0) { prot |= PROT_READ; @@ -644,17 +873,91 @@ int process_load64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ hal_memset(vaddr + filesz, 0, round_page((ptr_t)vaddr + memsz) - ((ptr_t)vaddr + filesz)); } } + + return EOK; +} + + +const char *process_getInterpreterPath64(const Elf64_Ehdr *ehdr) +{ + const Elf64_Phdr *phdrs = (const void *)ehdr + ehdr->e_phoff; + Elf64_Half i; + + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdrs[i].p_type == PT_INTERP) { + return (const char *)ehdr + phdrs[i].p_offset; + } + } + + return NULL; +} + + +/* *interpreterEntry is changed only if interpreter is found and loaded properly. */ +int process_loadInterpreter64(vm_map_t *map, const void *iehdr, void **interpreterEntry, struct auxInfo **auxData) +{ + const Elf64_Ehdr *ehdr = iehdr; + int err; + const char *interpreterPath; + oid_t interpOid; + vm_object_t *interpO; + void *interpIehdr; + size_t interpSize; + void *baseAddr; + + /* Assume ehdr is already validated. */ + interpreterPath = process_getInterpreterPath64(ehdr); + 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, 0, MAP_NONE); + if (interpIehdr == NULL) { + vm_objectPut(interpO); + return -ENOMEM; + } + err = process_load64(map, interpO, 0, interpIehdr, interpSize, NULL, NULL, NULL, interpreterEntry, &baseAddr, NULL); + if (err < 0) { + vm_munmap(process_common.kmap, interpIehdr, interpSize); + vm_objectPut(interpO); + return err; + } + err = vm_munmap(process_common.kmap, interpIehdr, interpSize); + if (err < 0) { + vm_objectPut(interpO); + return err; + } + err = 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) +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; @@ -671,19 +974,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: - *entry = (void *)(ptr_t)((Elf32_Ehdr *)ehdr)->e_entry; - err = process_load32(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr); + switch (ehdr->e_ident[EI_CLASS]) { + case ELFCLASS32: + err = process_load32(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr, entry, NULL, auxData); + if (err == 0) { + err = process_loadInterpreter32(map, ehdr, entry, auxData); + } break; - /* 64-bit binary */ - case 2: - *entry = (void *)(ptr_t)ehdr->e_entry; - err = process_load64(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr); + case ELFCLASS64: + err = process_load64(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr, entry, NULL, auxData); + if (err == 0) { + err = process_loadInterpreter64(map, ehdr, entry, auxData); + } break; - default: err = -ENOEXEC; } @@ -737,7 +1041,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; @@ -754,6 +1058,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; } @@ -1042,6 +1348,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; @@ -1071,10 +1379,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); From 544964372b2dc3f66a6635dcdb7939919b001340 Mon Sep 17 00:00:00 2001 From: Hubert Badocha Date: Fri, 16 Aug 2024 14:28:06 +0200 Subject: [PATCH 3/3] process/load: Create TLS on interpreted programs JIRA: RTOS-664 --- include/syscalls.h | 4 ++-- proc/process.c | 43 +++++++++++++++++++++++++++++-------------- proc/process.h | 3 ++- proc/threads.c | 4 ++-- syscalls.c | 4 ++-- 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/include/syscalls.h b/include/syscalls.h index 174e25cb6..1abe5bd56 100644 --- a/include/syscalls.h +++ b/include/syscalls.h @@ -29,8 +29,8 @@ ID(getpid) \ ID(getppid) \ ID(gettid) \ - ID(beginthreadex) \ - ID(endthread) \ + ID(beginthreadexsvc) \ + ID(endthreadsvc) \ ID(nsleep) \ ID(phMutexCreate) \ ID(phMutexLock) \ diff --git a/proc/process.c b/proc/process.c index bb4ee0426..a6b12d3d0 100644 --- a/proc/process.c +++ b/proc/process.c @@ -215,6 +215,8 @@ int proc_start(void (*initthr)(void *), void *arg, const char *path) process->lazy = 1; #endif + process->hasInterpreter = 0; + proc_changeMap(process, NULL, NULL, NULL); /* Initialize resources tree for mutex and cond handles */ @@ -315,6 +317,7 @@ static void process_tlsAssign(hal_tls_t *process_tls, hal_tls_t *tls, ptr_t tbss } process_tls->tdata_sz = tls->tdata_sz; process_tls->tbss_sz = tls->tbss_sz; + // TODO: Add missing space for linkers additional data. process_tls->tls_sz = (tls->tbss_sz + tls->tdata_sz + sizeof(void *) + sizeof(void *) - 1) & ~(sizeof(void *) - 1); process_tls->arm_m_tls = tls->arm_m_tls; } @@ -564,9 +567,6 @@ int process_load32(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ } loadOff = ((Elf32_Addr)(*baseAddr) - loadLow); *entry = (char *)(*entry) + loadOff; - char dupa[32]; - lib_sprintf(dupa, "ENTRY %p %x\n", *entry, loadOff); - hal_consolePrint(ATTR_BOLD, dupa); } if (auxData != NULL) { @@ -676,7 +676,7 @@ const char *process_getInterpreterPath32(const Elf32_Ehdr *ehdr) /* *interpreterEntry is changed only if interpreter is found and loaded properly. */ -int process_loadInterpreter32(vm_map_t *map, const void *iehdr, void **interpreterEntry, struct auxInfo **auxData) +int process_loadInterpreter32(vm_map_t *map, const void *iehdr, void **interpreterEntry, struct auxInfo **auxData, int *hasInterp) { const Elf32_Ehdr *ehdr = iehdr; int err; @@ -725,6 +725,8 @@ int process_loadInterpreter32(vm_map_t *map, const void *iehdr, void **interpret return err; } + *hasInterp = 1; + (*auxData)->a_type = AT_BASE; (*auxData)->a_v = (u64)(ptr_t)baseAddr; (*auxData)++; @@ -781,9 +783,6 @@ int process_load64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ } loadOff = ((Elf64_Addr)(ptr_t)(*baseAddr) - loadLow); *entry = (char *)(*entry) + loadOff; - char dupa[32]; - lib_sprintf(dupa, "ENTRY %p %x\n", *entry, loadOff); - hal_consolePrint(ATTR_BOLD, dupa); } if (auxData != NULL) { @@ -894,7 +893,7 @@ const char *process_getInterpreterPath64(const Elf64_Ehdr *ehdr) /* *interpreterEntry is changed only if interpreter is found and loaded properly. */ -int process_loadInterpreter64(vm_map_t *map, const void *iehdr, void **interpreterEntry, struct auxInfo **auxData) +int process_loadInterpreter64(vm_map_t *map, const void *iehdr, void **interpreterEntry, struct auxInfo **auxData, int *hasInterp) { const Elf64_Ehdr *ehdr = iehdr; int err; @@ -943,6 +942,8 @@ int process_loadInterpreter64(vm_map_t *map, const void *iehdr, void **interpret return err; } + *hasInterp = 1; + (*auxData)->a_type = AT_BASE; (*auxData)->a_v = (u64)(ptr_t)baseAddr; (*auxData)++; @@ -978,14 +979,14 @@ int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, vo case ELFCLASS32: err = process_load32(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr, entry, NULL, auxData); if (err == 0) { - err = process_loadInterpreter32(map, ehdr, entry, auxData); + err = process_loadInterpreter32(map, ehdr, entry, auxData, &process->hasInterpreter); } break; case ELFCLASS64: err = process_load64(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr, entry, NULL, auxData); if (err == 0) { - err = process_loadInterpreter64(map, ehdr, entry, auxData); + err = process_loadInterpreter64(map, ehdr, entry, auxData, &process->hasInterpreter); } break; default: @@ -1402,8 +1403,8 @@ static void process_exec(thread_t *current, process_spawn_t *spawn) hal_spinlockClear(&spawn->sl, &sc); } - if ((err == EOK) && (current->process->tls.tls_base != NULL)) { - err = process_tlsInit(¤t->tls, ¤t->process->tls, current->process->mapp); + if ((err == EOK) && ((current->process->tls.tls_base != NULL) || (current->process->hasInterpreter != 0))) { + err = process_tlsInit(¤t->tls, ¤t->process->tls, current->process->mapp, current->process->hasInterpreter); } if (err != 0) { @@ -2006,17 +2007,31 @@ int _process_init(vm_map_t *kmap, vm_object_t *kernel) } -int process_tlsInit(hal_tls_t *dest, hal_tls_t *source, vm_map_t *map) +int process_tlsInit(hal_tls_t *dest, hal_tls_t *source, vm_map_t *map, int hasInterpreter) { int err; + /* FIXME: Hack to add support for dynamic binaries. */ + /* Binary may not have any TLS section albeit loaded libraries may use it. */ + /* RTLD relies on having enough space for static TLS sections of shared libraries. */ + /* Thus free space for it is added. */ + size_t freeSpaceBefore, freeSpaceAfter; + if (hasInterpreter == 0) { + freeSpaceAfter = 0; + freeSpaceBefore = 0; + } + else { + freeSpaceAfter = 64; + freeSpaceBefore = SIZE_PAGE / 2; + } dest->tdata_sz = source->tdata_sz; dest->tbss_sz = source->tbss_sz; - dest->tls_sz = round_page(source->tls_sz); + dest->tls_sz = round_page(source->tls_sz + freeSpaceBefore + freeSpaceAfter); dest->arm_m_tls = source->arm_m_tls; dest->tls_base = (ptr_t)vm_mmap(map, NULL, NULL, dest->tls_sz, PROT_READ | PROT_WRITE | PROT_USER, NULL, 0, MAP_NONE); if (dest->tls_base != NULL) { + dest->tls_base += freeSpaceBefore; hal_memcpy((void *)dest->tls_base, (void *)source->tls_base, dest->tdata_sz); hal_memset((char *)dest->tls_base + dest->tdata_sz, 0, dest->tbss_sz); /* At the end of TLS there must be a pointer to itself */ diff --git a/proc/process.h b/proc/process.h index c0c881ae3..46a50505d 100644 --- a/proc/process.h +++ b/proc/process.h @@ -70,6 +70,7 @@ typedef struct _process_t { void *got; hal_tls_t tls; + int hasInterpreter; } process_t; @@ -130,7 +131,7 @@ extern int _process_init(vm_map_t *kmap, vm_object_t *kernel); extern void process_dumpException(unsigned int n, exc_context_t *exc); -extern int process_tlsInit(hal_tls_t *dest, hal_tls_t *source, vm_map_t *map); +extern int process_tlsInit(hal_tls_t *dest, hal_tls_t *source, vm_map_t *map, int hasInterpreter); extern int process_tlsDestroy(hal_tls_t *tls, vm_map_t *map); diff --git a/proc/threads.c b/proc/threads.c index 46044f182..3042f24bb 100644 --- a/proc/threads.c +++ b/proc/threads.c @@ -787,8 +787,8 @@ int proc_threadCreate(process_t *process, void (*start)(void *), int *id, unsign return -ENOMEM; } - if (process != NULL && (process->tls.tdata_sz != 0 || process->tls.tbss_sz != 0)) { - err = process_tlsInit(&t->tls, &process->tls, process->mapp); + if ((process != NULL) && ((process->tls.tls_base != NULL) || (process->hasInterpreter != 0))) { + err = process_tlsInit(&t->tls, &process->tls, process->mapp, process->hasInterpreter); if (err != EOK) { lib_idtreeRemove(&threads_common.id, &t->idlinkage); vm_kfree(t->kstack); diff --git a/syscalls.c b/syscalls.c index 9a41c7f1f..afe87dc52 100644 --- a/syscalls.c +++ b/syscalls.c @@ -287,7 +287,7 @@ int syscalls_gettid(void *ustack) } -int syscalls_beginthreadex(void *ustack) +int syscalls_beginthreadexsvc(void *ustack) { process_t *proc = proc_current()->process; void (*start)(void *); @@ -319,7 +319,7 @@ int syscalls_beginthreadex(void *ustack) } -int syscalls_endthread(void *ustack) +int syscalls_endthreadsvc(void *ustack) { proc_threadEnd(); return EOK;