cxx: Add KConfig option for C++ exceptions, disable by default

Fixes https://github.com/espressif/esp-idf/issues/1072

(Additional 20KB is still used if C++ exception support is enabled in
menuconfig.)
This commit is contained in:
Angus Gratton 2017-10-04 17:29:21 +11:00 committed by Angus Gratton
parent 6f07e0797d
commit 9c7477ef34
6 changed files with 122 additions and 1 deletions

14
Kconfig
View File

@ -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"

View 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

View File

@ -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

View 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

View 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) {

View 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