Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++ exceptions do not work when building with GNU Arm Embedded #35972

Closed
stephanosio opened this issue Jun 4, 2021 · 5 comments · Fixed by #35982
Closed

C++ exceptions do not work when building with GNU Arm Embedded #35972

stephanosio opened this issue Jun 4, 2021 · 5 comments · Fixed by #35982
Assignees
Labels
area: C++ bug The issue is a bug, or the PR is fixing a bug priority: medium Medium impact/importance bug
Milestone

Comments

@stephanosio
Copy link
Member

Describe the bug
C++ exceptions do not work when building for ARM platforms using GNU Arm Embedded toolchain.

To Reproduce
Build and run tests/subsys/cpp/libcxx with CONFIG_EXCEPTIONS=y for any ARM board (e.g. mps2_an521).

Logs and console output
For qemu_cortex_r5:

*** Booting Zephyr OS build v2.6.0-rc3-101-g2bf63134e8f0  ***
version 201703
Running test suite libcxx_tests
===================================================================
START - test_array
 PASS - test_array in 0.1 seconds
===================================================================
START - test_vector
 PASS - test_vector in 0.1 seconds
===================================================================
START - test_make_unique
 PASS - test_make_unique in 0.1 seconds
===================================================================
START - test_exception
exit

For the rest of the emulated ARM platforms, including qemu_cortex_m3 and mps2_an521, it just hangs during boot and nothing is output on console.

Environment (please complete the following information):

  • OS: Ubuntu 20.04
  • Toolchain: GNU Arm Embedded 2020-q4 (gcc-arm-none-eabi-10-2020-q4-major)
  • Commit SHA: 2bf6313

Additional context
Note that this problem is specific to the GNU Arm Embedded toolchain. C++ exceptions work when building with Zephyr SDK (0.12.4).

@stephanosio stephanosio added bug The issue is a bug, or the PR is fixing a bug area: C++ labels Jun 4, 2021
@stephanosio
Copy link
Member Author

cc @galak @tejlmand

@stephanosio
Copy link
Member Author

Analysis

When building with Zephyr SDK:

0x0000000020000000                __data_ram_start = .
0x0000000020000184                __data_ram_end = .

When building with GNU Arm Embedded:

0x0000000020000000                __data_ram_start = .
0x0000000000007490                __data_ram_end = .

The __data_ram_end address is invalid when built with GNU Arm Embedded toolchain and this causes z_data_copy's data section memcpy to be called with a negative size:

zephyr/kernel/xip.c

Lines 28 to 29 in 21d1ad3

(void)memcpy(&__data_ram_start, &__data_rom_start,
__data_ram_end - __data_ram_start);

This happens only with the GNU Arm Embedded toolchain because it is built with the libgcc transactional memory support is disabled and therefore .tm_clone_table section is not present.

Note that the __data_ram_end for ARM Cortex-M is set right after cplusplus-ram.ld:

#include <linker/cplusplus-ram.ld>
__data_ram_end = .;

cplusplus-ram.ld links .tm_clone_table as the last item. Note that .tm_clone_table is linked in RAMABLE_REGION and, when this section is present, the location counter (.) points to the RAM address.

SECTION_PROLOGUE(.eh_frame,,)
{
KEEP (*(SORT_NONE(EXCLUDE_FILE (*crtend.o) .eh_frame)))
KEEP (*(SORT_NONE(.eh_frame)))
} GROUP_LINK_IN(ROMABLE_REGION)
SECTION_PROLOGUE(.tm_clone_table,,)
{
KEEP (*(SORT_NONE(EXCLUDE_FILE (*crtend.o) .tm_clone_table)))
KEEP (*(SORT_NONE(.tm_clone_table)))
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)

When .tm_clone_table section is not present (i.e. in case of GNU Arm Embedded), however, the location counter points to the address of the last linked section, which is .eh_frame that resides in the ROMABLE_REGION; hence, making __data_ram_end invalid.

@stephanosio stephanosio added the priority: medium Medium impact/importance bug label Jun 5, 2021
@stephanosio stephanosio self-assigned this Jun 5, 2021
@stephanosio stephanosio added this to the v2.7.0 milestone Jun 5, 2021
stephanosio added a commit to stephanosio/zephyr that referenced this issue Jun 5, 2021
The `.eh_frame_hdr` and `.eh_frame` ROM sections, which contain read-
only C++ exception handling information, are currently specified in
`cplusplus-ram.ld`, and this can cause the linker output location
counter to take a ROM region address while in the RAM region.

This commit relocates these sections to `cplusplus-rom.ld` in order to
prevent the linker output location counter from getting corrupted.

For more details, refer to the issue zephyrproject-rtos#35972.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
@stephanosio
Copy link
Member Author

stephanosio commented Jun 5, 2021

UPDATE: C++ exception handling now works on all 32-bit ARM platforms when building with GNU Arm Embedded after #35982, given that CONFIG_NEWLIB_LIBC_NANO=n.

It fails as follows when CONFIG_NEWLIB_LIBC_NANO=y:

