Browse Source

[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
Jakob Hasse 5 years ago
parent
commit
dd08575c29

+ 46 - 2
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_FILE:${pthread}>)
 target_link_libraries(${COMPONENT_LIB} PUBLIC stdcpp_pthread)
+add_library(libgcc_cxx INTERFACE)
+target_link_libraries(libgcc_cxx INTERFACE gcc $<TARGET_FILE:${cxx}>)
+target_link_libraries(${COMPONENT_LIB} PUBLIC libgcc_cxx)
 
 if(NOT CONFIG_COMPILER_CXX_EXCEPTIONS)
     target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __cxx_fatal_exception")

+ 6 - 1
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 =

+ 149 - 29
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 abort_expect_void(const void *context)
+{
+    abort();
+}
+
+extern "C" void *abort_expect_void_and_return(const void *context)
+{
+    abort();
+    return nullptr;
+}
+
+extern "C" void *forward_abort_uw_ctx(struct _Unwind_Context *context)
+{
+    return abort_expect_void_and_return((void*) context);
+}
 
-extern "C" void __cxx_fatal_exception(void)
+template<typename T>
+static T abort_return()
 {
     abort();
+    return static_cast<T>(0);
 }
 
-extern "C" bool __cxx_fatal_exception_bool(void)
+// unwind-dw2-fde.o
+extern "C" void __wrap__Unwind_SetEnableExceptionFdeSorting(unsigned char enable)
 {
-    __cxx_fatal_exception();
-    return false;
+    abort();
 }
 
-extern "C" void __cxx_fatal_exception_message(const char *msg)
+extern "C" void __wrap___register_frame_info_bases (const void *begin, struct object *ob, void *tbase, void *dbase)
 {
-    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 (const void *begin, struct object *ob)
 {
-    __cxx_fatal_exception_message(msg);
+    abort();
 }
 
-extern "C" void __cxx_fatal_exception_int(int i)
+extern "C" void __wrap___register_frame_info_table_bases (void *begin, struct object *ob, void *tbase, void *dbase)
 {
-    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_table (void *begin, struct object *ob)
+{
+    abort();
+}
+
+
+extern "C" void __wrap___register_frame (void *begin)
+        __attribute__((alias("abort_expect_void")));
+
+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<fde*>();
+}
+
+// unwind-dw2.o (riscv), unwind-dw2-xtensa.o (xtensa)
+typedef void* _Unwind_Ptr;
+typedef int _Unwind_Word;
 
-   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" _Unwind_Word __wrap__Unwind_GetGR (struct _Unwind_Context *context, int index)
+{
+    return abort_return<_Unwind_Word>();
+}
 
-   If these are left out, a bunch of unwanted exception handler code is linked.
+extern "C" _Unwind_Word __wrap__Unwind_GetCFA (struct _Unwind_Context *context)
+{
+    return abort_return<_Unwind_Word>();
+}
 
-   Note: these function prototypes are not correct.
-*/
+extern "C" void __wrap__Unwind_SetIP (struct _Unwind_Context *context, _Unwind_Ptr val)
+{
+    abort();
+}
 
-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__Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val)
+{
+    abort();
+}
 
-bool std::uncaught_exception() __attribute__((alias("__cxx_fatal_exception_bool")));
+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<struct frame_state *>();
+}
+
+// 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

+ 6 - 0
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)

+ 2 - 0
tools/test_apps/system/cxx_no_except/README.md

@@ -0,0 +1,2 @@
+| Supported Targets | ESP32 |
+| ----------------- | ----- |

+ 2 - 0
tools/test_apps/system/cxx_no_except/main/CMakeLists.txt

@@ -0,0 +1,2 @@
+idf_component_register(SRCS "main.cpp"
+                       INCLUDE_DIRS ".")

+ 27 - 0
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 <new>
+#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;
+}

+ 2 - 0
tools/test_apps/system/cxx_no_except/sdkconfig.ci.xtensa

@@ -0,0 +1,2 @@
+CONFIG_IDF_TARGET="esp32"
+CONFIG_COMPILER_CXX_EXCEPTIONS=n