/* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "elf.h" #include "exec_reader.h" #include "auxiliary_vector.h" #include "sched.h" #include "proc_struct.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static inline int check_for_addition(unsigned long base, unsigned long offset) { return base <= ULONG_MAX - offset; } static inline int check_for_signed_addition(unsigned long base, long offset) { if (offset < 0) return base >= 1UL - offset + 1UL; else return base <= ULONG_MAX - offset; } /* Detecte le format d'executable et construit un exec_reader a partir de * filename. Retourne un code d'erreur en cas d'erreur. */ static int create_exec_reader(const char *fs_filename, struct exec_reader_struct **exec_reader) { int retval; struct file_struct *file; struct stat_struct statbuf; /* resolution du nom et controle du type du fichier */ if ((retval = resolve(fs_filename, &file)) < 0) goto error; if ((retval = stat_node(file, &statbuf)) < 0) goto error_release; if (statbuf.type != FT_REG) { retval = -EACCES; goto error_release; } if ((retval = node_deny_write_access(file)) < 0) /* verification que le fichier n'est pas ouvert en ecriture */ goto error_release; /* detection du type d'executable */ if ((retval = is_elf(file)) < 0) goto error_release_write_access; if (retval) { if ((retval = alloc_elf_exec_reader(file, exec_reader)) < 0) goto error_release_write_access; return 0; } retval = -ENOEXEC; error_release_write_access: node_release_write_access(file); error_release: node_release(file); error: return retval; } static unsigned int get_str_table_size(const struct string_table_struct *string_table) { return ALIGN(string_table->size); } static unsigned int get_auxv_entry_size() { return 2 * sizeof (unsigned long); } static unsigned int get_auxv_size(const struct auxiliary_vector_struct *auxiliary_vector) { unsigned int size; size = 0; size += get_auxv_entry_size(); if (auxiliary_vector->contents.program_header_table) size += get_auxv_entry_size(); if (auxiliary_vector->contents.program_header_entry_size) size += get_auxv_entry_size(); if (auxiliary_vector->contents.program_header_entry_number) size += get_auxv_entry_size(); if (auxiliary_vector->contents.page_size) size += get_auxv_entry_size(); if (auxiliary_vector->contents.interpreter_base_address) size += get_auxv_entry_size(); if (auxiliary_vector->contents.program_entry_point) size += get_auxv_entry_size(); if (auxiliary_vector->contents.not_elf) size += get_auxv_entry_size(); if (auxiliary_vector->contents.vdso_entry_point) size += get_auxv_entry_size(); if (auxiliary_vector->contents.vdso_elf_header) size += get_auxv_entry_size(); return size; } static unsigned int get_str_array_size(unsigned int count) { return (count + 1) * sizeof (long); } static unsigned int get_int_size() { return sizeof (long); } /* Retourne la taille initiale minimale que devra avoir la pile utilisateur. * La valeur retournee n'est jamais nulle. */ static unsigned int get_user_stack_size(const struct string_table_struct *environ, const struct auxiliary_vector_struct *auxiliary_vector) { unsigned int size; size = 0; /* environnement */ size += get_str_table_size(environ); /* arguments */ size += get_str_table_size(¤t->cmdline); /* auxv[] */ size += get_auxv_size(auxiliary_vector); /* envp[] */ size += get_str_array_size(environ->count); /* argv[] */ size += get_str_array_size(current->cmdline.count); /* argc */ size += get_int_size(); return size; } static void push_str_table(void **fs_user_stack_top, const struct string_table_struct *string_table) { if (string_table->buf) { *fs_user_stack_top -= ALIGN(string_table->size); memcpy_tofs(*fs_user_stack_top, string_table->buf, string_table->size); memset_tofs(*fs_user_stack_top + string_table->size, 0, ALIGN(string_table->size) - string_table->size); } } static void push_auxv_entry(void **fs_user_stack_top, int id, unsigned long val) { *fs_user_stack_top -= sizeof (long); put_fs_long(val, *fs_user_stack_top); *fs_user_stack_top -= sizeof (long); put_fs_long(id, *fs_user_stack_top); } static void push_auxv(void **fs_user_stack_top, const struct auxiliary_vector_struct *auxiliary_vector) { push_auxv_entry(fs_user_stack_top, AT_NULL, 0); if (auxiliary_vector->contents.program_header_table) push_auxv_entry(fs_user_stack_top, AT_PHDR, auxiliary_vector->program_header_table); if (auxiliary_vector->contents.program_header_entry_size) push_auxv_entry(fs_user_stack_top, AT_PHENT, auxiliary_vector->program_header_entry_size); if (auxiliary_vector->contents.program_header_entry_number) push_auxv_entry(fs_user_stack_top, AT_PHNUM, auxiliary_vector->program_header_entry_number); if (auxiliary_vector->contents.page_size) push_auxv_entry(fs_user_stack_top, AT_PAGESZ, auxiliary_vector->page_size); if (auxiliary_vector->contents.interpreter_base_address) push_auxv_entry(fs_user_stack_top, AT_BASE, auxiliary_vector->interpreter_base_address); if (auxiliary_vector->contents.program_entry_point) push_auxv_entry(fs_user_stack_top, AT_ENTRY, auxiliary_vector->program_entry_point); if (auxiliary_vector->contents.not_elf) push_auxv_entry(fs_user_stack_top, AT_NOTELF, 0); if (auxiliary_vector->contents.vdso_entry_point) push_auxv_entry(fs_user_stack_top, AT_SYSINFO, auxiliary_vector->vdso_entry_point); if (auxiliary_vector->contents.vdso_elf_header) push_auxv_entry(fs_user_stack_top, AT_SYSINFO_EHDR, auxiliary_vector->vdso_elf_header); } static void push_str_array(void **fs_user_stack_top, const char *str_table, unsigned int count) { char **fs_str_array; const char *s; unsigned int i; *fs_user_stack_top -= (count + 1) * sizeof (long); fs_str_array = *fs_user_stack_top; s = str_table; for (i = 0; i < count; i++) { put_fs_long((long)s, (long *)&fs_str_array[i]); while (get_fs_byte(s++)); } put_fs_long((long)NULL, (long *)&fs_str_array[count]); } static void push_int(void **fs_user_stack_top, int value) { *fs_user_stack_top -= sizeof (long); put_fs_long(value, *fs_user_stack_top); } /* Ecrit la pile utilisateur initiale. */ static void fill_in_user_stack(void **fs_user_stack_top, const struct string_table_struct *environ, const struct auxiliary_vector_struct *auxiliary_vector) { const char *environ_table, *argument_table; /* environnement */ push_str_table(fs_user_stack_top, environ); environ_table = *fs_user_stack_top; /* arguments */ push_str_table(fs_user_stack_top, ¤t->cmdline); argument_table = *fs_user_stack_top; /* auxv[] */ push_auxv(fs_user_stack_top, auxiliary_vector); /* envp[] */ push_str_array(fs_user_stack_top, environ_table, environ->count); /* argv[] */ push_str_array(fs_user_stack_top, argument_table, current->cmdline.count); /* argc */ push_int(fs_user_stack_top, current->cmdline.count); } /* Detruit et reinitialise le programme execute par le processus courant. Cette * fonction ne retourne pas de code d'erreur. */ static void reset_exec(struct string_table_struct *cmdline) { int intr; disable_intr(intr); current->did_exec = 1; string_table_destroy(¤t->cmdline); current->cmdline = *cmdline; vm_free(¤t->vm); current->brk = 0; restore_intr(intr); } static void close_file_descrs_on_exec(struct file_descr_struct *file_descr_table[]) { struct file_descr_struct **file_descr; for (file_descr = file_descr_table; file_descr < file_descr_table + OPEN_MAX; file_descr++) if (*file_descr && fd_get_close_on_exec(*file_descr)) { fd_close(*file_descr); *file_descr = NULL; } } static void init_auxiliary_vector(struct auxiliary_vector_struct *auxiliary_vector) { auxiliary_vector->contents.program_header_table = 0; auxiliary_vector->contents.program_header_entry_size = 0; auxiliary_vector->contents.program_header_entry_number = 0; auxiliary_vector->contents.page_size = 0; auxiliary_vector->contents.interpreter_base_address = 0; auxiliary_vector->contents.program_entry_point = 0; auxiliary_vector->contents.not_elf = 0; auxiliary_vector->contents.vdso_entry_point = 0; auxiliary_vector->contents.vdso_elf_header = 0; } static int setup_user_stack(unsigned long *user_stack_top, const struct string_table_struct *environ, const struct auxiliary_vector_struct *auxiliary_vector) { unsigned long length; int rv; length = PAGE_ALIGN(get_user_stack_size(environ, auxiliary_vector)); /* jamais nul */ if ((rv = vm_map_anon(¤t->vm, USER_STACK_TOP - length, length, PROT_RDWR, 1, "stack")) < 0) /* pile utilisateur */ return rv; if (!verify_area((void *)(USER_STACK_TOP - length), length, PF_WRITE)) return -EFAULT; *user_stack_top = USER_STACK_TOP; fill_in_user_stack((void **)user_stack_top, environ, auxiliary_vector); return 0; } int exec(const char *fs_filename, char *const fs_argv[], char *const fs_envp[], struct registers_struct *registers, struct interrupted_instruction_struct *interrupted_instruction, int *status) { int retval; struct exec_reader_struct *exec_reader; struct string_table_struct cmdline, environ; unsigned long user_stack_top, entry_point; struct auxiliary_vector_struct auxiliary_vector; /* detection du format d'executable et des erreurs */ if ((retval = create_exec_reader(fs_filename, &exec_reader)) < 0) goto error; if ((retval = string_table_init_from_array(&cmdline, fs_argv)) < 0) goto error_free_exec_reader; if ((retval = string_table_init_from_array(&environ, fs_envp)) < 0) goto error_destroy_cmdline; if (cmdline.size + environ.size > ARG_MAX) { retval = -E2BIG; goto error_destroy_environ; } /* destruction et reinitialisation de certains champs de proc_struct */ /* A partir de ce point, exec() ne peut plus retourner de code d'erreur dans le processus courant. Apres la reinitialisation, en cas d'erreur, un abort() sera genere a partir du code mis dans status. */ reset_exec(&cmdline); close_file_descrs_on_exec(current->file_descr_table); /* mise en place du nouveau programme */ init_auxiliary_vector(&auxiliary_vector); if ((*status = exec_reader->vptr->load(exec_reader, &entry_point, &auxiliary_vector)) < 0) goto fatal_error_destroy_environ; if ((*status = setup_user_stack(&user_stack_top, &environ, &auxiliary_vector)) < 0) goto fatal_error_destroy_environ; if (!current->brk) { *status = -EINVAL; goto fatal_error_destroy_environ; } string_table_destroy(&environ); exec_reader->vptr->free(exec_reader); registers->eax = 0; registers->ebx = 0; registers->ecx = 0; registers->edx = 0; registers->esi = 0; registers->edi = 0; registers->ebp = 0; registers->ds = USER_DATA_SEL; registers->es = USER_DATA_SEL; registers->fs = USER_DATA_SEL; registers->gs = USER_DATA_SEL; interrupted_instruction->eip = entry_point; interrupted_instruction->cs = USER_CODE_SEL; interrupted_instruction->eflags = 0x292; /* AF, SF, IF */ interrupted_instruction->esp = user_stack_top; interrupted_instruction->ss = USER_DATA_SEL; *status = 0; return 0; error_destroy_environ: string_table_destroy(&environ); error_destroy_cmdline: string_table_destroy(&cmdline); error_free_exec_reader: exec_reader->vptr->free(exec_reader); error: return retval; fatal_error_destroy_environ: string_table_destroy(&environ); exec_reader->vptr->free(exec_reader); return 0; } int sbrk(int increment, unsigned long *brk) { unsigned long new_brk; unsigned long old_end_data, new_end_data; int rv; if (increment) { if (!check_for_signed_addition(current->brk, increment)) return -EINVAL; new_brk = current->brk + increment; old_end_data = PAGE_ALIGN(current->brk); if (!check_for_addition(new_brk, PAGE_SIZE - 1)) return -EINVAL; new_end_data = PAGE_ALIGN(new_brk); if (new_end_data < old_end_data) { if ((rv = vm_unmap(¤t->vm, new_end_data, old_end_data - new_end_data)) < 0) return rv; } else if (new_end_data > old_end_data) if ((rv = vm_map_anon(¤t->vm, old_end_data, new_end_data - old_end_data, PROT_RDWR, 0, "heap")) < 0) return rv; current->brk = new_brk; } *brk = current->brk; return 0; }