mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 05:37:54 +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 &) { }
|
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() { }
|
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() { }
|
|
||||||
|
Loading…
Reference in New Issue
Block a user