*** Booting Zephyr OS build v2.6.0-rc3-104-g9044bacb8081  ***
version 201703
Running test suite libcxx_tests
===================================================================
START - test_array
 PASS - test_array in 0.1 seconds
===================================================================
START - test_vector
 PASS - test_vector in 0.1 seconds
===================================================================
START - test_make_unique
 PASS - test_make_unique in 0.1 seconds
===================================================================
START - test_exception
exit

This might be a newlib/libstdc++ nano build-time configuration-related issue, especially noting that there seems to be a big difference in terms of the compiled binary size between the Zephyr SDK and the GNU Arm Embedded toolchains:

Zephyr SDK (qemu_cortex_m3, CONFIG_NEWLIB_LIBC_NANO=y)

[117/117] Linking CXX executable zephyr/zephyr.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       53712 B       256 KB     20.49%
            SRAM:        8760 B        64 KB     13.37%
        IDT_LIST:          0 GB         2 KB      0.00%

GNU Arm Embedded (qemu_cortex_m3, CONFIG_NEWLIB_LIBC_NANO=y)

[119/119] Linking CXX executable zephyr/zephyr.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       29840 B       256 KB     11.38%
            SRAM:        8736 B        64 KB     13.33%
        IDT_LIST:          0 GB         2 KB      0.00%

@stephanosio
Copy link
Member Author

stephanosio commented Jun 5, 2021

NOTE: Comparing Zephyr SDK and GNU Arm Embedded libraries

Zephyr SDK

$ ls -lh /opt/zephyr-sdk-0.12.4/arm-zephyr-eabi/arm-zephyr-eabi/lib/libc*.a
-rw-r--r-- 2 root root 1.1M Mar 27 01:56 /opt/zephyr-sdk-0.12.4/arm-zephyr-eabi/arm-zephyr-eabi/lib/libc.a
-rw-r--r-- 1 root root 1.1M Mar 27 01:57 /opt/zephyr-sdk-0.12.4/arm-zephyr-eabi/arm-zephyr-eabi/lib/libc_nano.a

$ ls -lh /opt/zephyr-sdk-0.12.4/arm-zephyr-eabi/arm-zephyr-eabi/lib/lib*c++*.a
-rw-r--r-- 1 root root 5.4M Mar 27 01:57 /opt/zephyr-sdk-0.12.4/arm-zephyr-eabi/arm-zephyr-eabi/lib/libstdc++.a
-rw-r--r-- 1 root root 5.3M Mar 27 01:57 /opt/zephyr-sdk-0.12.4/arm-zephyr-eabi/arm-zephyr-eabi/lib/libstdc++_nano.a
-rw-r--r-- 1 root root 318K Mar 27 01:57 /opt/zephyr-sdk-0.12.4/arm-zephyr-eabi/arm-zephyr-eabi/lib/libsupc++.a
-rw-r--r-- 1 root root 298K Mar 27 01:57 /opt/zephyr-sdk-0.12.4/arm-zephyr-eabi/arm-zephyr-eabi/lib/libsupc++_nano.a

GNU Arm Embedded

$ ls -lh /opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/libc*.a
-rw-r--r-- 2 root root 1.3M Nov 24  2020 /opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/libc.a
-rw-r--r-- 1 root root 1.1M Nov 24  2020 /opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/libc_nano.a

$ ls -lh /opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/lib*c++*.a
-rw-r--r-- 1 root root 5.5M Nov 24  2020 /opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/libstdc++.a
-rw-r--r-- 1 root root 3.5M Nov 24  2020 /opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/libstdc++_nano.a
-rw-r--r-- 1 root root 319K Nov 24  2020 /opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/libsupc++.a
-rw-r--r-- 1 root root 250K Nov 24  2020 /opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/libsupc++_nano.a

Note the size difference between libstdc++.a and libstdc++_nano.a for the two toolchains:

Toolchain libstdc++.a libstdc++_nano.a
Zephyr SDK 5.4M 5.3M
GNU Arm Embedded 5.5M 3.5M

There is no discernible difference between the Zephyr SDK and the GNU Arm Embedded when it comes to newlib (libc*.a); however, a significant difference can be seen for the standard C++ library (lib*c++.a).

TODO: Investigate the build options for the standard C++ library and find out what differences there are.

p.s. It looks like Zephyr SDK is not properly building the nano version of libstdc++. If libstdc++_nano.a, by design, is not meant to support C++ exception handling, consider revising the Zephyr-side config scheme to disallow C++ exception usage with nano.specs (selected by the newlib nano config).

@galak FYI

@stephanosio
Copy link
Member Author

So, as far as GNU Arm Embedded is concerned, the nano variant of libstdc++ nano is not supposed to support C++ exceptions.

From GNU Arm Embedded build script:

rm -rf $BUILDDIR_NATIVE/gcc-size-libstdcxx && mkdir -p $BUILDDIR_NATIVE/gcc-size-libstdcxx
pushd $BUILDDIR_NATIVE/gcc-size-libstdcxx

