]> granicus.if.org Git - esp-idf/commitdiff
cxx: Add KConfig option for C++ exceptions, disable by default
authorAngus Gratton <angus@espressif.com>
Wed, 4 Oct 2017 06:29:21 +0000 (17:29 +1100)
committerAngus Gratton <gus@projectgus.com>
Tue, 17 Oct 2017 07:29:25 +0000 (15:29 +0800)
Fixes https://github.com/espressif/esp-idf/issues/1072

(Additional 20KB is still used if C++ exception support is enabled in
menuconfig.)

Kconfig
components/cxx/component.mk
components/cxx/cxx_exception_stubs.cpp [new file with mode: 0644]
components/cxx/test/test_cxx.cpp
components/esp32/cpu_start.c
make/project.mk

diff --git a/Kconfig b/Kconfig
index 2cf242481247ceb846e26093ea18d56bf19425c3..c641882f840048b9cc0a49c48f1082604ecbb532 100644 (file)
--- a/Kconfig
+++ b/Kconfig
@@ -93,7 +93,19 @@ config OPTIMIZATION_ASSERTIONS_DISABLED
 
 endchoice # assertions
 
-endmenu # Optimization level
+config CXX_EXCEPTIONS
+   bool "Enable C++ exceptions"
+   default n
+   help
+       Enabling this option compiles all IDF C++ files with exception support enabled.
+
+       Disabling this option disables C++ exception support in all compiled files, and any libstdc++ code which throws
+       an exception will abort instead.
+
+       Enabling this option currently adds an additional 20KB of heap overhead, and 4KB of additional heap is allocated
+       the first time an exception is thrown in user code.
+
+endmenu # Compiler Options
 
 menu "Component config"
 source "$COMPONENT_KCONFIGS"
index 5d563847795ca8bbd1fc75e498ea10f6b11bda3d..00e05e0db23f40ed56b492caec88e00ed4c03666 100644 (file)
@@ -1,3 +1,11 @@
 # Mark __cxa_guard_dummy as undefined so that implementation of static guards 
 # is taken from cxx_guards.o instead of libstdc++.a
 COMPONENT_ADD_LDFLAGS += -u __cxa_guard_dummy
