/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
/* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
/* Copyright (C) 2014 OSCAR lab, Stony Brook University
This file is part of Graphene Library OS.
Graphene Library OS 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.
Graphene Library OS 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 . */
/*
* shim_epoll.c
*
* Implementation of system call "execve".
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int close_cloexec_handle (struct shim_handle_map * map)
{
auto int close_on_exec (struct shim_fd_handle * fd_hdl,
struct shim_handle_map * map, void * arg)
{
if (fd_hdl->flags & FD_CLOEXEC) {
struct shim_handle * hdl = __detach_fd_handle(fd_hdl, NULL, map);
close_handle(hdl);
}
return 0;
}
return walk_handle_map(&close_on_exec, map, NULL);
}
DEFINE_PROFILE_CATAGORY(exec_rtld, exec);
DEFINE_PROFILE_INTERVAL(alloc_new_stack_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(arrange_arguments_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(unmap_executable_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(unmap_loaded_binaries_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(unmap_all_vmas_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(load_new_executable_for_exec, exec_rtld);
static void * old_stack_top, * old_stack, * old_stack_red;
static const char ** new_argp;
static int new_argc;
static elf_auxv_t * new_auxp;
#define REQUIRED_ELF_AUXV 6
int shim_do_execve_rtld (struct shim_handle * hdl, const char ** argv,
const char ** envp)
{
BEGIN_PROFILE_INTERVAL();
struct shim_thread * cur_thread = get_cur_thread();
int ret;
if ((ret = close_cloexec_handle(cur_thread->handle_map)) < 0)
return ret;
SAVE_PROFILE_INTERVAL(close_CLOEXEC_files_for_exec);
void * tcb = malloc(sizeof(__libc_tcb_t));
if (!tcb)
return -ENOMEM;
populate_tls(tcb, false);
debug("set tcb to %p\n", tcb);
put_handle(cur_thread->exec);
get_handle(hdl);
cur_thread->exec = hdl;
old_stack_top = cur_thread->stack_top;
old_stack = cur_thread->stack;
old_stack_red = cur_thread->stack_red;
cur_thread->stack_top = NULL;
cur_thread->stack = NULL;
cur_thread->stack_red = NULL;
initial_envp = NULL;
new_argc = 0;
for (const char ** a = argv ; *a ; a++, new_argc++);
if ((ret = init_stack(argv, envp, &new_argp,
REQUIRED_ELF_AUXV, &new_auxp)) < 0)
return ret;
SAVE_PROFILE_INTERVAL(alloc_new_stack_for_exec);
switch_stack(new_argp);
cur_thread = get_cur_thread();
UPDATE_PROFILE_INTERVAL();
DkVirtualMemoryFree(old_stack, old_stack_top - old_stack);
DkVirtualMemoryFree(old_stack_red, old_stack - old_stack_red);
int flags = VMA_INTERNAL;
bkeep_munmap(old_stack, old_stack_top - old_stack, &flags);
bkeep_munmap(old_stack_red, old_stack - old_stack_red, &flags);
remove_loaded_libraries();
clean_link_map_list();
SAVE_PROFILE_INTERVAL(unmap_loaded_binaries_for_exec);
init_brk();
unmap_all_vmas();
SAVE_PROFILE_INTERVAL(unmap_all_vmas_for_exec);
if ((ret = load_elf_object(cur_thread->exec, NULL, 0)) < 0)
shim_terminate();
load_elf_interp(cur_thread->exec);
SAVE_PROFILE_INTERVAL(load_new_executable_for_exec);
cur_thread->robust_list = NULL;
debug("execve: start execution\n");
execute_elf_object(cur_thread->exec, new_argc, new_argp,
REQUIRED_ELF_AUXV, new_auxp);
return 0;
}
static void * __malloc (size_t size)
{
int flags = MAP_PRIVATE|MAP_ANONYMOUS|VMA_INTERNAL;
size = ALIGN_UP(size);
void * addr = get_unmapped_vma(size, flags);
addr = DkVirtualMemoryAlloc(addr, size, 0, PAL_PROT_READ|PAL_PROT_WRITE);
if (addr)
bkeep_mmap(addr, size, PROT_READ|PROT_WRITE, flags, NULL, 0, NULL);
return addr;
}
#define malloc_method __malloc
#include
DEFINE_PROFILE_CATAGORY(exec, );
DEFINE_PROFILE_INTERVAL(search_and_check_file_for_exec, exec);
DEFINE_PROFILE_INTERVAL(open_file_for_exec, exec);
DEFINE_PROFILE_INTERVAL(close_CLOEXEC_files_for_exec, exec);
static int migrate_execve (struct shim_cp_store * cpstore,
struct shim_process * process,
struct shim_thread * thread, va_list ap)
{
struct shim_handle_map * handle_map = NULL;
int ret;
const char ** envp = va_arg (ap, const char **);
size_t envsize = va_arg (ap, size_t);
BEGIN_PROFILE_INTERVAL();
if ((ret = dup_handle_map(&handle_map, thread->handle_map)) < 0)
return ret;
set_handle_map(thread, handle_map);
if ((ret = close_cloexec_handle(handle_map)) < 0)
return ret;
SAVE_PROFILE_INTERVAL(close_CLOEXEC_files_for_exec);
/* Now we start to migrate bookkeeping for exec.
The data we need to migrate are:
1. cur_threadrent thread
2. cur_threadrent filesystem
3. handle mapping
4. each handle */
BEGIN_MIGRATION_DEF(execve, struct shim_process * proc,
struct shim_thread * thread,
const char ** envp, size_t envsize)
{
store->use_gipc = true;
DEFINE_MIGRATE(process, proc, sizeof(struct shim_process), false);
DEFINE_MIGRATE(all_mounts, NULL, 0, false);
DEFINE_MIGRATE(running_thread, thread, sizeof(struct shim_thread),
false);
DEFINE_MIGRATE(handle_map, thread->handle_map,
sizeof (struct shim_handle_map), true);
DEFINE_MIGRATE(migratable, NULL, 0, false);
DEFINE_MIGRATE(environ, envp, envsize, true);
}
END_MIGRATION_DEF
return START_MIGRATE(cpstore, execve, 0, process, thread, envp, envsize);
}
int shim_do_execve (const char * file, const char ** argv,
const char ** envp)
{
struct shim_thread * cur_thread = get_cur_thread();
struct shim_dentry * dent = NULL;
int ret = 0;
if (!envp)
envp = initial_envp;
BEGIN_PROFILE_INTERVAL();
if ((ret = path_lookupat(NULL, file, LOOKUP_OPEN, &dent)) < 0)
return ret;
struct shim_mount * fs = dent->fs;
get_dentry(dent);
if (!fs->d_ops->open) {
ret = -EACCES;
err:
put_dentry(dent);
return ret;
}
if (fs->d_ops->mode) {
mode_t mode;
if ((ret = fs->d_ops->mode(dent, &mode, 1)) < 0)
goto err;
}
SAVE_PROFILE_INTERVAL(search_and_check_file_for_exec);
struct shim_handle * exec = NULL;
if (!(exec = get_new_handle())) {
ret = -ENOMEM;
goto err;
}
set_handle_fs(exec, fs);
exec->flags = O_RDONLY;
exec->acc_mode = MAY_READ;
ret = fs->d_ops->open(exec, dent, O_RDONLY);
if (qstrempty(&exec->uri)) {
put_handle(exec);
return -EACCES;
}
int sz;
char *path = dentry_get_path(dent, true, &sz);
qstrsetstr(&exec->path, path, sz);
if ((ret = check_elf_object(&exec)) < 0) {
put_handle(exec);
return ret;
}
SAVE_PROFILE_INTERVAL(open_file_for_exec);
int is_last = check_last_thread(cur_thread) == 0;
if (is_last)
return shim_do_execve_rtld(exec, argv, envp);
INC_PROFILE_OCCURENCE(syscall_use_ipc);
#ifdef PROFILE
unsigned long create_time = GET_PROFILE_INTERVAL();
#endif
size_t envsize = allocsize;
void * envptr = NULL;
const char ** empty_argv = NULL;
retry:
envptr = system_malloc(envsize);
if (!envptr)
return -ENOMEM;
ret = populate_user_stack(envptr, envsize, 0, NULL, &empty_argv, &envp);
if (ret == -ENOMEM) {
system_free(envptr, envsize);
envsize += allocsize;
goto retry;
}
lock(cur_thread->lock);
put_handle(cur_thread->exec);
cur_thread->exec = exec;
void * stack = cur_thread->stack;
void * stack_top = cur_thread->stack_top;
void * tcb = cur_thread->tcb;
bool user_tcb = cur_thread->user_tcb;
void * frameptr = cur_thread->frameptr;
cur_thread->stack = NULL;
cur_thread->stack_top = NULL;
cur_thread->frameptr = NULL;
cur_thread->tcb = NULL;
cur_thread->user_tcb = false;
cur_thread->in_vm = false;
unlock(cur_thread->lock);
ret = do_migrate_process(&migrate_execve, exec, argv, cur_thread, envp,
envptr + envsize - (void *) envp);
system_free(envptr, envsize);
lock(cur_thread->lock);
cur_thread->stack = stack;
cur_thread->stack_top = stack_top;
cur_thread->frameptr = frameptr;
cur_thread->tcb = tcb;
cur_thread->user_tcb = user_tcb;
if (ret < 0) {
cur_thread->in_vm = true;
unlock(cur_thread->lock);
return ret;
}
struct shim_handle_map * handle_map = cur_thread->handle_map;
cur_thread->handle_map = NULL;
unlock(cur_thread->lock);
if (handle_map)
put_handle_map(handle_map);
if (cur_thread->dummy)
switch_dummy_thread(cur_thread);
try_process_exit(0);
return 0;
}