1818#include < cstdint>
1919#include < cstdlib>
2020#include < exception>
21+ #include < memory_resource>
2122#include < span>
2223#include < typeinfo>
2324
25+ #include < libhal-exceptions/control.hpp>
26+
2427#include " internal.hpp"
2528
2629// NOLINTBEGIN(bugprone-reserved-identifier)
@@ -57,13 +60,7 @@ struct instructions_t
5760 };
5861};
5962
60- namespace {
61- // TODO(#19): Make this thread local and figure out how to support is using
62- // emutls.
63- exception_ptr active_exception = nullptr ;
64- // TODO(#42): Use the applications's polymorphic allocator, not our own space.
65- std::array<std::uint8_t , 256 > exception_buffer{};
66- } // namespace
63+ thread_local exception_control_block control_block{};
6764
6865su16_t * get_su16 (void * p_ptr)
6966{
@@ -74,11 +71,6 @@ lu_t* get_lu(void* p_ptr)
7471 return reinterpret_cast <lu_t *>(p_ptr);
7572}
7673
77- exception_ptr current_exception () noexcept
78- {
79- return active_exception;
80- }
81-
8274/* *
8375 * @brief Captures all registers CPU registers
8476 *
@@ -473,13 +465,13 @@ inline lsda_header_info parse_header(std::uint8_t const** p_lsda)
473465 return info;
474466}
475467
476- inline auto const * to_lsda (exception_object & p_exception_object)
468+ inline auto const * to_lsda (exception_control_block & p_exception_object)
477469{
478470 return reinterpret_cast <std::uint8_t const *>(
479471 index_entry_t::lsda_data (p_exception_object.cache .personality ));
480472}
481473
482- inline auto calculate_relative_pc (exception_object & p_exception_object)
474+ inline auto calculate_relative_pc (exception_control_block & p_exception_object)
483475{
484476 return p_exception_object.cache .relative_address () & ~1 ;
485477}
@@ -626,7 +618,7 @@ class action_decoder
626618 std::int32_t m_filter = 0 ;
627619};
628620
629- inline void enter_function (exception_object & p_exception_object)
621+ inline void enter_function (exception_control_block & p_exception_object)
630622{
631623 auto const * lsda = to_lsda (p_exception_object);
632624 auto info = parse_header (&lsda);
@@ -1732,7 +1724,7 @@ constexpr instructions_t personality_to_unwind_instructions(
17321724
17331725[[gnu::always_inline]]
17341726constexpr instructions_t create_instructions_from_entry (
1735- exception_object const & p_exception_object)
1727+ exception_control_block const & p_exception_object)
17361728{
17371729 constexpr auto generic = hal::bit_mask::from<31 >();
17381730
@@ -1752,10 +1744,14 @@ constexpr instructions_t create_instructions_from_entry(
17521744 return personality_to_unwind_instructions (handler_data);
17531745}
17541746
1755- void raise_exception (exception_object & p_exception_object)
1747+ void raise_exception (exception_control_block & p_exception_object)
17561748{
17571749 while (true ) {
17581750 switch (p_exception_object.cache .state ()) {
1751+ [[unlikely]]
1752+ case runtime_state::handled_state: {
1753+ std::terminate ();
1754+ }
17591755 case runtime_state::get_next_frame: {
17601756 auto const & index_entry = get_index_entry (p_exception_object.cpu .pc );
17611757 p_exception_object.cache .entry_ptr = &index_entry;
@@ -1831,8 +1827,8 @@ instructions_t cache(F* p_function_to_be_cached)
18311827 }
18321828}
18331829
1834- instructions_t cxa_throw_unwind_instructions = cache(&__wrap___cxa_throw);
1835- instructions_t cxa_rethrow_unwind_instructions = cache(&__wrap___cxa_rethrow);
1830+ auto const cxa_throw_unwind_instructions = cache(&__wrap___cxa_throw);
1831+ auto const cxa_rethrow_unwind_instructions = cache(&__wrap___cxa_rethrow);
18361832} // namespace ke
18371833
18381834// NOLINTBEGIN(bugprone-reserved-identifier)
@@ -1960,8 +1956,8 @@ void flatten_rtti(ke::exception_ptr p_thrown_exception,
19601956 auto iter = p_map.begin ();
19611957 auto info = get_rtti_type (p_type_info);
19621958
1963- // If this is a non-class type, then there is in hierarchy and this first
1964- // element we pushed is the only one.
1959+ // If this is a non-class type, then there is no hierarchy and the first
1960+ // element we pushed onto the list is the only one that exists .
19651961 if (info == rtti_type::anything_else) {
19661962 return ;
19671963 }
@@ -1985,6 +1981,7 @@ void flatten_rtti(ke::exception_ptr p_thrown_exception,
19851981 }
19861982 }
19871983}
1984+
19881985} // namespace ke
19891986
19901987namespace {
@@ -1998,23 +1995,37 @@ extern "C"
19981995 {
19991996 std::terminate ();
20001997 }
1998+
20011999 // NOLINTNEXTLINE(readability-identifier-naming)
20022000 void * __wrap___cxa_allocate_exception (size_t p_thrown_size)
20032001 {
2004- // TODO(#42): Use the applications's polymorphic allocator, not our own
2005- // space.
2006- if (p_thrown_size >
2007- ke::exception_buffer.size () + sizeof (ke::exception_object)) {
2002+ if (ke::control_block.cache .state () != ke::runtime_state::handled_state) {
20082003 std::terminate ();
20092004 }
20102005
2011- return ke::exception_buffer.data () + sizeof (ke::exception_object);
2006+ // We set the state to `get_next_frame` to prep the control block state but
2007+ // more importantly, in order to detect if the allocator throws bad alloc
2008+ // and we re-enter this function, we can detect that and terminate.
2009+ ke::control_block.cache .state (ke::runtime_state::get_next_frame);
2010+ auto const allocation_amount =
2011+ sizeof (ke::exception_allocation<>) + p_thrown_size;
2012+
2013+ auto exception_memory_resource = &hal::get_exception_allocator ();
2014+
2015+ auto * object = static_cast <ke::exception_allocation<>*>(
2016+ exception_memory_resource->allocate (allocation_amount));
2017+
2018+ object->allocator = exception_memory_resource;
2019+ object->size = allocation_amount;
2020+
2021+ return &object->data ;
20122022 }
20132023
20142024 // NOLINTNEXTLINE(readability-identifier-naming)
2015- void __wrap___cxa_free_exception ([[maybe_unused]] void * p_thrown_exception)
2025+ void __wrap___cxa_free_exception (void * p_thrown_exception)
20162026 {
2017- ke::exception_buffer.fill (0 );
2027+ auto object = ke::get_allocation_from_exception (p_thrown_exception);
2028+ object->allocator ->deallocate (object, object->size );
20182029 }
20192030
20202031 // NOLINTNEXTLINE(readability-identifier-naming)
@@ -2027,29 +2038,29 @@ extern "C"
20272038 // NOLINTNEXTLINE(readability-identifier-naming)
20282039 void __wrap___cxa_end_catch ()
20292040 {
2030- auto & exception_object = ke::extract_exception_object (ke::active_exception) ;
2031- if (exception_object .cache .rethrown ()) {
2032- exception_object .cache .rethrown (false );
2041+ auto & control_block = ke::control_block ;
2042+ if (control_block .cache .rethrown ()) {
2043+ control_block .cache .rethrown (false );
20332044 } else {
2034- __wrap___cxa_free_exception (ke::active_exception );
2045+ __wrap___cxa_free_exception (control_block. thrown_object );
20352046 }
20362047 }
20372048
20382049 // NOLINTNEXTLINE(readability-identifier-naming)
2039- void * __wrap___cxa_begin_catch (void * p_exception_object )
2050+ void * __wrap___cxa_begin_catch (void *)
20402051 {
2041- auto * eo = reinterpret_cast < ke::exception_object*>(p_exception_object) ;
2042- auto * thrown_object = ke::extract_thrown_object (eo );
2043- return thrown_object;
2052+ auto & control_block = ke::control_block ;
2053+ control_block. cache . state ( ke::runtime_state::handled_state );
2054+ return control_block. thrown_object ;
20442055 }
20452056
20462057 // NOLINTNEXTLINE(readability-identifier-naming)
20472058 void __wrap___cxa_end_cleanup ()
20482059 {
2049- auto & exception_object = ke::extract_exception_object (ke::active_exception) ;
2050- exception_object .cache .state (ke::runtime_state::unwind_frame);
2060+ auto & control_block = ke::control_block ;
2061+ control_block .cache .state (ke::runtime_state::unwind_frame);
20512062 // Raise exception returns when an error or call to terminate has been found
2052- ke::raise_exception (exception_object );
2063+ ke::raise_exception (control_block );
20532064 std::terminate ();
20542065 }
20552066
@@ -2058,18 +2069,22 @@ extern "C"
20582069 void __wrap__Unwind_Resume (void *)
20592070 // NOLINTEND(readability-identifier-naming)
20602071 {
2061- __wrap___cxa_end_cleanup ();
2072+ auto & control_block = ke::control_block;
2073+ control_block.cache .state (ke::runtime_state::unwind_frame);
2074+ // Raise exception returns when an error or call to terminate has been found
2075+ ke::raise_exception (control_block);
2076+ std::terminate ();
20622077 }
20632078
20642079 // NOLINTNEXTLINE(readability-identifier-naming)
20652080 void __wrap___cxa_rethrow () noexcept (false )
20662081 {
2067- auto & exception_object = ke::extract_exception_object (ke::active_exception) ;
2082+ auto & control_block = ke::control_block ;
20682083
2069- ke::capture_cpu_core (exception_object .cpu );
2084+ ke::capture_cpu_core (control_block .cpu );
20702085
2071- exception_object .cache .state (ke::runtime_state::get_next_frame);
2072- exception_object .cache .rethrown (true );
2086+ control_block .cache .state (ke::runtime_state::get_next_frame);
2087+ control_block .cache .rethrown (true );
20732088
20742089 // This must ALWAYS evaluate to false. But since the variable is volatile,
20752090 // the compiler will not optimize it away and thus, __wrap___cxa_throw will
@@ -2081,10 +2096,9 @@ extern "C"
20812096 // have in the C++ throw RTTI list, might as well
20822097 // reuse it here.
20832098 }
2084- ke::unwind_frame (ke::cxa_rethrow_unwind_instructions, exception_object.cpu );
2085-
2099+ ke::unwind_frame (ke::cxa_rethrow_unwind_instructions, control_block.cpu );
20862100 // Raise exception returns when an error or call to terminate has been found
2087- ke::raise_exception (exception_object );
2101+ ke::raise_exception (control_block );
20882102 // TODO(#38): this area is considered a catch block, meaning that the
20892103 // exception is handled at this point. We should mark it as such.
20902104 std::terminate ();
@@ -2095,13 +2109,12 @@ extern "C"
20952109 std::type_info* p_type_info,
20962110 ke::destructor_t p_destructor) noexcept (false )
20972111 {
2098- ke::active_exception = p_thrown_exception;
2099- auto & exception_object = ke::extract_exception_object (p_thrown_exception);
2100- exception_object.destructor = p_destructor;
2101- ke::capture_cpu_core (exception_object.cpu );
2112+ auto & control_block = ke::control_block;
21022113
2103- ke::flatten_rtti<12 >(
2104- p_thrown_exception, exception_object.type_info , p_type_info);
2114+ control_block.thrown_object = p_thrown_exception;
2115+ control_block.destructor = p_destructor;
2116+ ke::capture_cpu_core (control_block.cpu );
2117+ ke::flatten_rtti (p_thrown_exception, control_block.type_info , p_type_info);
21052118
21062119 // This must ALWAYS evaluate to false. But since the variable is volatile,
21072120 // the compiler will not optimize it away and thus, __wrap___cxa_throw will
@@ -2113,9 +2126,13 @@ extern "C"
21132126 // have in the C++ throw RTTI list, might as well
21142127 // reuse it here.
21152128 }
2116- ke::unwind_frame (ke::cxa_throw_unwind_instructions, exception_object.cpu );
2129+ // Use the expanded unwind instructions to quickly unwind cxa_throw.
2130+ ke::unwind_frame (ke::cxa_throw_unwind_instructions, control_block.cpu );
2131+
21172132 // Raise exception returns when an error or call to terminate has been found
2118- ke::raise_exception (exception_object);
2133+ ke::raise_exception (control_block);
2134+
2135+ control_block.cache .state (ke::runtime_state::handled_state);
21192136 // TODO(#38): this area is considered a catch block, meaning that the
21202137 // exception is handled at this point. We should mark it as such.
21212138 std::terminate ();
0 commit comments