base/error.h: add raise() interface

The new 'raise' function can be used instead of 'throw' to keep the
framework headers void of C++ throw statements, which would otherwise
prevent the compilation of the headers with -fno-exceptions.

In the presence of the C++ runtime, the 'raise' implementation reflects
the supplied error value(s) as C++ exceptions of the appropriate type.
In the (future) optional absence of the C++ runtime, 'raise' remains
unresolved, which then gives us the assurance that the binary contains
no code path leading to 'raise', all error conditions must have been
covered in other ways than 'raise'.

For this reason, 'Genode::raise' is not provided by the base library
but the cxx library (C++ runtime). Once we allow components to opt out
of the cxx library, 'raise' will automatically become unresolved for
those strict components.

Issue #5245
This commit is contained in:
Norman Feske 2025-04-06 11:43:02 +02:00
parent fd5172e1f3
commit b4a746bc89
6 changed files with 52 additions and 12 deletions

View File

@ -1,11 +1,12 @@
SRC_CC += lx_hybrid.cc new_delete.cc capability_space.cc
SRC_CC += signal_transmitter.cc signal.cc
SRC_CC += signal_transmitter.cc signal.cc raise.cc
SRC_C += libgcc.c
# new_delete.cc uses libsupc++ which means we need to access
# its include directory.
STDINC := yes
vpath raise.cc $(BASE_DIR)/src/lib/cxx
vpath new_delete.cc $(BASE_DIR)/src/lib/cxx
vpath lx_hybrid.cc $(REP_DIR)/src/lib/lx_hybrid
vpath libgcc.c $(REP_DIR)/src/lib/lx_hybrid

View File

@ -95,15 +95,13 @@ struct Genode::Allocator : Deallocator
/**
* Raise exception according to the 'error' value
*
* \deprecated use 'raise()' instead
* \noapi
*/
static void throw_alloc_error(Alloc_error error) __attribute__((noreturn))
static void throw_alloc_error(Alloc_error e) __attribute__((noreturn))
{
switch (error) {
case Alloc_error::OUT_OF_RAM: throw Out_of_ram();
case Alloc_error::OUT_OF_CAPS: throw Out_of_caps();
case Alloc_error::DENIED: break;
}
throw Denied();
raise(e);
}
/**
@ -121,8 +119,7 @@ struct Genode::Allocator : Deallocator
{
return try_alloc(size).convert<void *>(
[&] (void *ptr) { return ptr; },
[&] (Alloc_error error) -> void * {
throw_alloc_error(error); });
[&] (Alloc_error e) -> void * { raise(e); });
}
};

View File

@ -1,5 +1,5 @@
/*
* \brief Error types
* \brief Error types and last-resort error handling
* \author Norman Feske
* \date 2025-03-05
*/
@ -31,6 +31,19 @@ namespace Genode {
* the caller fail gracefully or consciously panic at the caller side.
*/
enum class Alloc_error { OUT_OF_RAM, OUT_OF_CAPS, DENIED };
/**
* Raise an error without return
*
* This function should never be called except in panic situations where
* no other way of reflecting an error condition exists.
*
* When using the C++ runtime, errors are reflected by the exceptions
* defined at 'base/exception.h'. If the component has no reference to
* 'raise()' C++ exceptions are disabled, the component is known to
* contain no unhandled error conditions.
*/
void raise(Alloc_error) __attribute__((noreturn));
}
#endif /* _INCLUDE__BASE__ERROR_H_ */

View File

@ -1,4 +1,5 @@
CXX_SRC_CC += misc.cc new_delete.cc malloc_free.cc exception.cc guard.cc emutls.cc
CXX_SRC_CC += misc.cc new_delete.cc malloc_free.cc exception.cc guard.cc \
emutls.cc raise.cc
INC_DIR += $(REP_DIR)/src/include
# We need the libsupc++ include directory
STDINC = yes

View File

@ -253,6 +253,7 @@ _ZN6Genode5printERNS_6OutputEl T
_ZN6Genode5printERNS_6OutputEm T
_ZN6Genode5printERNS_6OutputEx T
_ZN6Genode5printERNS_6OutputEy T
_ZN6Genode5raiseENS_11Alloc_errorE T
_ZN6Genode6Output10out_stringEPKcm T
_ZN6Genode6Parent8announceERKNS_13Rpc_in_bufferILm64EEENS_10CapabilityINS_4RootEEE T
_ZN6Genode6Signal19_dec_ref_and_unlockEv T

View File

@ -0,0 +1,27 @@
/*
* \brief Reflect error conditions as C++ exceptions
* \author Norman Feske
* \date 2025-03-05
*/
/*
* Copyright (C) 2025 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.
*/
/* Genode includes */
#include <base/exception.h>
#include <base/error.h>
void Genode::raise(Alloc_error error)
{
switch (error) {
case Alloc_error::OUT_OF_RAM: throw Out_of_ram();
case Alloc_error::OUT_OF_CAPS: throw Out_of_caps();
case Alloc_error::DENIED: break;
}
throw Denied();
}