1616#include < cstdint>
1717
1818#include < algorithm>
19+ #include < array>
1920#include < exception>
21+ #include < memory_resource>
2022
2123#include < libhal-exceptions/control.hpp>
2224
25+ namespace {
26+
27+ // Size of the GCC exception object header is 128 bytes. Will have to update
28+ // this if the size of the EO increases. 😅
29+ // Might need to add some GCC macro flags here to keep track of all of the
30+ // EO sizes over the versions.
31+ constexpr size_t gcc_header_size = 128 ;
32+
33+ template <typename T = std::byte>
34+ struct exception_allocation
35+ {
36+ std::size_t size;
37+ std::array<std::byte, 256 > header;
38+ alignas (std::max_align_t ) T data;
39+ };
40+
41+ template <typename T = std::byte>
42+ exception_allocation<T>* get_allocation_from_exception (void * p_exception_ptr)
43+ {
44+ auto const member_offset = offsetof (exception_allocation<T>, data);
45+ auto const exception_bytes = static_cast <std::byte*>(p_exception_ptr);
46+ return reinterpret_cast <exception_allocation<T>*>(exception_bytes -
47+ member_offset);
48+ }
49+
50+ } // namespace
51+
2352extern " C"
2453{
2554 void _exit ([[maybe_unused]] int rc) // NOLINT
2655 {
2756 std::terminate ();
2857 }
2958
30- // Size of the GCC exception object header is 128 bytes. Will have to update
31- // this if the size of the EO increases. 😅
32- // Might need to add some GCC macro flags here to keep track of all of the
33- // EO sizes over the versions.
34- constexpr size_t gcc_header_size = 128 ;
35- constexpr auto header_size = sizeof (std::size_t ) + gcc_header_size;
36-
3759 void * __wrap___cxa_allocate_exception (std::size_t p_size) noexcept // NOLINT
3860 {
39- auto const total_allocation = header_size + p_size;
40- auto * exception_memory = reinterpret_cast <std::uint8_t *>(
41- hal::get_exception_allocator ().allocate (total_allocation));
4261
43- if (exception_memory == nullptr ) {
62+ auto const allocation_amount = sizeof (exception_allocation<>) + p_size;
63+ auto exception_memory_resource = &hal::get_exception_allocator ();
64+
65+ auto * object = static_cast <exception_allocation<>*>(
66+ exception_memory_resource->allocate (allocation_amount));
67+
68+ if (object == nullptr ) {
4469 std::terminate ();
4570 }
4671
4772 // Required for GCC's impl to work correctly as it assumes that all bytes
4873 // default to 0.
49- std::fill_n (exception_memory, total_allocation, 0U );
50-
51- // Set the first std::size_t bytes to the header size for deallocation
52- reinterpret_cast <std::size_t *>(exception_memory)[0 ] = total_allocation;
53-
54- // Return the pointer to the memory after the header, which is the start of
55- // the thrown object's allocated memory.
56- return exception_memory + header_size;
74+ object->header .fill (std::byte{ 0 });
75+ object->size = allocation_amount;
76+ return &object->data ;
5777 }
5878
5979 void __wrap___cxa_call_unexpected (void *) // NOLINT
@@ -63,9 +83,89 @@ extern "C"
6383
6484 void __wrap___cxa_free_exception (void * p_exception) noexcept // NOLINT
6585 {
66- auto * exception = reinterpret_cast <std::uint8_t *>(p_exception);
67- auto * original_pointer = exception - header_size;
68- auto exception_size = *reinterpret_cast <std::size_t *>(original_pointer);
69- hal::get_exception_allocator ().deallocate (original_pointer, exception_size);
86+ auto * object = get_allocation_from_exception (p_exception);
87+ hal::get_exception_allocator ().deallocate (object, object->size );
7088 }
7189} // extern "C"
90+
91+ namespace __cxxabiv1 { // NOLINT
92+ std::terminate_handler __terminate_handler = +[]() { // NOLINT
93+ while (true ) {
94+ continue ;
95+ }
96+ };
97+ } // namespace __cxxabiv1
98+
99+ namespace hal {
100+ std::terminate_handler set_terminate (
101+ std::terminate_handler p_terminate_handler) noexcept
102+ {
103+ auto copy = __cxxabiv1::__terminate_handler;
104+ __cxxabiv1::__terminate_handler = p_terminate_handler;
105+ return copy;
106+ }
107+
108+ std::terminate_handler get_terminate () noexcept
109+ {
110+ return __cxxabiv1::__terminate_handler;
111+ }
112+
113+ /* *
114+ * @brief Simple exception allocator
115+ *
116+ * This allocator can only allocates space for a single exception object at a
117+ * time.
118+ */
119+ class single_exception_allocator : public std ::pmr::memory_resource
120+ {
121+ public:
122+ single_exception_allocator () = default ;
123+ ~single_exception_allocator () override = default ;
124+
125+ private:
126+ void * do_allocate (std::size_t p_size, // NOLINT
127+ [[maybe_unused]] std::size_t p_alignment) override
128+ {
129+ if (m_allocated || p_size > m_buffer.size ()) {
130+ return nullptr ;
131+ }
132+ m_allocated = true ;
133+ return m_buffer.data ();
134+ }
135+
136+ void do_deallocate (void * p_address,
137+ [[maybe_unused]] std::size_t p_size, // NOLINT
138+ [[maybe_unused]] std::size_t p_alignment) override
139+ {
140+ if (p_address != m_buffer.data ()) {
141+ std::terminate ();
142+ }
143+ m_allocated = false ;
144+ }
145+
146+ [[nodiscard]] bool do_is_equal (
147+ std::pmr::memory_resource const & other) const noexcept override
148+ {
149+ return this == &other;
150+ }
151+
152+ alignas (std::max_align_t ) std::array<std::uint8_t , 256 > m_buffer{};
153+ bool m_allocated = false ;
154+ };
155+
156+ // TODO(#11): Add macro to IFDEF this out if the user want to save 64 bytes.
157+ // NOLINTNEXTLINE
158+ single_exception_allocator _default_allocator{};
159+ // NOLINTNEXTLINE
160+ std::pmr::memory_resource* _exception_allocator = &_default_allocator;
161+
162+ void set_exception_allocator (std::pmr::memory_resource& p_allocator) noexcept
163+ {
164+ _exception_allocator = &p_allocator;
165+ }
166+
167+ std::pmr::memory_resource& get_exception_allocator () noexcept
168+ {
169+ return *_exception_allocator;
170+ }
171+ } // namespace hal
0 commit comments