Esp-idf: [TW#12147] Unable to switch on C++ exception handling -fexceptions (IDFGH-2244)

Created on 25 Mar 2017  路  21Comments  路  Source: espressif/esp-idf

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.

Feature Request

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.

All 21 comments

Attached to this post is the request app-template.elf which was added to a ZIP on linux using the "zip" command.

app-template.elf.zip

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 unwinding

So far I have found out the unwinder returns _URC_END_OF_STACK so there are several options what's going on:

  • there are no landing pads in the code
  • or the metadata/personality is wrong (this might be due to a bad linking)

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
  • linking of .eh_frame needs to be fixed
  • optionally, the toolchain should be recompiled to get libstdc++ with exception support

I 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?

    • As the object size is standard and probably won't change ever, the size can be hardcoded - easy, some of GCC libraries do it, changes only ESP-IDF and does not require recompiled toolchain. Cons: a little dirty. This is my candidate.

    • It is possible to make an Xtensa specific function for allocating the storage as a part of libgcc. Pros: Uses the original structure definition. Cons: Adds more Xtensa specific modifications to GCC, needs a recompiled toolchain.

  • How should be libstdc++ shipped? With exceptions only? Both versions?

    • if only a version with exception support is shipped, the behaviour for programs with -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.

    • ship both version and switch them. This involves modification to the build process in ESP-IDF and I am not feeling for that. If I have noticed, usually all the existing systems go with the first solution - ship only the versions with exception support.

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

Was this page helpful?
0 / 5 - 0 ratings