+
+ifndef CONFIG_CXX_EXCEPTIONS
+# If exceptions are disabled, ensure our fatal exception
+# hooks are preferentially linked over libstdc++ which
+# has full exception support
+COMPONENT_ADD_LDFLAGS += -u __cxx_fatal_exception
+endif
+
diff --git a/components/cxx/cxx_exception_stubs.cpp b/components/cxx/cxx_exception_stubs.cpp
new file mode 100644 (file)
index 0000000..4c914b2
--- /dev/null
@@ -0,0 +1,87 @@
+#include <cstdlib>
+#include <cstdio>
+#include <exception>
+#include <bits/functexcept.h>
+#include <sdkconfig.h>
+
+#ifndef CONFIG_CXX_EXCEPTIONS
+
+const char *FATAL_EXCEPTION = "Fatal C++ exception: ";
+
+extern "C" void __cxx_fatal_exception(void)
+{
+    abort();
+}
+
+extern "C" void __cxx_fatal_exception_message(const char *msg)
+{
+    printf("%s%s\n", FATAL_EXCEPTION, msg);
+    abort();
+}
+
+extern "C" void __cxx_fatal_exception_int(int i)
+{
+    printf("%s (%d)\n", FATAL_EXCEPTION, i);
+    abort();
+}
+
+void std::__throw_bad_exception(void) __attribute__((alias("__cxx_fatal_exception")));
+
+void std::__throw_bad_alloc(void) __attribute__((alias("__cxx_fatal_exception")));
+
+void std::__throw_bad_cast(void) __attribute__((alias("__cxx_fatal_exception")));
+
+void std::__throw_bad_typeid(void) __attribute__((alias("__cxx_fatal_exception")));
+
+void std::__throw_logic_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_domain_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_invalid_argument(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_length_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_out_of_range(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_out_of_range_fmt(const char*, ...) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_runtime_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_range_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_overflow_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_underflow_error(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_ios_failure(const char*) __attribute__((alias("__cxx_fatal_exception_message")));
+
+void std::__throw_system_error(int) __attribute__((alias("__cxx_fatal_exception_int")));
+
+void std::__throw_bad_function_call(void) __attribute__((alias("__cxx_fatal_exception")));
+
+void std::__throw_future_error(int) __attribute__((alias("__cxx_fatal_exception_int")));
+
+
+/* The following definitions are needed because libstdc++ is also compiled with
+   __throw_exception_again defined to throw, and some other exception code in a few places.
+
+   This cause exception handler code to be emitted in the library even though it's mostly
+   unreachable (as any libstdc++ "throw" will first call one of the above stubs, which will abort).
+
+   If these are left out, a bunch of unwanted exception handler code is linked.
+
+   Note: these function prototypes are not correct.
+*/
+
+extern "C" void __cxa_allocate_exception(void) __attribute__((alias("__cxx_fatal_exception")));
+extern "C" void __cxa_begin_catch(void) __attribute__((alias("__cxx_fatal_exception")));
+extern "C" void __cxa_end_catch(void) __attribute__((alias("__cxx_fatal_exception")));
+extern "C" void __cxa_get_exception_ptr(void) __attribute__((alias("__cxx_fatal_exception")));
+extern "C" void __cxa_free_exception(void) __attribute__((alias("__cxx_fatal_exception")));
+extern "C" void __cxa_rethrow(void) __attribute__((alias("__cxx_fatal_exception")));
+extern "C" void __cxa_throw(void) __attribute__((alias("__cxx_fatal_exception")));
+extern "C" void __cxa_call_terminate(void) __attribute__((alias("__cxx_fatal_exception")));
+
+bool std::uncaught_exception() __attribute__((alias("__cxx_fatal_exception")));
+
+#endif // CONFIG_CXX_EXCEPTIONS
index af73d9ea9210e44d64ad8d1bb89f28c35cfc2a5a..743a6869ded0076305b34723b35e31d3aeab58b2 100644 (file)
@@ -188,8 +188,12 @@ TEST_CASE("before scheduler has started, static initializers work correctly", "[
     TEST_ASSERT_EQUAL(2, StaticInitTestBeforeScheduler::order);
 }
 
+#ifdef CONFIG_CXX_EXCEPTIONS
+
 TEST_CASE("c++ exceptions work", "[cxx]")
 {
+    /* Note: This test currently trips the memory leak threshold
+       as libunwind allocates ~4KB of data on first exception. */
     int thrown_value;
     try
     {
@@ -203,6 +207,8 @@ TEST_CASE("c++ exceptions work", "[cxx]")
     printf("OK?\n");
 }
 
+#endif
+
 /* These test cases pull a lot of code from libstdc++ and are disabled for now
  */
 #if 0
index 3aefdce0a421166e447915b7973f62401a92ba4d..ee0edc2e23669de43ec9feae02d47520bf4f7577 100644 (file)
@@ -379,8 +379,10 @@ void start_cpu1_default(void)
 
 static void do_global_ctors(void)
 {
+#ifdef CONFIG_CXX_EXCEPTIONS
     static struct object ob;
     __register_frame_info( __eh_frame, &ob );
+#endif
 
     void (**p)(void);
     for (p = &__init_array_end - 1; p >= &__init_array_start; --p) {
index 60ecce3ec55e6c407459e5cd920030c668322036..deaba74a3aefc3b95cf2fd86e93359ede1f7bea6 100644 (file)
@@ -303,6 +303,12 @@ CXXFLAGS := $(strip \
        $(CXXFLAGS) \
        $(EXTRA_CXXFLAGS))
 
+ifdef CONFIG_CXX_EXCEPTIONS
+CXXFLAGS += -fexceptions
+else
+CXXFLAGS += -fno-exceptions
+endif
+
 export CFLAGS CPPFLAGS CXXFLAGS
 
 # Set host compiler and binutils