#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "__init.h" #include "__heap.h" #include "__fio.h" /* maximum of 32 handles can be received sent as arguments */ #define MAX_HANDLE_ARGS 32 /* maximum size of bootstrap message that can be received */ #define MAX_MSG_SIZE 0x2000 #if 1 #define dbg_log(...) #else static void dbg_log(const char *format, ...) { char buf[1024]; va_list arg; va_start(arg, format); int r = vsnprintf(buf, sizeof buf, format, arg); va_end(arg); mx_console_write(buf, r); } #endif #ifdef DYLD_PARENT_IMAGE extern mx_vaddr_t dyld_load_exec(int argc, const char **argv); #endif static const char **environ = NULL; static char arg_msg_buf[MAX_MSG_SIZE]; static mx_bootstrap_handle_t arg_handles[MAX_HANDLE_ARGS]; const char **__crt_environ() { return environ; } extern int main(int, const char **); extern void __crt_run_atexit(); static void extract_arg_handles(mx_bootstrap_msg_t *args, mx_handle_t *handles, int hndc, mx_bootstrap_handle_t *out) { uint32_t *hent = (uint32_t *)((char *)args + args->handle_info_off); for (int i = 0; i < hndc; i++) { out[i].info = hent[i]; out[i].handle = handles[i]; } } static void parse_args(mx_bootstrap_msg_t *args, const char **argv, const char **envp, const char **namep) { if (args->args_num > 0) { char *arg_buf = (char *)args + args->args_off; int arg_i = 0; int arg_off = 0; for (int i = 0; ; i++) { if (arg_buf[i] == '\0') { argv[arg_i++] = arg_buf + arg_off; arg_off = i + 1; } if (arg_i == (int)args->args_num) { break; } } } if (args->environ_num > 0) { char *env_buf = (char *)args + args->environ_off; int env_i = 0; int env_off = 0; for (int i = 0; ; i++) { if (env_buf[i] == '\0') { envp[env_i++] = env_buf + env_off; env_off = i + 1; } if (env_i == (int)args->environ_num) { break; } } } if (args->names_num) { char *name_buf = (char *)args + args->names_off; int name_i = 0; int name_off = 0; for (int i = 0; ; i++) { if (name_buf[i] == '\0') { namep[name_i++] = name_buf + name_off; name_off = i + 1; } if (name_i == (int)args->names_num) { break; } } } } static void hang() { while (1) { mx_nanosleep(mx_deadline_after(MX_SEC(1))); } } static void init_stdio_fd(int fd, mx_handle_t *handles, size_t nhandles) { if (handles[0] == MX_NULL_HANDLE) { dbg_log("** photon: FD %d is closed\n", fd); return; } mx_info_basic_t handle0_info; mx_status_t err = mx_object_get_info(handles[0], MX_INFO_HANDLE_BASIC, &handle0_info, sizeof(handle0_info)); if (err != MX_OK) { /* bad handle. TODO report error somehow */ return; } mio_object *obj = NULL; if (handle0_info.type == MX_OBJECT_PIPE) { obj = mio_pipe_create(handles[0]); dbg_log("** photon: FD %d is a pipe\n", fd); } else { /* just assume all other stdio handles implement the file protocol over a tunnel */ obj = mio_file_create(handles[0]); dbg_log("** photon: FD %d is a file\n", fd); } mio_fd_list_alloc_at(mio_global_fd_list(), obj, fd); } static void init_stdio(mx_bootstrap_handle_t *handles, size_t nhandles) { dbg_log("** photon: initialising stdio\n"); bool no_fds = true; int max_fd = 0; for (size_t i = 0; i < nhandles; i++) { uint32_t info = handles[i].info; if (MX_B_HND_TYPE(info) != MX_B_FD) { continue; } no_fds = false; int fd = MX_B_HND_ARG(info); if (fd > max_fd) { max_fd = fd; } } if (no_fds) { dbg_log("** photon: we've been given no FDs\n"); return; } int nfds = max_fd + 1; dbg_log("** photon: we've been given up to %d FDs\n", nfds); /* Supports up to two handles per FD */ mx_handle_t fd_handles[nfds][2]; memset(fd_handles, 0x0, sizeof(mx_handle_t) * 2 * nfds); for (size_t i = 0; i < nhandles; i++) { uint32_t info = handles[i].info; if (MX_B_HND_TYPE(info) != MX_B_FD) { continue; } int fd = MX_B_HND_ARG(info); mx_handle_t *this_fd_handles = fd_handles[fd]; if (this_fd_handles[0] != MX_NULL_HANDLE) { this_fd_handles[1] = handles[i].handle; } else { this_fd_handles[0] = handles[i].handle; } } for (int i = 0; i < nfds; i++) { size_t n = 1; if (fd_handles[i][1] != MX_NULL_HANDLE) { n++; } //dbg_log("** photon: handles for FD %d = { %x, %x }\n", i, fd_handles[i][0], fd_handles[i][1]); init_stdio_fd(i, fd_handles[i], n); } __fio_init(0, 1, 2); dbg_log("** photon: stdio init finished\n"); } static int do_init(mx_handle_t bootstrap, bool enable_fs, bool enable_stdio, int *out_argc, const char ***out_argv) { dbg_log("reading bootstrap message from %x\n", bootstrap); mx_signals_t sig = 0; mx_object_wait(bootstrap, MX_TUNNEL_READABLE, 0, &sig); if (!(sig & MX_TUNNEL_READABLE)) { dbg_log("no bootstrap message!\n"); return -1; } size_t msg_size = 0; size_t nr_handles = 0; mx_handle_t handles[MAX_HANDLE_ARGS + 1]; dbg_log("receiving message from handle %lx\n", bootstrap); mx_status_t err = mx_tunnel_read(bootstrap, arg_msg_buf, MAX_MSG_SIZE, handles, MAX_HANDLE_ARGS, &msg_size, &nr_handles); if (err != MX_OK) { dbg_log("error: cannot read bootstrap message from handle %lx (error %d)\n", bootstrap, err); return -1; } mx_bootstrap_msg_t *msg = (mx_bootstrap_msg_t *)arg_msg_buf; dbg_log("extracting handles\n"); extract_arg_handles(msg, handles, nr_handles, arg_handles); #if 0 arg_handles[nr_handles].handle = bootstrap; arg_handles[nr_handles].info = MX_B_HND(MX_B_TUNNEL_BTSTP, 0); nr_handles++; #endif dbg_log("extracted %zu handles\n", nr_handles); mx_bootstrap_handle_init(arg_handles, nr_handles); const char **argv = NULL, **envp = NULL, **namep = NULL; dbg_log("allocating buffers (%u, %u, %u)\n", msg->args_num, msg->environ_num, msg->names_num); argv = calloc(sizeof *argv, msg->args_num + 1); envp = calloc(sizeof *envp, msg->environ_num + 1); namep = calloc(sizeof *namep, msg->names_num + 1); environ = envp; parse_args(msg, argv, envp, namep); *out_argc = msg->args_num; *out_argv = argv; if (enable_stdio) { init_stdio(arg_handles, nr_handles); } else { __fio_init(-1, -1, -1); } if (!enable_fs) { return 0; } mio_namespace *ns = mio_namespace_create(); dbg_log("received %u names/%zu handles\n", msg->names_num, nr_handles); if (msg->names_num > 0) { for (size_t i = 0; i < nr_handles; i++) { int type = MX_B_HND_TYPE(arg_handles[i].info); int arg = MX_B_HND_ARG(arg_handles[i].info); if (type != MX_B_NS_DIR && type != MX_B_TUNNEL_CWD) { dbg_log(" * wrong type %x\n", type); continue; } const char *path = namep[arg]; dbg_log(" * %s = %x\n", path, arg_handles[i].handle); if (type == MX_B_TUNNEL_CWD) { mio_set_cwd(arg_handles[i].handle, path); } else { mio_namespace_add_entry(ns, path, arg_handles[i].handle); } } } mio_set_global_namespace(ns); return 0; } static int do_run(mx_handle_t bootstrap) { int argc; const char **argv; int err = do_init(bootstrap, true, true, &argc, &argv); if (err != 0) { return err; } return main(argc, argv); } #ifdef DYLD_PARENT_IMAGE static int do_load_and_run(mx_handle_t bootstrap) { int argc; const char **argv; int err = do_init(bootstrap, false, true, &argc, &argv); if (err != 0) { return err; } mx_vaddr_t main_ptr = dyld_load_exec(argc, argv); if (!main_ptr) { return -1; } mx_bootstrap_handle_cleanup(); err = do_init(bootstrap, true, true, &argc, &argv); if (err != 0) { return err; } int(*main_func)(int, const char **) = (int(*)(int, const char **))main_ptr; if (!main_func) { return -1; } return main_func(argc, argv); } #endif int __crt_init(mx_handle_t bootstrap, mx_vaddr_t arg2) { #ifndef DYLD_PARENT_IMAGE int ret = do_run(bootstrap); #else int ret = do_load_and_run(bootstrap); #endif fflush(stdout); fflush(stderr); __crt_run_atexit(); mio_fd_cleanup(); mio_fs_cleanup(); mx_task_kill(mx_bootstrap_handle_get(MX_B_TASK_SELF), ret); /* unreachable */ hang(); return 0; }