$SRCDIR/$GCC/configure --target=$TARGET \
    --prefix=$BUILDDIR_NATIVE/target-libs \
    --enable-languages=c,c++ \
    --disable-decimal-float \
    --disable-libffi \
    --disable-libgomp \
    --disable-libmudflap \
    --disable-libquadmath \
    --disable-libssp \
    --disable-libstdcxx-pch \
    --disable-libstdcxx-verbose \
    --disable-nls \
    --disable-shared \
    --disable-threads \
    --disable-tls \
    --with-gnu-as \
    --with-gnu-ld \
    --with-newlib \
    --with-headers=yes \
    --with-python-dir=share/gcc-arm-none-eabi \
    --with-sysroot=$BUILDDIR_NATIVE/target-libs/arm-none-eabi \
    $GCC_CONFIG_OPTS \
    "${GCC_CONFIG_OPTS_LCPP}"                              \
    "--with-pkgversion=$PKGVERSION" \
    ${MULTILIB_LIST}

make -j$JOBS CCXXFLAGS="$BUILD_OPTIONS" CXXFLAGS_FOR_TARGET="-g -Os -ffunction-sections -fdata-sections -fno-exceptions"

Note that -fno-exceptions is specified for the libstdc++ nano build.

This is, in fact, a sensible decision made by the GNU Arm Embedded architects, given that enabling C++ exception handling support significantly increases the binary size.

In the light of this, the following changes need to be implemented:

  1. Update Zephyr SDK to build libstdc++ nano according to the GNU Arm Embedded specs (i.e. without C++ exception handling support).
  2. Update Zephyr-side configs to disallow CONFIG_EXCEPTIONS when CONFIG_NEWLIB_LIBC_NANO=y (note that the selection of both newlib nano and libstdc++ nano is governed by nano.specs; this makes sense because there exists, although not direct, a correlation between these two libraries.).

stephanosio added a commit to stephanosio/zephyr that referenced this issue Jun 7, 2021
This commit makes C++ exception handling feature depend on the full
version of newlib (i.e. `CONFIG_NEWLIB_LIBC_NANO=n`).

The `nano.specs`, which selects the nano variant of newlib, libstdc++,
and libsupc++, does not support C++ exception handling because its
lib*c++ is compiled with `-fno-exceptions`.

For more details, refer to the issue zephyrproject-rtos#35972.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
galak pushed a commit that referenced this issue Jun 8, 2021
The `.eh_frame_hdr` and `.eh_frame` ROM sections, which contain read-
only C++ exception handling information, are currently specified in
`cplusplus-ram.ld`, and this can cause the linker output location
counter to take a ROM region address while in the RAM region.

This commit relocates these sections to `cplusplus-rom.ld` in order to
prevent the linker output location counter from getting corrupted.

For more details, refer to the issue #35972.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
galak pushed a commit that referenced this issue Jun 8, 2021
This commit makes C++ exception handling feature depend on the full
version of newlib (i.e. `CONFIG_NEWLIB_LIBC_NANO=n`).

The `nano.specs`, which selects the nano variant of newlib, libstdc++,
and libsupc++, does not support C++ exception handling because its
lib*c++ is compiled with `-fno-exceptions`.

For more details, refer to the issue #35972.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
zephyrbot pushed a commit that referenced this issue Jun 8, 2021
The `.eh_frame_hdr` and `.eh_frame` ROM sections, which contain read-
only C++ exception handling information, are currently specified in
`cplusplus-ram.ld`, and this can cause the linker output location
counter to take a ROM region address while in the RAM region.

This commit relocates these sections to `cplusplus-rom.ld` in order to
prevent the linker output location counter from getting corrupted.

For more details, refer to the issue #35972.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
zephyrbot pushed a commit that referenced this issue Jun 8, 2021
This commit makes C++ exception handling feature depend on the full
version of newlib (i.e. `CONFIG_NEWLIB_LIBC_NANO=n`).

The `nano.specs`, which selects the nano variant of newlib, libstdc++,
and libsupc++, does not support C++ exception handling because its
lib*c++ is compiled with `-fno-exceptions`.

For more details, refer to the issue #35972.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
galak pushed a commit that referenced this issue Jun 8, 2021
The `.eh_frame_hdr` and `.eh_frame` ROM sections, which contain read-
only C++ exception handling information, are currently specified in
`cplusplus-ram.ld`, and this can cause the linker output location
counter to take a ROM region address while in the RAM region.

This commit relocates these sections to `cplusplus-rom.ld` in order to
prevent the linker output location counter from getting corrupted.

For more details, refer to the issue #35972.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
galak pushed a commit that referenced this issue Jun 8, 2021
This commit makes C++ exception handling feature depend on the full
version of newlib (i.e. `CONFIG_NEWLIB_LIBC_NANO=n`).

The `nano.specs`, which selects the nano variant of newlib, libstdc++,
and libsupc++, does not support C++ exception handling because its
lib*c++ is compiled with `-fno-exceptions`.

For more details, refer to the issue #35972.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: C++ bug The issue is a bug, or the PR is fixing a bug priority: medium Medium impact/importance bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant