Skip to content

rework newlib directory prioritization to fix include processing for non-Zephyr toolchains #18242

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

Merged
merged 3 commits into from
Aug 20, 2019

Conversation

pabigot
Copy link
Collaborator

@pabigot pabigot commented Aug 13, 2019

This work closes #17564 and related C++ failures.

Zephyr builds with -ffreestanding meaning that __STDC_HOSTED__ is false. Consequently the toolchain standard <stdint.h> is a minimal header that routes to <stdint-gcc.h> which defines integer types without defining the marker macro __int64_t_defined that signals newlib <inttypes.h> to provide support for PRIu64 (#14310). In hosted builds <stdint.h> instead sets some flags then delegates to the host-provided (newlib) implementation of <stdint.h>.

In support of the desire to have support for integer types that aren't part of the freestanding system #14312 placed the newlib include headers before the system ones, specifically to make hosted features available in a non-hosted system.

This decision breaks building C++ applications that use standard C++ include files when using a non-Zephyr toolchain like gnumarmemb. The C++ header <cstdlib> (referenced from <array> in #17564) wants to #include_next <stdlib.h>, but the include priority has placed the directory that provides it earlier in the search path, where it can no longer be found.

The workaround succeeded with the Zephyr toolchain only because the Zephyr SDK uses a cross-compiler infrastructure technique that leaves behind and references a second copy of the base include directory that is normally needed only when building the compiler. <stdint.h> is then found at that second location.

This PR switches to a solution based on updating the include path to include an intercepting header that provides the required defines to satisfy the use case of #14310 without completely breaking the toolchain standard include processing. This should work on GCC and clang.

Note that the test case requires a patch from #18243 to support ztest with C++.

@zephyrbot zephyrbot added area: C Library C Standard Library area: X86 x86 Architecture (32-bit) area: ARM ARM (32-bit) Architecture area: NIOS2 NIOS2 Architecture area: Xtensa Xtensa Architecture area: ARC ARC Architecture area: Test Framework Issues related not to a particular test, but to the framework instead area: API Changes to public APIs area: Samples Samples area: Tests Issues related to a particular existing or missing test labels Aug 13, 2019
@pabigot
Copy link
Collaborator Author

pabigot commented Aug 13, 2019

Background information:

To understand the details you need to know what the system header search path is for a given compiler. Get this with:

target-g++ -xc++ -v -E - < /dev/null

With ZEPHYR_TOOLCHAIN_VARIANT=zephyr the standard system header search paths are (e.g.):

/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/include/c++/8.3.0
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/include/c++/8.3.0/arm-zephyr-eabi
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/include/c++/8.3.0/backward
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/8.3.0/include
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/8.3.0/include-fixed
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/sys-include
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/include

By adding -isystem ${LIBC_INCLUDE_DIR} this changes to:

/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/include
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/include/c++/8.3.0
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/include/c++/8.3.0/arm-zephyr-eabi
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/include/c++/8.3.0/backward
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/8.3.0/include
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/8.3.0/include-fixed
/usr/local/zephyr-sdk-0.10.2/arm-zephyr-eabi/arm-zephyr-eabi/sys-include

The contents of the zephyr toolchain path ending in /sys-include is identical to that of /include except that it lacks the c++ subdirectory. It is an artefact of a multi-stage toolchain bootstrap that allows libc to be built using a compiler that doesn't have a real libc.

With ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb the standard system header search paths are (e.g.):

/usr/local/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include/c++/8.3.1
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include/c++/8.3.1/arm-none-eabi
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include/c++/8.3.1/backward
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/lib/gcc/arm-none-eabi/8.3.1/include
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/lib/gcc/arm-none-eabi/8.3.1/include-fixed
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include

By adding -isystem ${LIBC_INCLUDE_DIR} this changes to:

/usr/local/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include/c++/8.3.1
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include/c++/8.3.1/arm-none-eabi
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/arm-none-eabi/include/c++/8.3.1/backward
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/lib/gcc/arm-none-eabi/8.3.1/include
/usr/local/gcc-arm-none-eabi-8-2019-q3-update/lib/gcc/arm-none-eabi/8.3.1/include-fixed

Note that this build does not have /sys-include because the ARM folks use a two-stage build where the final build doesn't include it. Consequently #include_next for any file in the normal include path will be found in /sys-include for Zephyr toolchains, but will not be found in other toolchains that don't have duplicated standard include directories.

@zephyrbot
Copy link
Collaborator

zephyrbot commented Aug 13, 2019

All checks are passing now.

Review history of this comment for details about previous failed status.
Note that some checks might have not completed yet.

Consistently place C++ use of extern "C" after all include directives,
within the negative branch of _ASMLANGUAGE if used.

Background from issue zephyrproject-rtos#17997:

Declarations that use C linkage should be placed within extern "C"
so the language linkage is correct when the header is included by
a C++ compiler.

Similarly #include directives should be outside the extern "C" to
ensure the language-specific default linkage is applied to any
declarations provided by the included header.

See: https://en.cppreference.com/w/cpp/language/language_linkage
Signed-off-by: Peter Bigot <[email protected]>
@pabigot pabigot requested review from galak and SebastianBoe August 14, 2019 13:48
@pabigot pabigot removed area: API Changes to public APIs area: ARC ARC Architecture area: ARM ARM (32-bit) Architecture area: NIOS2 NIOS2 Architecture area: Samples Samples area: Test Framework Issues related not to a particular test, but to the framework instead area: X86 x86 Architecture (32-bit) area: Xtensa Xtensa Architecture labels Aug 14, 2019
@pabigot pabigot marked this pull request as ready for review August 14, 2019 13:52
@pabigot pabigot added this to the v2.0.0 milestone Aug 14, 2019
@pabigot
Copy link
Collaborator Author

pabigot commented Aug 14, 2019

@pfalcon Please check this branch for the use case underlying #15627. It essentially reverts a change from #14312 that you used to justify #15937, i.e. using -isystem for a libc header path in order to change its priority.

That change was incorrect for newlib, but is arguably appropriate for minimal libc.

I don't have a test case for #15627 but if your solution depended on the newlib use of -isystem this commit may have broken it again. If so, I think the correct path is to add support for
a zephyr_early_include_directories() macro that uses the cmake BEFORE tag instead of the SYSTEM tag to ensure that some directory is placed earlier in the search path. Or possibly use the technique demonstrated here to avoid name conflicts.

@pfalcon
Copy link
Collaborator

pfalcon commented Aug 14, 2019

Please check this branch for the use case underlying #15627. It essentially reverts a change from

@pabigot: As a quick "first thing which comes to mind", based on wording above and "Commits 3 Files changed 11", I'd say that this is too late for 2.0.

I'll try however to look in detail as time permits.

@pfalcon pfalcon self-requested a review August 14, 2019 14:19
@pabigot
Copy link
Collaborator Author

pabigot commented Aug 14, 2019

@pfalcon Only one commit, and a change to two files, fixes this bug, which I thought was in scope for this stage of the release process. The other two commits are (1) a test case to avoid regressions, and (2) additional cleanup(bug) fixes queued in #18243 necessary to do unit tests on C++ code.

However, I'm not adamant about merging this, as long as it's OK to leave C++ broken in 2.0.0 with non-Zephyr toolchains.

@pfalcon
Copy link
Collaborator

pfalcon commented Aug 14, 2019

@pabigot

I don't have a test case for #15627 but if your solution depended on the newlib use of -isystem this commit may have broken it again.

So, testcases are includes in a PR that fixed that issue, #15937 , specifically, a40cc0d . I.e., with my patch, POSIX-using apps no longer need to do target_include_directories(app PRIVATE $ENV{ZEPHYR_BASE}/include/) (which is kinda implementation detail of POSIX subsys, which should not leak into config of each and every user app).

As can be seen, CI is green for this PR, so hopefully teh handling above is not broken. Well, I'd still hope to try it manually myself to be double-sure (tomorrow, perhaps).

But otherwise, I'd leave it to @galak, the author of #14312, to vote this PR up/down.

@pabigot
Copy link
Collaborator Author

pabigot commented Aug 14, 2019

https://reviews.llvm.org/D32411 includes a commit that documents a design to support #include_next on MSVC, which doesn't have it, using nasty looking code and CMake. However, the practice within newlib itself is to simply provide the missing content inline.

Are there non-clang/gnuc toolchains we need to worry about?

The solution from zephyrproject-rtos#14312 of using -isystem to prioritize the position of
the libc directory bypasses the effect of -ffreestanding with respect to
libc symbols expected to be present in a non-hosted environment.

Further, it breaks C++ with the ARM Embedded toolchain as the system
fails to find the right file with #include_next.

Use a more fine-grained solution that explicitly includes the underlying
newlib header required for <inttypes.h> support before moving on to
include the next available one, whether system or non-system.

Closes zephyrproject-rtos#17564

Signed-off-by: Peter Bigot <[email protected]>
Confirms build (and run) of C++17 applications that make use of STL
containers and other features.

Signed-off-by: Peter Bigot <[email protected]>
@zephyrbot zephyrbot added the area: Test Framework Issues related not to a particular test, but to the framework instead label Aug 14, 2019
@pfalcon
Copy link
Collaborator

pfalcon commented Aug 15, 2019

Ok, manually tested tests/posix/common, tests/posix/fs - all's ok (yeah, that should be covered by CI, but as we know there's a bit randomization and subsetting in it, so some tests aren't run all platforms, and given a trouble it took to lead POSIX-related changes into the mainline, I wanted to have an explicit checkpoint here).

So, I'd be happy to +1 this, just giving some time to @galak to review too.

Copy link
Collaborator

@SebastianBoe SebastianBoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work.

@galak galak merged commit aded6a5 into zephyrproject-rtos:master Aug 20, 2019
@pabigot pabigot deleted the nordic/20190813b branch August 21, 2019 14:55
@backporting
Copy link

backporting bot commented Aug 21, 2019

The backport to v1.14-branch failed:

Commits ["654d2df47a80501a4ab5077362f0224cc361a31d","3aee7b8c96592f489617aba8873982fbcc32166b","26bedf08be2e10fa941df0353ed6902009f55b79"] could not be cherry-picked on top of v1.14-branch

To backport manually, run these commands in your terminal:

# Fetch latest updates from GitHub.
git fetch
# Create new working tree.
git worktree add .worktrees/backport v1.14-branch
# Navigate to the new directory.
cd .worktrees/backport
# Cherry-pick all the commits of this pull request and resolve the likely conflicts.
git cherry-pick 654d2df47a80501a4ab5077362f0224cc361a31d 3aee7b8c96592f489617aba8873982fbcc32166b 26bedf08be2e10fa941df0353ed6902009f55b79
# Create a new branch with these backported commits.
git checkout -b backport-18242-to-v1.14-branch
# Push it to GitHub.
git push --set-upstream origin backport-18242-to-v1.14-branch
# Go back to the original working tree.
cd ../..
# Delete the working tree.
git worktree remove .worktrees/backport

Then, create a pull request where the base branch is v1.14-branch and the compare/head branch is backport-18242-to-v1.14-branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: C Library C Standard Library area: C++ area: Test Framework Issues related not to a particular test, but to the framework instead area: Tests Issues related to a particular existing or missing test
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing stdlib.h include when C++ standard library is used.
6 participants