/* * \brief Process creation * \author Norman Feske * \author Christian Helmuth * \date 2006-07-18 */ /* * Copyright (C) 2006-2013 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 /* base-internal includes */ #include using namespace Genode; Dataspace_capability Process::_dynamic_linker_cap; /** * Check for dynamic ELF header * * \param elf_ds_cap dataspace containing the ELF binary */ 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 (Rm_session::Attach_failed) { return false; } /* read program header */ Elf_binary elf((addr_t)elf_addr); env()->rm_session()->detach((void *)elf_addr); return elf.is_dynamically_linked(); } /** * Parse ELF and setup segment dataspace * * \param parent_cap parent capability for child (i.e. myself) * \param elf_ds_cap dataspace containing the ELF binary * \param ram RAM session of the new protection domain * \param rm region manager session of the new protection domain */ static addr_t _setup_elf(Parent_capability parent_cap, Dataspace_capability elf_ds_cap, Ram_session &ram, Rm_session &rm) { /* attach ELF locally */ addr_t elf_addr; try { elf_addr = env()->rm_session()->attach(elf_ds_cap); } catch (Rm_session::Attach_failed) { return 0; } /* setup ELF object and read program entry pointer */ Elf_binary elf((addr_t)elf_addr); if (!elf.valid()) return 0; /* * entry point of program - can be set to 0 to indicate errors in ELF * handling */ addr_t entry = elf.entry(); /* setup region map for the new pd */ Elf_segment seg; for (unsigned n = 0; (seg = elf.get_segment(n)).valid(); ++n) { if (seg.flags().skip) continue; /* same values for r/o and r/w segments */ addr_t addr = (addr_t)seg.start(); size_t size = seg.mem_size(); bool parent_info = false; off_t offset; Dataspace_capability ds_cap; void *out_ptr = 0; bool write = seg.flags().w; bool exec = seg.flags().x; if (write) { /* read-write segment */ offset = 0; /* alloc dataspace */ try { ds_cap = ram.alloc(size); } catch (Ram_session::Alloc_failed) { PERR("Ram.alloc() failed"); entry = 0; break; } /* attach dataspace */ void *base; try { base = env()->rm_session()->attach(ds_cap); } catch (Rm_session::Attach_failed) { PERR("env()->rm_session()->attach() failed"); entry = 0; break; } void *ptr = base; addr_t laddr = elf_addr + seg.file_offset(); /* copy contents and fill with zeros */ memcpy(ptr, (void *)laddr, seg.file_size()); if (size > seg.file_size()) memset((void *)((addr_t)ptr + seg.file_size()), 0, size - seg.file_size()); /* * we store the parent information at the beginning of the first * data segment */ if (!parent_info) { Native_capability::Raw *raw = (Native_capability::Raw *)ptr; raw->dst = parent_cap.dst(); raw->local_name = parent_cap.local_name(); parent_info = true; } /* detach dataspace */ env()->rm_session()->detach(base); try { out_ptr = rm.attach_at(ds_cap, addr, size, offset); } catch (Rm_session::Attach_failed) { } } else { /* read-only segment */ offset = seg.file_offset(); ds_cap = elf_ds_cap; /* XXX currently we assume r/o segment sizes never differ */ if (seg.file_size() != seg.mem_size()) PWRN("filesz and memsz for read-only segment differ"); if (exec) try { out_ptr = rm.attach_executable(ds_cap, addr, size, offset); } catch (Rm_session::Attach_failed) { } else try { out_ptr = rm.attach_at(ds_cap, addr, size, offset); } catch (Rm_session::Attach_failed) { } } if ((addr_t)out_ptr != addr) PWRN("addresses differ after attach (addr=%p out_ptr=%p)", (void *)addr, out_ptr); } /* detach ELF */ env()->rm_session()->detach((void *)elf_addr); return entry; } Process::Process(Dataspace_capability elf_ds_cap, Pd_session_capability pd_session_cap, Ram_session_capability ram_session_cap, Cpu_session_capability cpu_session_cap, Rm_session_capability rm_session_cap, Parent_capability parent_cap, char const *name) : _pd_session_client(pd_session_cap), _cpu_session_client(cpu_session_cap), _rm_session_client(rm_session_cap) { if (!pd_session_cap.valid()) return; enum Local_exception { THREAD_FAIL, ELF_FAIL, ASSIGN_PARENT_FAIL, THREAD_ADD_FAIL, THREAD_BIND_FAIL, THREAD_PAGER_FAIL, THREAD_START_FAIL, }; /* XXX this only catches local exceptions */ /* FIXME find sane quota values or make them configurable */ try { int err; /* create thread0 */ try { enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT }; _thread0_cap = _cpu_session_client.create_thread(WEIGHT, name); } catch (Cpu_session::Thread_creation_failed) { PERR("Creation of thread0 failed"); throw THREAD_FAIL; } /* * The argument 'elf_ds_cap' may be invalid, which is not an error. * This can happen when the process library is used to set up a process * forked from another. In this case, all process initialization should * be done except for the ELF loading and the startup of the main * thread (as a forked process does not start its execution at the ELF * entrypoint). */ bool const forked = !elf_ds_cap.valid(); /* check for dynamic program header */ if (!forked && _check_dynamic_elf(elf_ds_cap)) { if (!_dynamic_linker_cap.valid()) { PERR("Dynamically linked file found, but no dynamic linker binary present"); throw ELF_FAIL; } elf_ds_cap = _dynamic_linker_cap; } /* init temporary allocator object */ Ram_session_client ram(ram_session_cap); /* parse ELF binary and setup segment dataspaces */ addr_t entry = 0; if (elf_ds_cap.valid()) { entry = _setup_elf(parent_cap, elf_ds_cap, ram, _rm_session_client); if (!entry) { PERR("Setup ELF failed"); throw ELF_FAIL; } } /* register parent interface for new protection domain */ if (_pd_session_client.assign_parent(parent_cap)) { PERR("Could not assign parent interface to new PD"); throw ASSIGN_PARENT_FAIL; } /* bind thread0 */ err = _pd_session_client.bind_thread(_thread0_cap); if (err) { PERR("Thread binding failed (%d)", err); throw THREAD_BIND_FAIL; } /* register thread0 at region manager session */ Pager_capability pager; try { pager = _rm_session_client.add_client(_thread0_cap); } catch (...) { PERR("Pager setup failed (%d)", err); throw THREAD_ADD_FAIL; } /* set pager in thread0 */ err = _cpu_session_client.set_pager(_thread0_cap, pager); if (err) { PERR("Setting pager for thread0 failed"); throw THREAD_PAGER_FAIL; } /* * Inhibit start of main thread if the new process happens to be forked * from another. In this case, the main thread will get manually * started after constructing the 'Process'. */ if (!forked) { /* start main thread */ err = _cpu_session_client.start(_thread0_cap, entry, 0 /* unused */); if (err) { PERR("Thread0 startup failed"); throw THREAD_START_FAIL; } } } catch (Local_exception cause) { switch (cause) { case THREAD_START_FAIL: case THREAD_PAGER_FAIL: case THREAD_ADD_FAIL: case THREAD_BIND_FAIL: case ASSIGN_PARENT_FAIL: case ELF_FAIL: _cpu_session_client.kill_thread(_thread0_cap); _thread0_cap = Thread_capability(); case THREAD_FAIL: default: PWRN("unknown exception?"); } } } Process::~Process() { /* * Try to kill thread0, which was created in the process constructor. If * this fails, do nothing. */ try { _cpu_session_client.kill_thread(_thread0_cap); } catch (Genode::Ipc_error) { } }