Skip to content

Commit

Permalink
[WIP] 🐛 Enable __cxa_throw & __cxa_rethrow exception entry
Browse files Browse the repository at this point in the history
This allows them to be unwound like any other function.
  • Loading branch information
kammce committed Nov 11, 2024
1 parent 4b55ec1 commit 9eadd90
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 77 deletions.
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ libhal_make_library(
libhal::util
)

target_compile_definitions(libhal-exceptions PRIVATE
OPTIMIZATION_LEVEL=${OPTIMIZATION_LEVEL})
target_compile_options(libhal-exceptions PRIVATE -save-temps=obj)

if(NOT ${CMAKE_CROSSCOMPILING})
libhal_unit_test(
Expand Down
13 changes: 1 addition & 12 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,7 @@ def requirements(self):

def build(self):
cmake = CMake(self)
optimization_level = {
'Debug': 1,
'MinSizeRel': 2,
'RelWithDebInfo': 3,
'Release': 4
}

configure_variables = {"RUNTIME": self._runtime_select}
build_type = self.settings.build_type
if str(build_type) in optimization_level:
configure_variables["OPTIMIZATION_LEVEL"] = optimization_level[str(
build_type)]

cmake.configure(variables=configure_variables)
cmake.build()

Expand Down Expand Up @@ -126,6 +114,7 @@ def package_info(self):

if self.options.runtime == "estell":
self.cpp_info.exelinkflags.extend([
"-fexceptions",
"-Wl,--wrap=__cxa_throw",
"-Wl,--wrap=__cxa_rethrow",
"-Wl,--wrap=__cxa_end_catch",
Expand Down
93 changes: 30 additions & 63 deletions src/arm_cortex/estell/exception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@

#include "internal.hpp"

#define Debug 1
#define MinSizeRel 2
#define RelWithDebInfo 3
#define Release 4

#if !defined(OPTIMIZATION_LEVEL)
#error "OPTIMIZATION LEVEL MUST BE DEFINED!"
#endif

namespace ke {

union instructions_t
Expand Down Expand Up @@ -1752,6 +1743,9 @@ void raise_exception(exception_object& p_exception_object)
p_exception_object.cache.state(runtime_state::unwind_frame);
break;
}
if (index_entry.personality_offset == 0x1) {
return;
}
p_exception_object.cache.personality = index_entry.personality();
auto const* descriptor_start =
index_entry_t::descriptor_start(p_exception_object.cache.personality);
Expand Down Expand Up @@ -1929,6 +1923,10 @@ void flatten_rtti(ke::exception_ptr p_thrown_exception,
}
} // namespace ke

namespace {
bool const volatile libhal_convince_compiler_to_emit_metadata = false;
}

extern "C"
{
void _exit([[maybe_unused]] int rc) // NOLINT
Expand Down Expand Up @@ -1982,7 +1980,7 @@ extern "C"
std::terminate();
}

void __wrap___cxa_rethrow()
void __wrap___cxa_rethrow() noexcept(false)
{
auto& exception_object = ke::extract_exception_object(ke::active_exception);

Expand All @@ -1991,30 +1989,16 @@ extern "C"
exception_object.cache.state(ke::runtime_state::get_next_frame);
exception_object.cache.rethrown(true);

// TODO(35): Replace this with an immediate call to unwind_frame(). What we
// have below is fragile and can break very easily.
#if defined(OPTIMIZATION_LEVEL)
// Perform an inline trivial unwind __cxa_throw:
#if OPTIMIZATION_LEVEL == Debug
std::uint32_t const* stack_pointer = *exception_object.cpu.sp;
exception_object.cpu.r3 = stack_pointer[0];
exception_object.cpu.pc = stack_pointer[1];
exception_object.cpu.sp = stack_pointer + 2;
#elif OPTIMIZATION_LEVEL == MinSizeRel
std::uint32_t const* stack_pointer = *exception_object.cpu.sp;
exception_object.cpu.r4 = stack_pointer[0];
exception_object.cpu.pc = stack_pointer[1];
exception_object.cpu.sp = stack_pointer + 2;
#elif OPTIMIZATION_LEVEL == Release
std::uint32_t const* stack_pointer = *exception_object.cpu.sp;
exception_object.cpu.r3 = stack_pointer[0];
exception_object.cpu.pc = stack_pointer[1];
exception_object.cpu.sp = stack_pointer + 2;
#elif OPTIMIZATION_LEVEL == RelWithDebInfo
#error "Sorry Release mode unwinding is not supported yet.";
#endif
#endif

// This must ALWAYS evaluate to false. But since the variable is volatile,
// the compiler will not optimize it away and thus, __wrap___cxa_throw will
// require unwind information. This prevents the compiler from optimizing
// the data away.
if (libhal_convince_compiler_to_emit_metadata) {
throw std::bad_alloc(); // What is thrown is not important, just that we
// throw something and since bad_alloc is a MUST
// have in the C++ throw RTTI list, might as well
// reuse it here.
}
// Raise exception returns when an error or call to terminate has been found
ke::raise_exception(exception_object);
// TODO(#38): this area is considered a catch block, meaning that the
Expand All @@ -2033,37 +2017,20 @@ extern "C"
ke::flatten_rtti<12>(
p_thrown_exception, exception_object.type_info, p_type_info);

// TODO(35): Replace this with an immediate call to unwind_frame(). What we
// have below is fragile and can break very easily.
#if defined(OPTIMIZATION_LEVEL)
// Perform an inline trivial unwind __cxa_throw:
#if OPTIMIZATION_LEVEL == Debug
std::uint32_t const* stack_pointer = *exception_object.cpu.sp;
exception_object.cpu.r3 = stack_pointer[0];
exception_object.cpu.r4 = stack_pointer[1];
exception_object.cpu.r5 = stack_pointer[2];
exception_object.cpu.pc = stack_pointer[3];
exception_object.cpu.sp = stack_pointer + 4;
#elif OPTIMIZATION_LEVEL == MinSizeRel
#elif OPTIMIZATION_LEVEL == MinSizeRel
std::uint32_t const* stack_pointer = *exception_object.cpu.sp;
exception_object.cpu.r4 = stack_pointer[0];
exception_object.cpu.pc = stack_pointer[1];
exception_object.cpu.sp = stack_pointer + 2;
#elif OPTIMIZATION_LEVEL == Release
std::uint32_t const* stack_pointer = *exception_object.cpu.sp;
exception_object.cpu.r3 = stack_pointer[0];
exception_object.cpu.r4 = stack_pointer[1];
exception_object.cpu.r5 = stack_pointer[2];
exception_object.cpu.pc = stack_pointer[3];
exception_object.cpu.sp = stack_pointer + 4;
#elif OPTIMIZATION_LEVEL == RelWithDebInfo
#error "Sorry Release mode unwinding is not supported yet.";
#endif
#endif

// This must ALWAYS evaluate to false. But since the variable is volatile,
// the compiler will not optimize it away and thus, __wrap___cxa_throw will
// require unwind information. This prevents the compiler from optimizing
// the data away.
if (libhal_convince_compiler_to_emit_metadata) {
throw std::bad_alloc(); // What is thrown is not important, just that we
// throw something and since bad_alloc is a MUST
// have in the C++ throw RTTI list, might as well
// reuse it here.
}
// Raise exception returns when an error or call to terminate has been found
ke::raise_exception(exception_object);
// TODO(#38): this area is considered a catch block, meaning that the
// exception is handled at this point. We should mark it as such.
std::terminate();
}
} // extern "C"

0 comments on commit 9eadd90

Please sign in to comment.