Skip to content

Commit 9c7477e

Browse files
committed
cxx: Add KConfig option for C++ exceptions, disable by default
Fixes espressif#1072 (Additional 20KB is still used if C++ exception support is enabled in menuconfig.)
1 parent 6f07e07 commit 9c7477e

File tree

6 files changed

+122
-1
lines changed

6 files changed

+122
-1
lines changed

Kconfig

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,19 @@ config OPTIMIZATION_ASSERTIONS_DISABLED
9393

9494
endchoice # assertions
9595

96-
endmenu # Optimization level
96+
config CXX_EXCEPTIONS
97+
bool "Enable C++ exceptions"
98+
default n
99+
help
100+
Enabling this option compiles all IDF C++ files with exception support enabled.
101+
102+
Disabling this option disables C++ exception support in all compiled files, and any libstdc++ code which throws
103+
an exception will abort instead.
104+
105+
Enabling this option currently adds an additional 20KB of heap overhead, and 4KB of additional heap is allocated
106+
the first time an exception is thrown in user code.
107+
108+
endmenu # Compiler Options
97109

98110
menu "Component config"
99111
source "$COMPONENT_KCONFIGS"

components/cxx/component.mk

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
# Mark __cxa_guard_dummy as undefined so that implementation of static guards
22
# is taken from cxx_guards.o instead of libstdc++.a
33
COMPONENT_ADD_LDFLAGS += -u __cxa_guard_dummy
4+
5+
ifndef CONFIG_CXX_EXCEPTIONS
6+
# If exceptions are disabled, ensure our fatal exception
7+
# hooks are preferentially linked over libstdc++ which
8+
# has full exception support
9+
COMPONENT_ADD_LDFLAGS += -u __cxx_fatal_exception
10+
endif
11+
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include <cstdlib>
2+
#include <cstdio>
3+
#include <exception>
4+
#include <bits/functexcept.h>
5+
#include <sdkconfig.h>
6+
7+
#ifndef CONFIG_CXX_EXCEPTIONS
8+
9+
const char *FATAL_EXCEPTION = "Fatal C++ exception: ";
10+
11+
extern "C" void __cxx_fatal_exception(void)
12+
{
13+
abort();
14+
}
15+
16+
extern "C" void __cxx_fatal_exception_message(const char *msg)
17+
{
18+
printf("%s%s\n", FATAL_EXCEPTION, msg);
19+
abort();
20+
}
21+
22+
extern "C" void __cxx_fatal_exception_int(int i)
23+
{
24+
printf("%s (%d)\n", FATAL_EXCEPTION, i);
25+
abort();
26+
}
27+
28+
void std::__throw_bad_exception(void) __attribute__((alias("__cxx_fatal_exception")));
29+
30+
void std::__throw_bad_alloc(void) __attribute__((alias("__cxx_fatal_exception")));
31+
32+
void std::__throw_bad_cast(void) __attribute__((alias("__cxx_fatal_exception")));
33+
34+
void std::__throw_bad_typeid(void) __attribute__((alias("__cxx_fatal_exception")));
35+
36+
void std::__throw_logic_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
37+
38+
void std::__throw_domain_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
39+
40+
void std::__throw_invalid_argument(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
41+
42+
void std::__throw_length_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
43+
44+
void std::__throw_out_of_range(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
45+
46+
void std::__throw_out_of_range_fmt(const char*, ...) __attribute__((alias("__cxx_fatal_exception_message")));
47+
48+
void std::__throw_runtime_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
49+
50+
void std::__throw_range_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
51+
52+
void std::__throw_overflow_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
53+
54+
void std::__throw_underflow_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
55+
56+
void std::__throw_ios_failure(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
57+
58+
void std::__throw_system_error(int) __attribute__((alias("__cxx_fatal_exception_int")));
59+
60+
void std::__throw_bad_function_call(void) __attribute__((alias("__cxx_fatal_exception")));
61+
62+
void std::__throw_future_error(int) __attribute__((alias("__cxx_fatal_exception_int")));
63+
64+
65+
/* The following definitions are needed because libstdc++ is also compiled with
66+
__throw_exception_again defined to throw, and some other exception code in a few places.
67+
68+
This cause exception handler code to be emitted in the library even though it's mostly
69+
unreachable (as any libstdc++ "throw" will first call one of the above stubs, which will abort).
70+
71+
If these are left out, a bunch of unwanted exception handler code is linked.
72+
73+
Note: these function prototypes are not correct.
74+
*/
75+
76+
extern "C" void __cxa_allocate_exception(void) __attribute__((alias("__cxx_fatal_exception")));
77+
extern "C" void __cxa_begin_catch(void) __attribute__((alias("__cxx_fatal_exception")));
78+
extern "C" void __cxa_end_catch(void) __attribute__((alias("__cxx_fatal_exception")));
79+
extern "C" void __cxa_get_exception_ptr(void) __attribute__((alias("__cxx_fatal_exception")));
80+
extern "C" void __cxa_free_exception(void) __attribute__((alias("__cxx_fatal_exception")));
81+
extern "C" void __cxa_rethrow(void) __attribute__((alias("__cxx_fatal_exception")));
82+
extern "C" void __cxa_throw(void) __attribute__((alias("__cxx_fatal_exception")));
83+
extern "C" void __cxa_call_terminate(void) __attribute__((alias("__cxx_fatal_exception")));
84+
85+
bool std::uncaught_exception() __attribute__((alias("__cxx_fatal_exception")));
86+
87+
#endif // CONFIG_CXX_EXCEPTIONS

components/cxx/test/test_cxx.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,12 @@ TEST_CASE("before scheduler has started, static initializers work correctly", "[
188188
TEST_ASSERT_EQUAL(2, StaticInitTestBeforeScheduler::order);
189189
}
190190

191+
#ifdef CONFIG_CXX_EXCEPTIONS
192+
191193
TEST_CASE("c++ exceptions work", "[cxx]")
192194
{
195+
/* Note: This test currently trips the memory leak threshold
196+
as libunwind allocates ~4KB of data on first exception. */
193197
int thrown_value;
194198
try
195199
{
@@ -203,6 +207,8 @@ TEST_CASE("c++ exceptions work", "[cxx]")
203207
printf("OK?\n");
204208
}
205209

210+
#endif
211+
206212
/* These test cases pull a lot of code from libstdc++ and are disabled for now
207213
*/
208214
#if 0

components/esp32/cpu_start.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,10 @@ void start_cpu1_default(void)
379379

380380
static void do_global_ctors(void)
381381
{
382+
#ifdef CONFIG_CXX_EXCEPTIONS
382383
static struct object ob;
383384
__register_frame_info( __eh_frame, &ob );
385+
#endif
384386

385387
void (**p)(void);
386388
for (p = &__init_array_end - 1; p >= &__init_array_start; --p) {

make/project.mk

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,12 @@ CXXFLAGS := $(strip \
303303
$(CXXFLAGS) \
304304
$(EXTRA_CXXFLAGS))
305305

306+
ifdef CONFIG_CXX_EXCEPTIONS
307+
CXXFLAGS += -fexceptions
308+
else
309+
CXXFLAGS += -fno-exceptions
310+
endif
311+
306312
export CFLAGS CPPFLAGS CXXFLAGS
307313

308314
# Set host compiler and binutils

0 commit comments

Comments
 (0)