Skip to content

native-gcc host build fails on macOS (Apple SDK, GCC-16 -Os libstdc++, --start-group, -Werror=uninitialized) #39

Description

@erik-rainey-neurophos

Summary

The native-gcc host preset (cmake --workflow --preset on-host-native-gcc) does not complete on macOS (Apple Silicon, recent Command Line Tools / SDK). It fails in several independent layers — the first masks the rest, so they surface one at a time as each is worked around. The native-clang host path is unaffected. All findings are against main (commit c66a0d2).

1. Toolchain search picks a GCC too old for the current macOS SDK

build-support/lib/cmake/native-gcc.cmake searches gcc-13 first:

find_program(CMAKE_C_COMPILER REQUIRED NAMES gcc-13 gcc-14 gcc-15 gcc ...)
find_program(CMAKE_CXX_COMPILER REQUIRED NAMES g++-13 g++-14 g++-15 g++ ...)

Homebrew gcc-13 (and gcc-14) cannot parse the current macOS SDK headers. Compiling anything that includes <mach/mach.h> (e.g. via googletest's gtest-port.cc) fails with:

.../MacOSX*.sdk/usr/include/mach/message.h:299:1: error: expected constructor, destructor, or type conversion before '(' token
  299 | xnu_static_assert_struct_size(mach_msg_type_descriptor_t, 12);

The xnu_static_assert_struct_size macros in mach/port.h are gated on #if __arm64__; the older GCCs don't handle them for this SDK. gcc-16 parses the SDK cleanly. Searching newest-first (gcc-16 … gcc-13) resolves this.

2. With GCC 16, -Os fails to link std::string's move constructor

The MinSizeDebug build type compiles the host build at -Os. Homebrew GCC 16 at -Os emits an undefined reference to the unified C4 constructor variant of the extern-template std::__cxx11::basic_string, which the shipped libstdc++ exports only as C1/C2 (static libstdc++ lacks C4 too). Test executables then fail to link:

Undefined symbols for architecture arm64:
  "std::__cxx11::basic_string<...>::basic_string(std::__cxx11::basic_string<...>&&)", referenced from:
      __static_initialization_and_destruction_0() in <tu>.o
      ... in libgtest.a / Catch2 objects

Minimal reproducer (Homebrew g++-16, macOS arm64):

#include <string>
std::string mk(std::string&& s){ return std::string(std::move(s)); }
int main(){ std::string a="x"; return (int)mk(std::move(a)).size(); }
  • g++-16 -Os … → link fails (undefined ...basic_stringIcSt11char_traitsIcESaIcEEC4EOS4_)
  • g++-16 -O0 … / g++-16 -O2 … → links (move ctor is inlined, no C4 reference)

The host test build has no size constraint, so building it at -O2 sidesteps the miscompile and is arguably more appropriate for host unit tests anyway.

3. --start-group/--end-group is enabled for GNU on macOS, where Apple ld rejects it

CMakeLists.txt (lines ~18–24):

if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 11)
    OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_SYSTEM_NAME STREQUAL "Linux"))
    set(CMAKE_CXX_LINK_GROUP_USING_RESCAN_SUPPORTED TRUE)
    set(CMAKE_CXX_LINK_GROUP_USING_RESCAN "LINKER:--start-group" "LINKER:--end-group")

The Clang branch is correctly restricted to Linux, but the GNU branch has no platform guard. Homebrew GCC on macOS drives Apple's ld, which does not support the GNU archive-group flags:

ld: unknown options: --start-group --end-group

(Clang on macOS avoids this only because its branch requires Linux.) Adding AND NOT APPLE to the GNU branch fixes it; the bare-metal arm-none-eabi cross build is GNU with CMAKE_SYSTEM_NAME=Generic (not APPLE), so it keeps the group flags its GNU ld needs.

4. -Werror=uninitialized in CortexProcessor::GetRevision()

modules/jarnax/source/CortexProcessor.cpp (~line 68):

cortex::Revision CortexProcessor::GetRevision() const {
    cortex::Revision revision;                 // <-- not initialized on all paths GCC can see
    if constexpr (cortex::run_in_privileged_mode_only) {
        revision = cortex::processor::GetRevision();
    } else {
        // filled in via the supervisor marshal call by &revision
        ...
    }
    return revision;
}

Under GCC, -Werror=uninitialized fires (the supervisor path writes revision through a pointer that GCC can't see through). The sibling GetPartNumber() initializes its local (partno = cortex::PartNumber::Unknown); GetRevision() should likewise value-initialize (cortex::Revision revision{};). Clang does not diagnose this.

Suggested fixes

  1. native-gcc.cmake: search newest GCC first (gcc-16 gcc-15 gcc-14 gcc-13 gcc).
  2. native-gcc.cmake: build the host at -O2 (host tests are not size-constrained), avoiding the GCC-16 -Os std::string C4 link failure.
  3. CMakeLists.txt: add AND NOT APPLE to the GNU branch of the RESCAN link-group check.
  4. CortexProcessor.cpp: value-initialize cortex::Revision revision{};.

Environment

  • macOS on Apple Silicon (arm64), recent SDK (the xnu_static_assert_struct_size macros).
  • Homebrew gcc@13, gcc@14, gcc@16 installed.
  • Reference: the native-clang host preset builds and tests cleanly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions