I am working in C++ and wish to switch on exception handling. I compiled my C++ code by enabling the GCC flag called -fexceptions. I did this by editing my local component.mk and adding:
CXXFLAGS+=-fexceptions
The code seems to compile cleanly however when I run "make" for the ESP-IDF build system, we abort at the end with the following:
$ make
CXX BLEUtils.o
/home/kolban/esp32/esptest/apps/workspace/bt_test/components/cpp_utils/./BLEUtils.cpp:181:20:
AR libcpp_utils.a
LD app-template.elf
esptool.py v2.0-beta2
Traceback (most recent call last):
File "/home/kolban/esp32/esptest/esp-idf/components/esptool_py/esptool/esptool.py", line 2294, in <module>
main()
File "/home/kolban/esp32/esptest/esp-idf/components/esptool_py/esptool/esptool.py", line 2096, in main
operation_func(args)
File "/home/kolban/esp32/esptest/esp-idf/components/esptool_py/esptool/esptool.py", line 1732, in elf2image
image.save(args.output)
File "/home/kolban/esp32/esptest/esp-idf/components/esptool_py/esptool/esptool.py", line 1306, in save
assert (f.tell() + 8) % IROM_ALIGN == segment.addr % IROM_ALIGN
AssertionError
/home/kolban/esp32/esptest/esp-idf/components/esptool_py/Makefile.projbuild:49: recipe for target '/home/kolban/esp32/esptest/apps/workspace/bt_test/build/app-template.bin' failed
make: *** [/home/kolban/esp32/esptest/apps/workspace/bt_test/build/app-template.bin] Error 1
Unfortunately, I don't know how to make progress from here.
Attached to this post is the request app-template.elf which was added to a ZIP on linux using the "zip" command.
Thanks @nkolban . xtensa-esp32-elf-objdump -h app-template.elf shows that the gcc exception structures are being linked in to the default "flash" address space as separate sections:
7 .gcc_except_table._Z41__static_initialization_and_destruction_0ii 000001b5 4015db5d 4015db5d 000cc93d 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .gcc_except_table._ZL12uuidToString13esp_bt_uuid_t 0000002c 4015dd12 4015dd12 000ccaf2 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .gcc_except_table._ZL14gattIdToString13esp_gatt_id_t 00000031 4015dd3e 4015dd3e 000ccb1e 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .gcc_except_table._ZL35characteristic_properties_to_string20esp_gatt_char_prop_t 0000002b 4015dd6f 4015dd6f 000ccb4f 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
11 .gcc_except_table._ZN8BLEUtils15addressToStringENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE 0000002b 4015dd9a 4015dd9a 000ccb7a 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
12 .gcc_except_table._ZN8BLEUtils17registerByAddressENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEP9BLEDevice 00000020 4015ddc5 4015ddc5 000ccba5 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
13 .gcc_except_table._Z23bt_utils_log_gatt_event20esp_gattc_cb_event_thP24esp_ble_gattc_cb_param_t 000000a2 4015dde5 4015dde5 000ccbc5 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
etc, etc.
and this long list of additional flash-resident sections is what is causing the (fairly cryptic) IROM-related assertion in esptool.py.
The workaround may be as simple as adding .gcc_except_table.* to the list of IRAM sections in esp32.common.ld (not sure if it's safe to put them in flash, in case an exception occurs while flash cache is disabled.) Or perhaps there is some way to split the linkage of exception tables between those exception tables that belong to IRAM functions and those which do not.
I'm going to rope in @igrr here as I think this may be the tip of a larger iceberg of support that's required for C++ exceptions to work properly. Ivan have you already looked into this at all?
@projectgus is correct, there is more to exception support than adding exception tables sections to the linker script. We also need to have a separate build of libstdc++, as the current one is built with exceptions disabled.
A customer has recently requested this feature, I will be working on it.
@igrr , have you made any progress on exceptions support?
I have briefly looked at it - I changed the compiler flags, linker script. Now I am the point, when the functions from the ABI are correctly called during exception handling, however __cxa_throw calls __cxa terminate. I briefly looked to the API sources and it seems there is no possiblity to disable exception in ABI and therefore I assume there is something wrong with the unwinder library. Are my observation correct?
I am asking about your progress as I don't want to redo something somebody else did.
Sorry, this was preempted by other things.
Did you also rebuild GCC (and libgcc) without -fno-exceptions passed into CXX_FLAGS?
https://github.com/espressif/crosstool-NG/blob/xtensa-1.22.x/samples/xtensa-esp32-elf/crosstool.config#L15
Yes I have. I think it only enables/disables exceptions in libstdc++, it doesn't change implementation of ABI.
Looking at eh_throw.cc:60 the unwinder probably does not unwind and therefore std::terminate is hit. However this is only my current conjecture. Unfortunately, I don't have a JTAG debugger right now, so it hard for me what is actually going on.
My assumption was correct - ABI supports exceptions by default. Here is dump of __cxa_throw:
Dump of assembler code for function __cxxabiv1::__cxa_throw(void*, std::type_info*, void (*)(void*)):
0x400f7e3c <+0>: entry a1, 32
0x400f7e3f <+3>: call8 0x400f7410 <__cxxabiv1::__cxa_get_globals()>
0x400f7e42 <+6>: l32i a5, a10, 4
0x400f7e45 <+9>: movi a8, 1
0x400f7e48 <+12>: addi.n a5, a5, 1
0x400f7e4a <+14>: s32i.n a5, a10, 4
0x400f7e4c <+16>: addi a5, a2, -96
0x400f7e4f <+19>: s32i.n a8, a5, 0
0x400f7e51 <+21>: s32i.n a3, a5, 16
0x400f7e53 <+23>: s32i a4, a5, 20
0x400f7e56 <+26>: call8 0x400f76ec <std::get_unexpected()>
0x400f7e59 <+29>: s32i.n a10, a5, 24
0x400f7e5b <+31>: call8 0x400f76c4 <std::get_terminate()>
0x400f7e5e <+34>: l32r a8, 0x400d0648 <_stext+1584>
0x400f7e61 <+37>: l32r a9, 0x400d064c <_stext+1588>
0x400f7e64 <+40>: s32i a8, a5, 64
0x400f7e67 <+43>: l32r a8, 0x400d0650 <_stext+1592>
0x400f7e6a <+46>: addi a2, a2, -32
0x400f7e6d <+49>: s32i.n a10, a5, 28
0x400f7e6f <+51>: s32i a9, a5, 68
0x400f7e72 <+54>: s32i a8, a5, 72
0x400f7e75 <+57>: mov.n a10, a2
0x400f7e77 <+59>: call8 0x400f64d8 <_Unwind_RaiseException>
0x400f7e7a <+62>: mov.n a10, a2
0x400f7e7c <+64>: call8 0x400f7338 <__cxxabiv1::__cxa_begin_catch(void*)>
0x400f7e7f <+67>: call8 0x400f76d4 <std::terminate()>
It means:
_Unwind_RaiseException is used for unwinding_Unwind_RaiseException returns, therefore there is an error during unwindingSo far I have found out the unwinder returns _URC_END_OF_STACK so there are several options what's going on:
Finally, with the help of @jcmvbkbc, I have working C++ exceptions on ESP32. To make them work, two things need to be fixed:
__register_frame_info needs to be called before running global constructors.eh_frame needs to be fixedI will open PR for this, however I have few questions regarding these changes as I am first time contributor to ESP-IDF and I have no idea what your preferences are.
__register_frame_info needs to allocate a space for a structure defined in libgcc. The header is unavailable to ESP-IDF (it is present only during toolchain compilation). I see two solution, which one do you prefer?-fno-exceptions stays nearly the same. Instead of abort, std::terminate will be called. The binary size shouldn't also increase much, as all the exception-related machines is already in current version present, the only thing added will be the exception metadata.Sidenote: @igrr do you consider upgrading to GCC 6.3 for the toolchain? I have managed to compile the toolchain with GCC 6.3 using the newest CT-NG. It required only to take two patches of Espressif fork and to apply them to the current stable of CT-NG (+ 3 manual modifications to get the desired versions).
That's great progress, @yaqwsx.
My suggestion is to introduce a (disabled by default) Kconfig option for "C++ exception handling".
With this option enabled, we link against the (newly added) libgcc-fexceptions.a. Otherwise, we use the existing libgcc.a and add -fno-exceptions to CXXFLAGS.
Feel free to make a PR with part of the changes (linker script change and the call to __register_frame_info) if you don't feel comfortable modifying makefiles. I can make the makefile change and add the new libgcc version on top of your PR. I think that hardcoding size for __register_frame_info is okay.
With regards to GCC 6.3, we'll test our code against it, and if all goes well, we will upgrade for the next major version of SDK.
Please be mindful of code space use within the ESP32 framework and toolchain. Features like rtti and exceptions may not be desirable for everybody to the additional code they use, so these should always be optional and not add to the code space usage. The ESP32 is still an embedded platform with limited Flash and RAM.
Thanks @hwmaier, noted.
That is the reason I propose to make this a "disabled by default" option, and keep using the original (-fno-exceptions) libgcc unless exceptions support has been explicitly enabled.
@igrr: Ok, when I am done with work, I'll send a PR modifying startup function and linker script. I'll leave the rest up to you - thanks! Can I have also a request to add the possibility to enable RTTI?
@hwmaier I vote for having exceptions enabled by default (of course with the possibility to disable them). ESP32 is no a small platform anymore, the implementation of exception is zero-cost and most of the beginners with ESP32 are not going to reach flash limits and with exceptions enable by default they can easily use all features of C++. If a user wants to save memory, he is not a beginner anymore and he should be able to disable exceptions and RTTI.
Could you please provide a guide to activate the C++ exceptions?
Apply the patch from #681 and add -fexceptions and -frtti as compiler flags. This will enable exceptions in your project, however STL won't use exceptions. If you want STL with exception support, you need to compile toolchain using Espressif's Crosstool-NG (https://github.com/espressif/crosstool-NG). Before you compile it, remove -fno-exceptions and -fno-rtti flags flags from the configuration file (I can't remeber precise location, please find the occurence in the sources by yourself). That should be all.
@igrr is implementing necessary menu config modifications and merging of #681 planned any time soon?
Yes, sorry for the delay, we have several changes related to the toolchain staged for release now, including this one. The constraint here is that we want to have as few updates of toolchain as possible. The thing we are waiting for now is enabling C++ concurrency support (at least for mutexes) to fix non-thread-safe shared_ptr. I believe we can release new toolchain version (and hence be able to merge this change) in about two weeks.
noting here for future reference (and Google searchers) I was having build problems when I tried to use the latest code as of yesterday (a4fe12cb6d195a580a57690ceed45e1e06dc58e0) in code that tried to use a std::list with exceptions enabled.
The presence of the GCC exception table in the ELF binary was causing the following error:
A fatal error occurred: Segment loaded at 0x3f40b42c lands in same 64KB flash mapping as segment loaded at 0x3f400020. Can't generate binary. Suggest changing linker script or ELF to merge sections.
where the extra section was this:
6 .gcc_except_table._Z14parse_elementsB5cxx11PK24wifi_ieee80211_mac_hdr_tPKhj 00000012 3f40b42c 3f40b42c 0000b4cc 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
Changing the CXXFLAGS back to -fno-exceptions removed the extra section from the ELF binary, and allowed building to complete.
Closing this, as exception support is now enabled, and the toolchain has been updated.
Hi.
Exception isn't working inside threads: https://esp32.com/viewtopic.php?t=3580
I think it should be fixed too...
@mikhail-khalizev Exceptions in top-level FreeRTOS task functions is supported in more recent ESP-IDF, I've replied to you on the forum: https://esp32.com/viewtopic.php?f=14&t=3580&p=52289#p52289
Most helpful comment
@igrr: Ok, when I am done with work, I'll send a PR modifying startup function and linker script. I'll leave the rest up to you - thanks! Can I have also a request to add the possibility to enable RTTI?
@hwmaier I vote for having exceptions enabled by default (of course with the possibility to disable them). ESP32 is no a small platform anymore, the implementation of exception is zero-cost and most of the beginners with ESP32 are not going to reach flash limits and with exceptions enable by default they can easily use all features of C++. If a user wants to save memory, he is not a beginner anymore and he should be able to disable exceptions and RTTI.