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:
Norman Feske 2024-07-01 16:29:20 +02:00
parent 0288cffaee
commit 943dfa10e7
4 changed files with 115 additions and 192 deletions

View File

@ -55,47 +55,14 @@ Child::Initial_thread::~Initial_thread() { }
void Child::Initial_thread::start(addr_t, Start &) { } void Child::Initial_thread::start(addr_t, Start &) { }
/* Child::Start_result Child::_start_process(Dataspace_capability ldso_ds,
* 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,
Pd_session &pd, Pd_session &pd,
Initial_thread_base &, Initial_thread_base &,
Initial_thread::Start &, Initial_thread::Start &,
Region_map &local_rm, Region_map &,
Region_map &remote_rm, Region_map &,
Parent_capability parent_cap) Parent_capability)
:
loaded_executable(type, ldso_ds, pd, local_rm, remote_rm, parent_cap)
{ {
/* skip loading when called during fork */ Linux_native_pd_client(pd.native_pd()).start(ldso_ds);
if (type == TYPE_FORKED) return Start_result::OK;
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();
}
pd.assign_parent(parent_cap);
Linux_native_pd_client lx_pd(pd.native_pd());
lx_pd.start(ldso_ds);
} }
Child::Process::~Process() { }

View File

@ -375,65 +375,20 @@ class Genode::Child : protected Rpc_object<Parent>,
} _initial_thread_start { _policy }; } _initial_thread_start { _policy };
struct Process struct Entry { addr_t ip; };
{
class Missing_dynamic_linker : Exception { };
class Invalid_executable : Exception { };
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 static Load_result _load_static_elf(Dataspace_capability elf_ds,
{
/**
* 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,
Ram_allocator &ram, Ram_allocator &ram,
Region_map &local_rm, Region_map &local_rm,
Region_map &remote_rm, Region_map &remote_rm,
Parent_capability parent_cap); Parent_capability parent_cap);
} loaded_executable;
/** enum class Start_result { UNKNOWN, OK, OUT_OF_RAM, OUT_OF_CAPS, INVALID };
* Constructor
* static Start_result _start_process(Dataspace_capability ldso_ds,
* \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,
Pd_session &pd, Pd_session &pd,
Initial_thread_base &, Initial_thread_base &,
Initial_thread::Start &, Initial_thread::Start &,
@ -441,10 +396,7 @@ class Genode::Child : protected Rpc_object<Parent>,
Region_map &remote_rm, Region_map &remote_rm,
Parent_capability parent); Parent_capability parent);
~Process(); Start_result _start_result { };
};
Constructible<Process> _process { };
/* /*
* The child's environment sessions * 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 * environment sessions could not be established, e.g., the ROM session
* of the binary could not be obtained. * 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 * Initialize the child's PD session

View File

@ -754,24 +754,32 @@ void Child::_try_construct_env_dependent_members()
if (session.phase == Session_state::AVAILABLE) if (session.phase == Session_state::AVAILABLE)
session.phase = Session_state::CAP_HANDED_OUT; }); session.phase = Session_state::CAP_HANDED_OUT; });
if (_process.constructed()) if (_start_result == Start_result::OK || _start_result == Start_result::INVALID)
return; return;
_policy.init(_cpu.session(), _cpu.cap()); _policy.init(_cpu.session(), _cpu.cap());
Process::Type const type = _policy.forked()
? Process::TYPE_FORKED : Process::TYPE_LOADED;
try { try {
_initial_thread.construct(_cpu.session(), _pd.cap(), _policy.name()); _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_ram) { _error("out of RAM while creating initial child thread"); }
catch (Out_of_caps) { _error("out of caps during ELF loading"); } catch (Out_of_caps) { _error("out of caps while creating initial child thread"); }
catch (Process::Missing_dynamic_linker) { _error("dynamic linker unavailable"); }
catch (Process::Invalid_executable) { _error("invalid ELF executable"); } _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() Child::~Child()
{ {
close_all_sessions(); close_all_sessions();
_process.destruct();
} }

View File

@ -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 * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
@ -24,24 +24,13 @@
using namespace Genode; using namespace Genode;
Child::Process::Loaded_executable::Loaded_executable(Type type, Child::Load_result Child::_load_static_elf(Dataspace_capability elf_ds,
Dataspace_capability ldso_ds,
Ram_allocator &ram, Ram_allocator &ram,
Region_map &local_rm, Region_map &local_rm,
Region_map &remote_rm, Region_map &remote_rm,
Parent_capability parent_cap) Parent_capability parent_cap)
{ {
/* skip loading when called during fork */ addr_t const elf_addr = local_rm.attach(elf_ds, Region_map::Attr{}).convert<addr_t>(
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>(
[&] (Region_map::Range range) { return range.start; }, [&] (Region_map::Range range) { return range.start; },
[&] (Region_map::Attach_error const e) -> addr_t { [&] (Region_map::Attach_error const e) -> addr_t {
if (e == Region_map::Attach_error::INVALID_DATASPACE) if (e == Region_map::Attach_error::INVALID_DATASPACE)
@ -51,11 +40,19 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
return 0; }); return 0; });
if (!elf_addr) 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); Elf_binary elf(elf_addr);
entry = elf.entry(); Entry const entry { elf.entry() };
/* setup region map for the new pd */ /* setup region map for the new pd */
Elf_segment seg; Elf_segment seg;
@ -87,10 +84,20 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
*/ */
/* alloc dataspace */ /* alloc dataspace */
Dataspace_capability ds_cap; Ram_allocator::Alloc_result const alloc_result = ram.try_alloc(size);
try { ds_cap = ram.alloc(size); }
catch (Out_of_ram) { if (alloc_result.failed())
error("allocation of read-write segment failed"); throw; }; 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 */ /* attach dataspace */
Region_map::Attr attr { }; Region_map::Attr attr { };
@ -103,6 +110,8 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
if (e == Region_map::Attach_error::REGION_CONFLICT) if (e == Region_map::Attach_error::REGION_CONFLICT)
error("region conflict while locally attaching ELF segment"); error("region conflict while locally attaching ELF segment");
return nullptr; }); return nullptr; });
if (!ptr)
return Load_error::INVALID;
addr_t const laddr = elf_addr + seg.file_offset(); addr_t const laddr = elf_addr + seg.file_offset();
@ -124,19 +133,19 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
/* detach dataspace */ /* detach dataspace */
local_rm.detach(addr_t(ptr)); 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, .size = size,
.offset = { }, .offset = { },
.use_at = true, .use_at = true,
.at = addr, .at = addr,
.executable = false, .executable = false,
.writeable = true .writeable = true
}).with_result( });
[&] (Region_map::Range) { }, if (remote_attach_result.failed()) {
[&] (Region_map::Attach_error) { error("failed to remotely attach writable ELF segment");
error("region conflict while remotely attaching ELF segment"); error("addr=", (void *)addr, " size=", (void *)size);
error("addr=", (void *)addr, " size=", (void *)size); } return Load_error::INVALID;
); }
} else { } else {
/* read-only segment */ /* read-only segment */
@ -144,28 +153,22 @@ Child::Process::Loaded_executable::Loaded_executable(Type type,
if (seg.file_size() != seg.mem_size()) if (seg.file_size() != seg.mem_size())
warning("filesz and memsz for read-only segment differ"); 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, .size = size,
.offset = seg.file_offset(), .offset = seg.file_offset(),
.use_at = true, .use_at = true,
.at = addr, .at = addr,
.executable = seg.flags().x, .executable = seg.flags().x,
.writeable = false .writeable = false
}).with_result( });
[&] (Region_map::Range) { }, if (remote_attach_result.failed()) {
[&] (Region_map::Attach_error const e) { error("failed to remotely attach read-only ELF segment");
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");
error("addr=", (void *)addr, " size=", (void *)size); error("addr=", (void *)addr, " size=", (void *)size);
} return Load_error::INVALID;
);
} }
} }
}
/* detach ELF */ return entry;
local_rm.detach(elf_addr);
} }
@ -199,31 +202,26 @@ void Child::Initial_thread::start(addr_t ip, Start &start)
} }
Child::Process::Process(Type type, Child::Start_result Child::_start_process(Dataspace_capability ldso_ds,
Dataspace_capability ldso_ds,
Pd_session &pd, Pd_session &pd,
Initial_thread_base &initial_thread, Initial_thread_base &initial_thread,
Initial_thread::Start &start, Initial_thread::Start &start,
Region_map &local_rm, Region_map &local_rm,
Region_map &remote_rm, Region_map &remote_rm,
Parent_capability parent_cap) Parent_capability parent_cap)
:
loaded_executable(type, ldso_ds, pd, local_rm, remote_rm, parent_cap)
{ {
/* register parent interface for new protection domain */ return _load_static_elf(ldso_ds, pd, local_rm, remote_rm, parent_cap).convert<Start_result>(
pd.assign_parent(parent_cap); [&] (Entry entry) {
initial_thread.start(entry.ip, start);
/* return Start_result::OK;
* Inhibit start of main thread if the new process happens to be forked },
* from another. In this case, the main thread will get manually [&] (Load_error e) {
* started after constructing the 'Process'. switch (e) {
*/ case Load_error::OUT_OF_RAM: return Start_result::OUT_OF_RAM;
if (type == TYPE_FORKED) case Load_error::OUT_OF_CAPS: return Start_result::OUT_OF_CAPS;
return; case Load_error::INVALID: break;
}
/* start main thread */ return Start_result::INVALID;
initial_thread.start(loaded_executable.entry, start); }
);
} }
Child::Process::~Process() { }