/* * \brief Implementation of process creation for Linux * \author Norman Feske * \date 2006-07-06 */ /* * Copyright (C) 2006-2012 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ /* Genode includes */ #include #include #include #include #include #include /* Framework-internal includes */ #include using namespace Genode; Dataspace_capability Process::_dynamic_linker_cap; /** * Argument frame for passing 'execve' paremeters through 'clone' */ struct Execve_args { const char *filename; char *const *argv; char *const *envp; int parent_sd; }; /** * Startup code of the new child process */ static int _exec_child(Execve_args *arg) { lx_dup2(arg->parent_sd, PARENT_SOCKET_HANDLE); return lx_execve(arg->filename, arg->argv, arg->envp); } /** * List of Unix environment variables, initialized by the startup code */ extern char **lx_environ; /** * Read environment variable as string * * If no matching key exists, return an empty string. */ static const char *get_env(const char *key) { Genode::size_t key_len = Genode::strlen(key); for (char **curr = lx_environ; curr && *curr; curr++) if ((Genode::strcmp(*curr, key, key_len) == 0) && (*curr)[key_len] == '=') return (const char *)(*curr + key_len + 1); return ""; } /** * Check for dynamic ELF header */ static bool _check_dynamic_elf(Dataspace_capability elf_ds_cap) { /* attach ELF locally */ addr_t elf_addr; try { elf_addr = env()->rm_session()->attach(elf_ds_cap); } catch (...) { return false; } /* * If attach is called within core, it will return zero because * Linux uses Core_rm_session. */ if (!elf_addr) return false; /* read program header and interpreter */ Elf_binary elf((addr_t)elf_addr); env()->rm_session()->detach((void *)elf_addr); return elf.is_dynamically_linked(); } const char *Process::_priv_pd_args(Parent_capability parent_cap, Dataspace_capability elf_data_ds_cap, const char *name, char *const argv[]) { /* * Serialize calls of this function because it uses the static 'envbuf' and * 'stack' variables. */ static Lock _priv_pd_args_lock; Lock::Guard _lock_guard(_priv_pd_args_lock); /* check for dynamic program header */ if (_check_dynamic_elf(elf_data_ds_cap)) { if (!_dynamic_linker_cap.valid()) { PERR("Dynamically linked file found, but no dynamic linker binary present"); return 0; } elf_data_ds_cap = _dynamic_linker_cap; } /* pass parent capability as environment variable to the child */ enum { ENV_STR_LEN = 256 }; static char envbuf[5][ENV_STR_LEN]; Genode::snprintf(envbuf[1], ENV_STR_LEN, "parent_local_name=%lu", parent_cap.local_name()); Genode::snprintf(envbuf[2], ENV_STR_LEN, "DISPLAY=%s", get_env("DISPLAY")); Genode::snprintf(envbuf[3], ENV_STR_LEN, "HOME=%s", get_env("HOME")); Genode::snprintf(envbuf[4], ENV_STR_LEN, "LD_LIBRARY_PATH=%s", get_env("LD_LIBRARY_PATH")); char *env[] = { &envbuf[0][0], &envbuf[1][0], &envbuf[2][0], &envbuf[3][0], &envbuf[4][0], 0 }; /* determine name of binary to start */ Linux_dataspace_client elf_data_ds(elf_data_ds_cap); Linux_dataspace::Filename fname = elf_data_ds.fname(); fname.buf[sizeof(fname.buf) - 1] = 0; /* prefix name of Linux program (helps killing some zombies) */ char pname_buf[9 + Linux_dataspace::FNAME_LEN]; snprintf(pname_buf, sizeof(pname_buf), "[Genode] %s", name); /* it may happen, that argv is null */ char *argv_buf[2]; if (!argv) { argv_buf[0] = pname_buf; argv_buf[1] = 0; argv = argv_buf; } else ((char **)argv)[0] = pname_buf; /* * We cannot create the new process via 'fork()' because all our used * memory including stack memory is backed by dataspaces, which had been * mapped with the 'MAP_SHARED' flag. Therefore, after being created, the * new process starts using the stack with the same physical memory pages * as used by parent process. This would ultimately lead to stack * corruption. To prevent both processes from concurrently accessing the * same stack, we pause the execution of the parent until the child calls * 'execve'. From then on, the child has its private memory layout. The * desired behaviour is normally provided by 'vfork' but we use the more * modern 'clone' call for this purpose. */ enum { STACK_SIZE = 4096 }; static char stack[STACK_SIZE]; /* initial stack used by the child until calling 'execve' */ /* * Argument frame as passed to 'clone'. Because, we can only pass a single * pointer, all arguments are embedded within the 'execve_args' struct. */ Execve_args arg = { fname.buf, argv, env, parent_cap.dst().socket }; pid_t pid = lx_create_process((int (*)(void *))_exec_child, stack + STACK_SIZE - sizeof(umword_t), &arg); /* * We create a pseudo pd session with the new pd's pid as argument * to enable Core to kill the process when the pd session gets closed. */ snprintf(_priv_pd_argbuf, sizeof(_priv_pd_argbuf), "PID=%d", pid); return _priv_pd_argbuf; } Process::Process(Dataspace_capability elf_data_ds_cap, Ram_session_capability ram_session_cap, Cpu_session_capability cpu_session_cap, Rm_session_capability rm_session_cap, Parent_capability parent_cap, const char *name, char *const argv[]) : _pd(_priv_pd_args(parent_cap, elf_data_ds_cap, name, argv)), _cpu_session_client(Cpu_session_capability()), _rm_session_client(Rm_session_capability()) { } Process::~Process() { }