From 2552c7ba0f194af084ac2ff6799345a13b8b419b Mon Sep 17 00:00:00 2001 From: Jakob Hasse Date: Sun, 7 Feb 2021 16:51:52 +0800 Subject: [PATCH] [C++]: wrapper functions around unwind code * Replaced all C++ exception related functions with wrappers if -fno-exception is used. This prevents linking of the corresponding code in libgcc. The code size will decrease by around 7-9 KB when building with -fno-exception. * added no except test app Closes https://github.com/espressif/esp-idf/pull/5380 Closes https://github.com/espressif/esp-idf/issues/5363 Closes https://github.com/espressif/esp-idf/issues/5224 Closes IDFGH-3153 Closes IDF-2577 --- components/cxx/CMakeLists.txt | 48 ++++- components/cxx/component.mk | 7 +- components/cxx/cxx_exception_stubs.cpp | 186 ++++++++++++++---- .../system/cxx_no_except/CMakeLists.txt | 6 + .../test_apps/system/cxx_no_except/README.md | 2 + .../system/cxx_no_except/main/CMakeLists.txt | 2 + .../system/cxx_no_except/main/main.cpp | 27 +++ .../system/cxx_no_except/sdkconfig.ci.riscv | 2 + .../system/cxx_no_except/sdkconfig.ci.xtensa | 2 + 9 files changed, 246 insertions(+), 36 deletions(-) create mode 100644 tools/test_apps/system/cxx_no_except/CMakeLists.txt create mode 100644 tools/test_apps/system/cxx_no_except/README.md create mode 100644 tools/test_apps/system/cxx_no_except/main/CMakeLists.txt create mode 100644 tools/test_apps/system/cxx_no_except/main/main.cpp create mode 100644 tools/test_apps/system/cxx_no_except/sdkconfig.ci.riscv create mode 100644 tools/test_apps/system/cxx_no_except/sdkconfig.ci.xtensa diff --git a/components/cxx/CMakeLists.txt b/components/cxx/CMakeLists.txt index ff2d0e4389..920cce1d9d 100644 --- a/components/cxx/CMakeLists.txt +++ b/components/cxx/CMakeLists.txt @@ -3,14 +3,58 @@ idf_component_register(SRCS "cxx_exception_stubs.cpp" # Make sure that pthread is in component list PRIV_REQUIRES pthread) +if(NOT CONFIG_CXX_EXCEPTIONS) + set(WRAP_FUNCTIONS + _Unwind_SetEnableExceptionFdeSorting + __register_frame_info_bases + __register_frame_info + __register_frame + __register_frame_info_table_bases + __register_frame_info_table + __register_frame_table + __deregister_frame_info_bases + __deregister_frame_info + _Unwind_Find_FDE + _Unwind_GetGR + _Unwind_GetCFA + _Unwind_GetIP + _Unwind_GetIPInfo + _Unwind_GetRegionStart + _Unwind_GetDataRelBase + _Unwind_GetTextRelBase + _Unwind_SetIP + _Unwind_SetGR + _Unwind_GetLanguageSpecificData + _Unwind_FindEnclosingFunction + _Unwind_Resume + _Unwind_RaiseException + _Unwind_DeleteException + _Unwind_ForcedUnwind + _Unwind_Resume_or_Rethrow + _Unwind_Backtrace + __cxa_call_unexpected + __gxx_personality_v0) + + foreach(wrap ${WRAP_FUNCTIONS}) + target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=${wrap}") + endforeach() +endif() + target_link_libraries(${COMPONENT_LIB} PUBLIC stdc++ gcc) target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __cxa_guard_dummy") -# Force pthread to also appear later than stdc++ in link line -add_library(stdcpp_pthread INTERFACE) +# Force libpthread to appear later than libstdc++ in link line since libstdc++ depends on libpthread. +# Furthermore, force libcxx to appear later than libgcc because some libgcc unwind code is wrapped, if C++ +# exceptions are disabled. libcxx (this component) provides the unwind code wrappers. +# This is to prevent linking of libgcc's unwind code which considerably increases the binary size. idf_component_get_property(pthread pthread COMPONENT_LIB) +idf_component_get_property(cxx cxx COMPONENT_LIB) +add_library(stdcpp_pthread INTERFACE) target_link_libraries(stdcpp_pthread INTERFACE stdc++ $) target_link_libraries(${COMPONENT_LIB} PUBLIC stdcpp_pthread) +add_library(libgcc_cxx INTERFACE) +target_link_libraries(libgcc_cxx INTERFACE gcc $) +target_link_libraries(${COMPONENT_LIB} PUBLIC libgcc_cxx) if(NOT CONFIG_COMPILER_CXX_EXCEPTIONS) target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __cxx_fatal_exception") diff --git a/components/cxx/component.mk b/components/cxx/component.mk index 5b2073955a..7b5a1d0a6e 100644 --- a/components/cxx/component.mk +++ b/components/cxx/component.mk @@ -6,7 +6,12 @@ ifndef CONFIG_COMPILER_CXX_EXCEPTIONS # If exceptions are disabled, ensure our fatal exception # hooks are preferentially linked over libstdc++ which # has full exception support +WRAP_FUNCTIONS = _Unwind_SetEnableExceptionFdeSorting __register_frame_info_bases __register_frame_info __register_frame __register_frame_info_table_bases __register_frame_info_table __register_frame_table __deregister_frame_info_bases __deregister_frame_info _Unwind_Find_FDE _Unwind_GetGR _Unwind_GetCFA _Unwind_GetIP _Unwind_GetIPInfo _Unwind_GetRegionStart _Unwind_GetDataRelBase _Unwind_GetTextRelBase _Unwind_SetIP _Unwind_SetGR _Unwind_GetLanguageSpecificData _Unwind_FindEnclosingFunction _Unwind_Resume _Unwind_RaiseException _Unwind_DeleteException _Unwind_ForcedUnwind _Unwind_Resume_or_Rethrow _Unwind_Backtrace __cxa_call_unexpected __gxx_personality_v0 +WRAP_ARGUMENT := -Wl,--wrap= + +COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) $(addprefix $(WRAP_ARGUMENT),$(WRAP_FUNCTIONS)) COMPONENT_ADD_LDFLAGS += -u __cxx_fatal_exception -endif + +endif # CONFIG_COMPILER_CXX_EXCEPTIONS COMPONENT_ADD_INCLUDEDIRS = diff --git a/components/cxx/cxx_exception_stubs.cpp b/components/cxx/cxx_exception_stubs.cpp index 8c0de4bcce..01b6f5884d 100644 --- a/components/cxx/cxx_exception_stubs.cpp +++ b/components/cxx/cxx_exception_stubs.cpp @@ -6,58 +6,178 @@ #ifndef CONFIG_COMPILER_CXX_EXCEPTIONS -const char *FATAL_EXCEPTION = "Fatal C++ exception: "; - -extern "C" void __cxx_fatal_exception(void) +extern "C" void abort_expect_void(const void *context) { abort(); } -extern "C" bool __cxx_fatal_exception_bool(void) +extern "C" void *abort_expect_void_and_return(const void *context) { - __cxx_fatal_exception(); - return false; + abort(); + return nullptr; } -extern "C" void __cxx_fatal_exception_message(const char *msg) +extern "C" void *forward_abort_uw_ctx(struct _Unwind_Context *context) +{ + return abort_expect_void_and_return((void*) context); +} + +template +static T abort_return() +{ + abort(); + return static_cast(0); +} + +// unwind-dw2-fde.o +extern "C" void __wrap__Unwind_SetEnableExceptionFdeSorting(unsigned char enable) { - printf("%s%s\n", FATAL_EXCEPTION, msg); abort(); } -extern "C" void __cxx_fatal_exception_message_va(const char *msg, ...) +extern "C" void __wrap___register_frame_info_bases (const void *begin, struct object *ob, void *tbase, void *dbase) { - __cxx_fatal_exception_message(msg); -} - -extern "C" void __cxx_fatal_exception_int(int i) -{ - printf("%s (%d)\n", FATAL_EXCEPTION, i); abort(); } -/* 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. +extern "C" void __wrap___register_frame_info (const void *begin, struct object *ob) +{ + abort(); +} - 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). +extern "C" void __wrap___register_frame_info_table_bases (void *begin, struct object *ob, void *tbase, void *dbase) +{ + abort(); +} - If these are left out, a bunch of unwanted exception handler code is linked. +extern "C" void __wrap___register_frame_info_table (void *begin, struct object *ob) +{ + abort(); +} - Note: these function prototypes are not correct. -*/ -extern "C" void __cxa_allocate_exception(void) __attribute__((alias("__cxx_fatal_exception"))); -extern "C" void __cxa_allocate_dependent_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_free_dependent_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"))); +extern "C" void __wrap___register_frame (void *begin) + __attribute__((alias("abort_expect_void"))); -bool std::uncaught_exception() __attribute__((alias("__cxx_fatal_exception_bool"))); +extern "C" void __wrap___register_frame_table (void *begin) + __attribute__((alias("abort_expect_void"))); + +extern "C" void *__wrap___deregister_frame_info_bases (const void *begin) + __attribute__((alias("abort_expect_void_and_return"))); + +extern "C" void *__wrap___deregister_frame_info (const void *begin) + __attribute__((alias("abort_expect_void_and_return"))); + +extern "C" void __wrap___deregister_frame (void *begin) + __attribute__((alias("abort_expect_void"))); + +typedef void* fde; + +extern "C" const fde * __wrap__Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases) +{ + return abort_return(); +} + +// unwind-dw2.o (riscv), unwind-dw2-xtensa.o (xtensa) +typedef void* _Unwind_Ptr; +typedef int _Unwind_Word; + +extern "C" _Unwind_Word __wrap__Unwind_GetGR (struct _Unwind_Context *context, int index) +{ + return abort_return<_Unwind_Word>(); +} + +extern "C" _Unwind_Word __wrap__Unwind_GetCFA (struct _Unwind_Context *context) +{ + return abort_return<_Unwind_Word>(); +} + +extern "C" void __wrap__Unwind_SetIP (struct _Unwind_Context *context, _Unwind_Ptr val) +{ + abort(); +} + +extern "C" void __wrap__Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) +{ + abort(); +} + +extern "C" _Unwind_Ptr __wrap__Unwind_GetIPInfo (struct _Unwind_Context *context, int *ip_before_insn) +{ + return abort_return<_Unwind_Ptr>(); +} + +extern "C" _Unwind_Ptr __wrap__Unwind_GetIP (struct _Unwind_Context *context) + __attribute__((alias("forward_abort_uw_ctx"))); + +extern "C" _Unwind_Ptr __wrap__Unwind_GetRegionStart (struct _Unwind_Context *context) + __attribute__((alias("forward_abort_uw_ctx"))); + +extern "C" _Unwind_Ptr __wrap__Unwind_GetDataRelBase (struct _Unwind_Context *context) + __attribute__((alias("forward_abort_uw_ctx"))); + +extern "C" _Unwind_Ptr __wrap__Unwind_GetTextRelBase (struct _Unwind_Context *context) + __attribute__((alias("forward_abort_uw_ctx"))); + +extern "C" void *__wrap__Unwind_GetLanguageSpecificData (struct _Unwind_Context *context) + __attribute__((alias("forward_abort_uw_ctx"))); + +extern "C" void *__wrap__Unwind_FindEnclosingFunction (void *pc) + __attribute__((alias("abort_expect_void_and_return"))); + +struct frame_state *__frame_state_for (void *pc_target, struct frame_state *state_in) +{ + return abort_return(); +} + +// unwind.inc +typedef int _Unwind_Reason_Code; +typedef int _Unwind_Action; +typedef int _Unwind_Exception_Class; +typedef int* _Unwind_Trace_Fn; +typedef int* _Unwind_Stop_Fn; + +extern "C" void __wrap__Unwind_Resume (struct _Unwind_Exception *exc) +{ + abort(); +} + +extern "C" void __wrap__Unwind_DeleteException (struct _Unwind_Exception *exc) +{ + abort(); +} + + +extern "C" _Unwind_Reason_Code __wrap__Unwind_RaiseException(struct _Unwind_Exception *exc) +{ + return abort_return<_Unwind_Reason_Code>(); +} + +extern "C" _Unwind_Reason_Code __wrap__Unwind_Resume_or_Rethrow (struct _Unwind_Exception *exc) + __attribute__((alias("__wrap__Unwind_RaiseException"))); + +extern "C" _Unwind_Reason_Code __wrap__Unwind_ForcedUnwind (struct _Unwind_Exception *exc, + _Unwind_Stop_Fn stop, void * stop_argument) +{ + return abort_return<_Unwind_Reason_Code>(); +} + +extern "C" _Unwind_Reason_Code __wrap__Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument) +{ + return abort_return<_Unwind_Reason_Code>(); +} + +// eh_personality.o +extern "C" void __wrap___cxa_call_unexpected (void *exc_obj_in) + __attribute__((alias("abort_expect_void"))); + +extern "C" _Unwind_Reason_Code __wrap___gxx_personality_v0 (int version, + _Unwind_Action actions, + _Unwind_Exception_Class exception_class, + struct _Unwind_Exception *ue_header, + struct _Unwind_Context *context) +{ + return abort_return<_Unwind_Reason_Code>(); +} #endif // CONFIG_COMPILER_CXX_EXCEPTIONS diff --git a/tools/test_apps/system/cxx_no_except/CMakeLists.txt b/tools/test_apps/system/cxx_no_except/CMakeLists.txt new file mode 100644 index 0000000000..6393f7939c --- /dev/null +++ b/tools/test_apps/system/cxx_no_except/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(cxx_no_except) diff --git a/tools/test_apps/system/cxx_no_except/README.md b/tools/test_apps/system/cxx_no_except/README.md new file mode 100644 index 0000000000..1fb88efd15 --- /dev/null +++ b/tools/test_apps/system/cxx_no_except/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C3 | +| ----------------- | ----- | -------- | diff --git a/tools/test_apps/system/cxx_no_except/main/CMakeLists.txt b/tools/test_apps/system/cxx_no_except/main/CMakeLists.txt new file mode 100644 index 0000000000..9ca01d0fbb --- /dev/null +++ b/tools/test_apps/system/cxx_no_except/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.cpp" + INCLUDE_DIRS ".") diff --git a/tools/test_apps/system/cxx_no_except/main/main.cpp b/tools/test_apps/system/cxx_no_except/main/main.cpp new file mode 100644 index 0000000000..1d7273a6ca --- /dev/null +++ b/tools/test_apps/system/cxx_no_except/main/main.cpp @@ -0,0 +1,27 @@ +/* + No except example. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "esp_system.h" +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +extern "C" void app_main() +{ + char *char_array = new (std::nothrow) char [47]; + + for (int i = 10; i >= 0; i--) { + char_array[i] = i; + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + esp_restart(); + + delete [] char_array; +} diff --git a/tools/test_apps/system/cxx_no_except/sdkconfig.ci.riscv b/tools/test_apps/system/cxx_no_except/sdkconfig.ci.riscv new file mode 100644 index 0000000000..d623c48528 --- /dev/null +++ b/tools/test_apps/system/cxx_no_except/sdkconfig.ci.riscv @@ -0,0 +1,2 @@ +CONFIG_IDF_TARGET="esp32c3" +CONFIG_COMPILER_CXX_EXCEPTIONS=n diff --git a/tools/test_apps/system/cxx_no_except/sdkconfig.ci.xtensa b/tools/test_apps/system/cxx_no_except/sdkconfig.ci.xtensa new file mode 100644 index 0000000000..5a0b70e900 --- /dev/null +++ b/tools/test_apps/system/cxx_no_except/sdkconfig.ci.xtensa @@ -0,0 +1,2 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_COMPILER_CXX_EXCEPTIONS=n