mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
base/child.h: remove exceptions from process init
This patch replaces the former Child::Process and Child::Process::Loaded_executable classes by static functions that return failure conditions as return values. Issue #5245
This commit is contained in:
parent
0288cffaee
commit
943dfa10e7
@ -55,47 +55,14 @@ Child::Initial_thread::~Initial_thread() { }
|
||||
void Child::Initial_thread::start(addr_t, Start &) { }
|
||||
|
||||
|
||||
/*
|
||||
* On Linux, the ELF loading is performed by the kernel
|
||||
*/
|
||||
Child::Process::Loaded_executable::Loaded_executable(Type,
|
||||
Dataspace_capability,
|
||||
Ram_allocator &,
|
||||
Region_map &,
|
||||
Region_map &,
|
||||
Parent_capability) { }
|
||||
|
||||
|
||||
Child::Process::Process(Type type,
|
||||
Dataspace_capability ldso_ds,
|
||||
Child::Start_result Child::_start_process(Dataspace_capability ldso_ds,
|
||||
Pd_session &pd,
|
||||
Initial_thread_base &,
|
||||
Initial_thread::Start &,
|
||||
Region_map &local_rm,
|
||||
Region_map &remote_rm,
|
||||
Parent_capability parent_cap)
|
||||
:
|
||||
loaded_executable(type, ldso_ds, pd, local_rm, remote_rm, parent_cap)
|
||||
Region_map &,
|
||||
Region_map &,
|
||||
Parent_capability)
|
||||
{
|
||||
/* skip loading when called during fork */
|
||||
if (type == TYPE_FORKED)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the specified executable is a dynamically linked program, we load
|
||||
* the dynamic linker instead.
|
||||
*/
|
||||
if (!ldso_ds.valid()) {
|
||||
error("attempt to start dynamic executable without dynamic linker");
|
||||
throw Missing_dynamic_linker();
|
||||
Linux_native_pd_client(pd.native_pd()).start(ldso_ds);
|
||||
return Start_result::OK;
|
||||
}
|
||||
|
||||
pd.assign_parent(parent_cap);
|
||||
|
||||
Linux_native_pd_client lx_pd(pd.native_pd());
|
||||
|
||||
lx_pd.start(ldso_ds);
|
||||
}
|
||||
|
||||
|
||||
Child::Process::~Process() { }
|
||||
|
@ -375,65 +375,20 @@ class Genode::Child : protected Rpc_object<Parent>,
|
||||
|
||||
} _initial_thread_start { _policy };
|
||||
|
||||
struct Process
|
||||
{
|
||||
class Missing_dynamic_linker : Exception { };
|
||||
class Invalid_executable : Exception { };
|
||||
struct Entry { addr_t ip; };
|
||||
|
||||
enum Type { TYPE_LOADED, TYPE_FORKED };
|
||||
enum class Load_error { INVALID, OUT_OF_RAM, OUT_OF_CAPS };
|
||||
using Load_result = Attempt<Entry, Load_error>;
|
||||
|
||||
struct Loaded_executable
|
||||
{
|
||||
/**
|
||||
* Initial instruction pointer of the new process, as defined
|
||||
* in the header of the executable.
|
||||
*/
|
||||
addr_t entry { 0 };
|
||||
|
||||
/**
|
||||
* Constructor parses the executable and sets up segment
|
||||
* dataspaces
|
||||
*
|
||||
* \param local_rm local address space, needed to make the
|
||||
* segment dataspaces temporarily visible in
|
||||
* the local address space to initialize their
|
||||
* content with the data from the 'elf_ds'
|
||||
*
|
||||
* \throw Region_map::Region_conflict
|
||||
* \throw Region_map::Invalid_dataspace
|
||||
* \throw Invalid_executable
|
||||
* \throw Missing_dynamic_linker
|
||||
* \throw Out_of_ram
|
||||
* \throw Out_of_caps
|
||||
*/
|
||||
Loaded_executable(Type type,
|
||||
Dataspace_capability ldso_ds,
|
||||
static Load_result _load_static_elf(Dataspace_capability elf_ds,
|
||||
Ram_allocator &ram,
|
||||
Region_map &local_rm,
|
||||
Region_map &remote_rm,
|
||||
Parent_capability parent_cap);
|
||||
} loaded_executable;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \throw Missing_dynamic_linker
|
||||
* \throw Invalid_executable
|
||||
* \throw Region_map::Region_conflict
|
||||
* \throw Region_map::Invalid_dataspace
|
||||
* \throw Out_of_ram
|
||||
* \throw Out_of_caps
|
||||
*
|
||||
* On construction of a protection domain, the initial thread is
|
||||
* started immediately.
|
||||
*
|
||||
* The 'type' 'TYPE_FORKED' creates an empty process. In this case,
|
||||
* all process initialization steps except for the creation of the
|
||||
* initial thread must be done manually, i.e., as done for
|
||||
* implementing fork.
|
||||
*/
|
||||
Process(Type type,
|
||||
Dataspace_capability ldso_ds,
|
||||
enum class Start_result { UNKNOWN, OK, OUT_OF_RAM, OUT_OF_CAPS, INVALID };
|
||||
|
||||
static Start_result _start_process(Dataspace_capability ldso_ds,
|
||||
Pd_session &pd,
|
||||
Initial_thread_base &,
|
||||
Initial_thread::Start &,
|
||||
@ -441,10 +396,7 @@ class Genode::Child : protected Rpc_object<Parent>,
|
||||
Region_map &remote_rm,
|
||||
Parent_capability parent);
|
||||
|
||||
~Process();
|
||||
};
|
||||
|
||||
Constructible<Process> _process { };
|
||||
Start_result _start_result { };
|
||||
|
||||
/*
|
||||
* The child's environment sessions
|
||||
@ -732,7 +684,7 @@ class Genode::Child : protected Rpc_object<Parent>,
|
||||
* environment sessions could not be established, e.g., the ROM session
|
||||
* of the binary could not be obtained.
|
||||
*/
|
||||
bool active() const { return _process.constructed(); }
|
||||
bool active() const { return _start_result == Start_result::OK; }
|
||||
|
||||
/**
|
||||
* Initialize the child's PD session
|
||||
|
@ -754,24 +754,32 @@ void Child::_try_construct_env_dependent_members()
|
||||
if (session.phase == Session_state::AVAILABLE)
|
||||
session.phase = Session_state::CAP_HANDED_OUT; });
|
||||
|
||||
if (_process.constructed())
|
||||
if (_start_result == Start_result::OK || _start_result == Start_result::INVALID)
|
||||
return;
|
||||
|
||||
_policy.init(_cpu.session(), _cpu.cap());
|
||||
|
||||
Process::Type const type = _policy.forked()
|
||||
? Process::TYPE_FORKED : Process::TYPE_LOADED;
|
||||
try {
|
||||
_initial_thread.construct(_cpu.session(), _pd.cap(), _policy.name());
|
||||
_policy.with_address_space(_pd.session(), [&] (Region_map &address_space) {
|
||||
_process.construct(type, _linker_dataspace(), _pd.session(),
|
||||
*_initial_thread, _initial_thread_start,
|
||||
_local_rm, address_space, cap()); });
|
||||
}
|
||||
catch (Out_of_ram) { _error("out of RAM during ELF loading"); }
|
||||
catch (Out_of_caps) { _error("out of caps during ELF loading"); }
|
||||
catch (Process::Missing_dynamic_linker) { _error("dynamic linker unavailable"); }
|
||||
catch (Process::Invalid_executable) { _error("invalid ELF executable"); }
|
||||
catch (Out_of_ram) { _error("out of RAM while creating initial child thread"); }
|
||||
catch (Out_of_caps) { _error("out of caps while creating initial child thread"); }
|
||||
|
||||
_pd.session().assign_parent(cap());
|
||||
|
||||
if (_policy.forked()) {
|
||||
_start_result = Start_result::OK;
|
||||
} else {
|
||||
_policy.with_address_space(_pd.session(), [&] (Region_map &address_space) {
|
||||
_start_result = _start_process(_linker_dataspace(), _pd.session(),
|
||||
*_initial_thread, _initial_thread_start,
|
||||
_local_rm, address_space, cap());
|
||||
});
|
||||
}
|
||||
|
||||
if (_start_result == Start_result::OUT_OF_RAM) _error("out of RAM during ELF loading");
|
||||
if (_start_result == Start_result::OUT_OF_CAPS) _error("out of caps during ELF loading");
|
||||
if (_start_result == Start_result::INVALID) _error("attempt to load an invalid executable");
|
||||
}
|
||||
|
||||
|
||||
@ -929,6 +937,4 @@ Child::Child(Region_map &local_rm,
|
||||
Child::~Child()
|
||||
{
|
||||
close_all_sessions();
|
||||
_process.destruct();
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2006-2024 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
@ -24,24 +24,13 @@
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Child::Process::Loaded_executable::Loaded_executable(Type type,
|
||||
Dataspace_capability ldso_ds,
|
||||
Child::Load_result Child::_load_static_elf(Dataspace_capability elf_ds,
|
||||
Ram_allocator &ram,
|
||||
Region_map &local_rm,
|
||||
Region_map &remote_rm,
|
||||
Parent_capability parent_cap)
|
||||
{
|
||||
/* skip loading when called during fork */
|
||||
if (type == TYPE_FORKED)
|
||||
return;
|
||||
|
||||
/* locally attach ELF binary of the dynamic linker */
|
||||
if (!ldso_ds.valid()) {
|
||||
error("attempt to start dynamic executable without dynamic linker");
|
||||
throw Missing_dynamic_linker();
|
||||
}
|
||||
|
||||
addr_t const elf_addr = local_rm.attach(ldso_ds, Region_map::Attr{}).convert<addr_t>(
|
||||
addr_t const elf_addr = local_rm.attach(elf_ds, Region_map::Attr{}).convert<addr_t>(
|
||||
[&] (Region_map::Range range) { return range.start; },
|
||||
[&] (Region_map::Attach_error const e) -> addr_t {
|
||||
if (e == Region_map::Attach_error::INVALID_DATASPACE)
|
||||
@ -51,11 +40,19 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
|
||||
return 0; });
|
||||
|
||||
if (!elf_addr)
|
||||
return;
|
||||
return Load_error::INVALID;
|
||||
|
||||
/* detach ELF binary from local address space when leaving the scope */
|
||||
struct Elf_detach_guard
|
||||
{
|
||||
Region_map &local_rm;
|
||||
addr_t const addr;
|
||||
~Elf_detach_guard() { local_rm.detach(addr); }
|
||||
} elf_detach_guard { .local_rm = local_rm, .addr = elf_addr };
|
||||
|
||||
Elf_binary elf(elf_addr);
|
||||
|
||||
entry = elf.entry();
|
||||
Entry const entry { elf.entry() };
|
||||
|
||||
/* setup region map for the new pd */
|
||||
Elf_segment seg;
|
||||
@ -87,10 +84,20 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
|
||||
*/
|
||||
|
||||
/* alloc dataspace */
|
||||
Dataspace_capability ds_cap;
|
||||
try { ds_cap = ram.alloc(size); }
|
||||
catch (Out_of_ram) {
|
||||
error("allocation of read-write segment failed"); throw; };
|
||||
Ram_allocator::Alloc_result const alloc_result = ram.try_alloc(size);
|
||||
|
||||
if (alloc_result.failed())
|
||||
error("allocation of read-write segment failed");
|
||||
|
||||
using Alloc_error = Ram_allocator::Alloc_error;
|
||||
|
||||
if (alloc_result == Alloc_error::OUT_OF_RAM) return Load_error::OUT_OF_RAM;
|
||||
if (alloc_result == Alloc_error::OUT_OF_CAPS) return Load_error::OUT_OF_CAPS;
|
||||
if (alloc_result.failed()) return Load_error::INVALID;
|
||||
|
||||
Dataspace_capability ds_cap = alloc_result.convert<Dataspace_capability>(
|
||||
[&] (Ram_dataspace_capability cap) { return cap; },
|
||||
[&] (Alloc_error) { /* handled above */ return Dataspace_capability(); });
|
||||
|
||||
/* attach dataspace */
|
||||
Region_map::Attr attr { };
|
||||
@ -103,6 +110,8 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
|
||||
if (e == Region_map::Attach_error::REGION_CONFLICT)
|
||||
error("region conflict while locally attaching ELF segment");
|
||||
return nullptr; });
|
||||
if (!ptr)
|
||||
return Load_error::INVALID;
|
||||
|
||||
addr_t const laddr = elf_addr + seg.file_offset();
|
||||
|
||||
@ -124,19 +133,19 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
|
||||
/* detach dataspace */
|
||||
local_rm.detach(addr_t(ptr));
|
||||
|
||||
remote_rm.attach(ds_cap, Region_map::Attr {
|
||||
auto remote_attach_result = remote_rm.attach(ds_cap, Region_map::Attr {
|
||||
.size = size,
|
||||
.offset = { },
|
||||
.use_at = true,
|
||||
.at = addr,
|
||||
.executable = false,
|
||||
.writeable = true
|
||||
}).with_result(
|
||||
[&] (Region_map::Range) { },
|
||||
[&] (Region_map::Attach_error) {
|
||||
error("region conflict while remotely attaching ELF segment");
|
||||
error("addr=", (void *)addr, " size=", (void *)size); }
|
||||
);
|
||||
});
|
||||
if (remote_attach_result.failed()) {
|
||||
error("failed to remotely attach writable ELF segment");
|
||||
error("addr=", (void *)addr, " size=", (void *)size);
|
||||
return Load_error::INVALID;
|
||||
}
|
||||
} else {
|
||||
|
||||
/* read-only segment */
|
||||
@ -144,28 +153,22 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
|
||||
if (seg.file_size() != seg.mem_size())
|
||||
warning("filesz and memsz for read-only segment differ");
|
||||
|
||||
remote_rm.attach(ldso_ds, Region_map::Attr {
|
||||
auto remote_attach_result = remote_rm.attach(elf_ds, Region_map::Attr {
|
||||
.size = size,
|
||||
.offset = seg.file_offset(),
|
||||
.use_at = true,
|
||||
.at = addr,
|
||||
.executable = seg.flags().x,
|
||||
.writeable = false
|
||||
}).with_result(
|
||||
[&] (Region_map::Range) { },
|
||||
[&] (Region_map::Attach_error const e) {
|
||||
if (e == Region_map::Attach_error::REGION_CONFLICT)
|
||||
error("region conflict while remotely attaching read-only ELF segment");
|
||||
if (e == Region_map::Attach_error::INVALID_DATASPACE)
|
||||
error("attempt to attach invalid read-only segment dataspace");
|
||||
});
|
||||
if (remote_attach_result.failed()) {
|
||||
error("failed to remotely attach read-only ELF segment");
|
||||
error("addr=", (void *)addr, " size=", (void *)size);
|
||||
}
|
||||
);
|
||||
return Load_error::INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/* detach ELF */
|
||||
local_rm.detach(elf_addr);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
@ -199,31 +202,26 @@ void Child::Initial_thread::start(addr_t ip, Start &start)
|
||||
}
|
||||
|
||||
|
||||
Child::Process::Process(Type type,
|
||||
Dataspace_capability ldso_ds,
|
||||
Child::Start_result Child::_start_process(Dataspace_capability ldso_ds,
|
||||
Pd_session &pd,
|
||||
Initial_thread_base &initial_thread,
|
||||
Initial_thread::Start &start,
|
||||
Region_map &local_rm,
|
||||
Region_map &remote_rm,
|
||||
Parent_capability parent_cap)
|
||||
:
|
||||
loaded_executable(type, ldso_ds, pd, local_rm, remote_rm, parent_cap)
|
||||
{
|
||||
/* register parent interface for new protection domain */
|
||||
pd.assign_parent(parent_cap);
|
||||
|
||||
/*
|
||||
* 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 (type == TYPE_FORKED)
|
||||
return;
|
||||
|
||||
/* start main thread */
|
||||
initial_thread.start(loaded_executable.entry, start);
|
||||
return _load_static_elf(ldso_ds, pd, local_rm, remote_rm, parent_cap).convert<Start_result>(
|
||||
[&] (Entry entry) {
|
||||
initial_thread.start(entry.ip, start);
|
||||
return Start_result::OK;
|
||||
},
|
||||
[&] (Load_error e) {
|
||||
switch (e) {
|
||||
case Load_error::OUT_OF_RAM: return Start_result::OUT_OF_RAM;
|
||||
case Load_error::OUT_OF_CAPS: return Start_result::OUT_OF_CAPS;
|
||||
case Load_error::INVALID: break;
|
||||
}
|
||||
return Start_result::INVALID;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Child::Process::~Process() { }
|
||||
|
Loading…
Reference in New Issue
Block a user