// Copyright 2016 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "userboot-elf.h" #include "bootfs.h" #include "util.h" #pragma GCC visibility push(hidden) #include #include #include #include #include #include #include #pragma GCC visibility pop #define INTERP_PREFIX "lib/" static zx_vaddr_t load(zx_handle_t log, const char* what, zx_handle_t vmar, zx_handle_t vmo, uintptr_t* interp_off, size_t* interp_len, zx_handle_t* segments_vmar, size_t* stack_size, bool close_vmo, bool return_entry) { elf_load_header_t header; uintptr_t phoff; zx_status_t status = elf_load_prepare(vmo, NULL, 0, &header, &phoff); check(log, status, "elf_load_prepare failed"); elf_phdr_t phdrs[header.e_phnum]; status = elf_load_read_phdrs(vmo, phdrs, phoff, header.e_phnum); check(log, status, "elf_load_read_phdrs failed"); if (interp_off != NULL && elf_load_find_interp(phdrs, header.e_phnum, interp_off, interp_len)) return 0; if (stack_size != NULL) { for (size_t i = 0; i < header.e_phnum; ++i) { if (phdrs[i].p_type == PT_GNU_STACK && phdrs[i].p_memsz > 0) *stack_size = phdrs[i].p_memsz; } } zx_vaddr_t base, entry; status = elf_load_map_segments(vmar, &header, phdrs, vmo, segments_vmar, &base, &entry); check(log, status, "elf_load_map_segments failed"); if (close_vmo) zx_handle_close(vmo); printl(log, "userboot: loaded %s at %p, entry point %p\n", what, (void*)base, (void*)entry); return return_entry ? entry : base; } zx_vaddr_t elf_load_vmo(zx_handle_t log, zx_handle_t vmar, zx_handle_t vmo) { return load(log, "vDSO", vmar, vmo, NULL, NULL, NULL, NULL, false, false); } enum loader_bootstrap_handle_index { BOOTSTRAP_EXEC_VMO, BOOTSTRAP_LOGGER, BOOTSTRAP_PROC, BOOTSTRAP_ROOT_VMAR, BOOTSTRAP_SEGMENTS_VMAR, BOOTSTRAP_THREAD, BOOTSTRAP_LOADER_SVC, BOOTSTRAP_HANDLES }; #define LOADER_BOOTSTRAP_ENVIRON "LD_DEBUG=1" #define LOADER_BOOTSTRAP_ENVIRON_NUM 1 struct loader_bootstrap_message { zx_proc_args_t header; uint32_t handle_info[BOOTSTRAP_HANDLES]; char env[sizeof(LOADER_BOOTSTRAP_ENVIRON)]; }; static void stuff_loader_bootstrap(zx_handle_t log, zx_handle_t proc, zx_handle_t root_vmar, zx_handle_t thread, zx_handle_t to_child, zx_handle_t segments_vmar, zx_handle_t vmo, zx_handle_t* loader_svc) { struct loader_bootstrap_message msg = { .header = { .protocol = ZX_PROCARGS_PROTOCOL, .version = ZX_PROCARGS_VERSION, .handle_info_off = offsetof(struct loader_bootstrap_message, handle_info), .environ_num = LOADER_BOOTSTRAP_ENVIRON_NUM, .environ_off = offsetof(struct loader_bootstrap_message, env), }, .handle_info = { [BOOTSTRAP_EXEC_VMO] = PA_HND(PA_VMO_EXECUTABLE, 0), [BOOTSTRAP_LOGGER] = PA_HND(PA_FDIO_LOGGER, 0), [BOOTSTRAP_PROC] = PA_HND(PA_PROC_SELF, 0), [BOOTSTRAP_ROOT_VMAR] = PA_HND(PA_VMAR_ROOT, 0), [BOOTSTRAP_SEGMENTS_VMAR] = PA_HND(PA_VMAR_LOADED, 0), [BOOTSTRAP_THREAD] = PA_HND(PA_THREAD_SELF, 0), [BOOTSTRAP_LOADER_SVC] = PA_HND(PA_LDSVC_LOADER, 0), }, .env = LOADER_BOOTSTRAP_ENVIRON, }; zx_handle_t handles[] = { [BOOTSTRAP_EXEC_VMO] = vmo, [BOOTSTRAP_LOGGER] = ZX_HANDLE_INVALID, [BOOTSTRAP_PROC] = ZX_HANDLE_INVALID, [BOOTSTRAP_ROOT_VMAR] = ZX_HANDLE_INVALID, [BOOTSTRAP_SEGMENTS_VMAR] = segments_vmar, [BOOTSTRAP_THREAD] = ZX_HANDLE_INVALID, [BOOTSTRAP_LOADER_SVC] = ZX_HANDLE_INVALID, }; check(log, zx_handle_duplicate(log, ZX_RIGHT_SAME_RIGHTS, &handles[BOOTSTRAP_LOGGER]), "zx_handle_duplicate failed"); check(log, zx_handle_duplicate(proc, ZX_RIGHT_SAME_RIGHTS, &handles[BOOTSTRAP_PROC]), "zx_handle_duplicate failed"); check(log, zx_handle_duplicate(root_vmar, ZX_RIGHT_SAME_RIGHTS, &handles[BOOTSTRAP_ROOT_VMAR]), "zx_handle_duplicate failed"); check(log, zx_handle_duplicate(thread, ZX_RIGHT_SAME_RIGHTS, &handles[BOOTSTRAP_THREAD]), "zx_handle_duplicate failed"); check(log, zx_channel_create(0, loader_svc, &handles[BOOTSTRAP_LOADER_SVC]), "zx_channel_create failed"); zx_status_t status = zx_channel_write( to_child, 0, &msg, sizeof(msg), handles, countof(handles)); check(log, status, "zx_channel_write of loader bootstrap message failed"); } zx_vaddr_t elf_load_bootfs(zx_handle_t log, struct bootfs *fs, zx_handle_t proc, zx_handle_t vmar, zx_handle_t thread, const char* filename, zx_handle_t to_child, size_t* stack_size, zx_handle_t* loader_svc) { zx_handle_t vmo = bootfs_open(log, "program", fs, filename); uintptr_t interp_off = 0; size_t interp_len = 0; zx_vaddr_t entry = load(log, filename, vmar, vmo, &interp_off, &interp_len, NULL, stack_size, true, true); if (interp_len > 0) { char interp[sizeof(INTERP_PREFIX) + interp_len]; memcpy(interp, INTERP_PREFIX, sizeof(INTERP_PREFIX) - 1); zx_status_t status = zx_vmo_read( vmo, &interp[sizeof(INTERP_PREFIX) - 1], interp_off, interp_len); if (status < 0) fail(log, "zx_vmo_read failed: %d", status); interp[sizeof(INTERP_PREFIX) - 1 + interp_len] = '\0'; printl(log, "'%s' has PT_INTERP \"%s\"", filename, interp); zx_handle_t interp_vmo = bootfs_open(log, "dynamic linker", fs, interp); zx_handle_t interp_vmar; entry = load(log, interp, vmar, interp_vmo, NULL, NULL, &interp_vmar, NULL, true, true); stuff_loader_bootstrap(log, proc, vmar, thread, to_child, interp_vmar, vmo, loader_svc); } return entry; }