Page MenuHomeFreeBSD

cross-build: Workaround system-provided strchrnul on macOS 15.4+
ClosedPublic

Authored by jhb on Apr 18 2025, 6:44 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sat, Oct 11, 2:10 AM
Unknown Object (File)
Thu, Sep 25, 7:18 PM
Unknown Object (File)
Wed, Sep 24, 10:05 AM
Unknown Object (File)
Sep 6 2025, 6:29 AM
Unknown Object (File)
Aug 30 2025, 7:33 PM
Unknown Object (File)
Aug 27 2025, 7:57 AM
Unknown Object (File)
Aug 24 2025, 12:48 AM
Unknown Object (File)
Aug 19 2025, 1:30 PM
Subscribers

Details

Summary

macOS added a native strchrnul in 15.4. There is not an easy way to
detect it at compile time, so use a macro to rename our local inline
version to avoid conflicts while also forcing its use during
bootstrap. The local version can be removed once macOS versions older
than 15.4 are no longer supported as build hosts.

Reported by: kib
Co-authored by: jrtc27

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 63580
Build 60464: arc lint + arc unit

Event Timeline

jhb requested review of this revision.Apr 18 2025, 6:44 PM

I commented out local strchrnul() definition and build failed in gtest. If you are interested, I will provide the log.

In D49893#1137709, @kib wrote:

I commented out local strchrnul() definition and build failed in gtest. If you are interested, I will provide the log.

Hmm, both amd64 world and kernel built fine for me on my mac with this change. I had seen your comment on IRC and was hoping to reproduce the failure locally. I did use LLVM 18 from brew to build. LLVM 19 and later move lld into a separate package in brew which will require some changes to make.py to work out of the box on macOS. :( I'm not sure why brew decided that was the right thing to do.

Some time ago I used the following recipe, the build succeeded then, I think it was llvm 20 already:

MAKEOBJDIRPREFIX=${HOME}/work/DEV/obj XLD=/opt/homebrew/opt/lld/bin/ld.lld \
    tools/build/make.py -s -j 14 TARGET=amd64 TARGET_ARCH=amd64 buildworld buildkernel

Now, after commenting out strchrnul(), I see

In file included from /Users/kostik/work/DEV/src/contrib/googletest/googletest/src/gtest-all.cc:38:
In file included from /Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/private/gtest/gtest.h:55:
In file included from /Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/memory:944:
In file included from /Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/__memory/inout_ptr.h:16:
In file included from /Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/__memory/shared_ptr.h:31:
/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/__memory/uninitialized_algorithms.h:645:22: error: first argument in call to '__builtin_memcpy' is a pointer to non-trivially copyable type '__remove_const_t<string>' (aka 'std::string') [-Werror,-Wnontrivial-memcall]
  645 |     __builtin_memcpy(const_cast<__remove_const_t<_Tp>*>(__result), __first, sizeof(_Tp) * (__last - __first));
      |                      ^
/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/vector:1052:8: note: in instantiation of function template specialization 'std::__uninitialized_allocator_relocate<std::allocator<std::string>, std::string>' requested here
 1052 |   std::__uninitialized_allocator_relocate(
      |        ^
/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/vector:1502:3: note: in instantiation of member function 'std::vector<std::string>::__swap_out_circular_buffer' requested here
 1502 |   __swap_out_circular_buffer(__v);
      |   ^
/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/vector:1514:13: note: in instantiation of function template specialization 'std::vector<std::string>::__push_back_slow_path<const std::string &>' requested here
 1514 |     __end = __push_back_slow_path(__x);
      |             ^
/Users/kostik/work/DEV/src/contrib/googletest/googletest/src/gtest-death-test.cc:1381:8: note: in instantiation of member function 'std::vector<std::string>::push_back' requested here
 1381 |   args.push_back(filter_flag);
      |        ^
/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/__memory/uninitialized_algorithms.h:645:22: note: explicitly cast the pointer to silence this warning
  645 |     __builtin_memcpy(const_cast<__remove_const_t<_Tp>*>(__result), __first, sizeof(_Tp) * (__last - __first));
      |                      ^
===> lib/libipt (all)
===> lib/libiconv_modules/ZW (all)
===> lib/libiconv_modules/iconv_none (all)
===> lib/libiconv_modules/iconv_std (all)
1 error generated.
--- gtest-all.o ---
*** [gtest-all.o] Error code 1

I installed llvm@18 from brew, and used the following recipe:

MAKEOBJDIRPREFIX=${HOME}/work/DEV/obj \
    XCC=/opt/homebrew/opt/llvm@18/bin/clang \
    XCXX=/opt/homebrew/opt/llvm@18/bin/clang \
    XCPP=/opt/homebrew/opt/llvm@18/bin/clang-cpp \
    XLD=/opt/homebrew/opt/llvm@18/bin/ld.lld \
    tools/build/make.py -s -j 14 TARGET=amd64 TARGET_ARCH=amd64 buildworld buildkernel

The result is

ld.lld: error: undefined symbol: operator new(unsigned long)
>>> referenced by new:265 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/new:265)
>>>               atf-check.o:(main)
>>> referenced by new:265 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/new:265)
>>>               atf-check.o:((anonymous namespace)::atf_check::specific_options() const)
>>> referenced by new:265 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/new:265)
>>>               atf-check.o:((anonymous namespace)::atf_check::specific_options() const)
>>> referenced 16 more times

ld.lld: error: undefined symbol: operator delete(void*)
>>> referenced by new:274 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/new:274)
>>>               atf-check.o:(main)
>>> referenced by new:274 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/new:274)
>>>               atf-check.o:(main)
>>> referenced by new:274 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/new:274)
>>>               atf-check.o:(main)
>>> referenced 153 more times

ld.lld: error: undefined symbol: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::find(char, unsigned long) const
>>> referenced by atf-check.cpp:345 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:345)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:263 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:263)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:313 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:313)
>>>               atf-check.o:(parse_output_check_arg(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))

ld.lld: error: undefined symbol: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::basic_string(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, unsigned long, unsigned long, std::__1::allocator<char> const&)
>>> referenced by string:1683 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:1683)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by string:1683 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:1683)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by string:1683 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:1683)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced 5 more times

ld.lld: error: undefined symbol: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::compare(unsigned long, unsigned long, char const*) const
>>> referenced by atf-check.cpp:264 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:264)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:314 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:314)
>>>               atf-check.o:(parse_output_check_arg(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))

ld.lld: error: undefined symbol: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*)
>>> referenced by string:4102 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:4102)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by string:1361 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:1361)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced by string:1361 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:1361)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced 3 more times

ld.lld: error: undefined symbol: __cxa_allocate_exception
>>> referenced by atf-check.cpp:0 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:0)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:0 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:0)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:304 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:304)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced 26 more times

ld.lld: error: undefined symbol: __cxa_throw
>>> referenced by atf-check.cpp:0 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:0)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:0 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:0)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:0 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:0)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced 18 more times

ld.lld: error: undefined symbol: __cxa_begin_catch
>>> referenced by atf-check.cpp:251 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:251)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:205 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:205)
>>>               atf-check.o:(parse_exit_code(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced by atf-check.cpp:139 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:139)
>>>               atf-check.o:((anonymous namespace)::temp_file::~temp_file())
>>> referenced 4 more times

ld.lld: error: undefined symbol: __cxa_end_catch
>>> referenced by atf-check.cpp:254 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:254)
>>>               atf-check.o:((anonymous namespace)::atf_check::process_option(int, char const*))
>>> referenced by atf-check.cpp:208 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:208)
>>>               atf-check.o:(parse_exit_code(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced by atf-check.cpp:141 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:141)
>>>               atf-check.o:((anonymous namespace)::temp_file::~temp_file())
>>> referenced 6 more times

ld.lld: error: undefined symbol: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::push_back(char)
>>> referenced by string:1365 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:1365)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced by atf-check.cpp:589 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:589)
>>>               atf-check.o:(run_output_checks(std::__1::vector<(anonymous namespace)::output_check, std::__1::allocator<(anonymous namespace)::output_check>> const&, atf::fs::path const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced by istream:1272 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/istream:1272)
>>>               atf-check.o:(std::__1::basic_istream<char, std::__1::char_traits<char>>& std::__1::getline[abi:se190107]<char, std::__1::char_traits<char>, std::__1::allocator<char>>(std::__1::basic_istream<char, std::__1::char_traits<char>>&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&, char))

ld.lld: error: undefined symbol: std::__1::cerr
>>> referenced by atf-check.cpp:0 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:0)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced by atf-check.cpp:0 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:0)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced by basic_ostream.h:619 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/__ostream/basic_ostream.h:619)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced 18 more times

ld.lld: error: undefined symbol: std::__1::basic_ostream<char, std::__1::char_traits<char>>::operator<<(int)
>>> referenced by atf-check.cpp:631 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:631)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced by atf-check.cpp:607 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:607)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced by basic_ostream.h:0 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/__ostream/basic_ostream.h:0)
>>>               atf-check.o:((anonymous namespace)::atf_check::main())
>>> referenced 1 more times

ld.lld: error: undefined symbol: std::runtime_error::runtime_error(char const*)
>>> referenced by atf-check.cpp:203 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:203)
>>>               atf-check.o:(parse_exit_code(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced by text.hpp:142 (/Users/kostik/work/DEV/src/contrib/atf/atf-c++/detail/text.hpp:142)
>>>               atf-check.o:(int atf::text::to_type<int>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))

ld.lld: error: undefined symbol: typeinfo for std::runtime_error
>>> referenced by atf-check.cpp:203 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:203)
>>>               atf-check.o:(parse_exit_code(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced by atf-check.cpp:179 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:179)
>>>               atf-check.o:(get_monotonic_useconds())
>>> referenced by atf-check.cpp:453 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:453)
>>>               atf-check.o:(cat_file(atf::fs::path const&))
>>> referenced 7 more times

ld.lld: error: undefined symbol: std::runtime_error::~runtime_error()
>>> referenced by atf-check.cpp:203 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:203)
>>>               atf-check.o:(parse_exit_code(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced by atf-check.cpp:179 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:179)
>>>               atf-check.o:(get_monotonic_useconds())
>>> referenced by atf-check.cpp:453 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:453)
>>>               atf-check.o:(cat_file(atf::fs::path const&))
>>> referenced 6 more times

ld.lld: error: undefined symbol: __cxa_free_exception
>>> referenced by atf-check.cpp:203 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:203)
>>>               atf-check.o:(parse_exit_code(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced by atf-check.cpp:179 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:179)
>>>               atf-check.o:(get_monotonic_useconds())
>>> referenced by atf-check.cpp:453 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:453)
>>>               atf-check.o:(cat_file(atf::fs::path const&))
>>> referenced 9 more times

ld.lld: error: undefined symbol: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::insert(unsigned long, char const*)
>>> referenced by string:4089 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:4089)
>>>               atf-check.o:(get_monotonic_useconds())
>>> referenced by string:4089 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:4089)
>>>               atf-check.o:(cat_file(atf::fs::path const&))
>>> referenced by string:4089 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/string:4089)
>>>               atf-check.o:(run_output_checks(std::__1::vector<(anonymous namespace)::output_check, std::__1::allocator<(anonymous namespace)::output_check>> const&, atf::fs::path const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced 5 more times

ld.lld: error: undefined symbol: std::runtime_error::runtime_error(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&)
>>> referenced by atf-check.cpp:179 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:179)
>>>               atf-check.o:(get_monotonic_useconds())
>>> referenced by atf-check.cpp:453 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:453)
>>>               atf-check.o:(cat_file(atf::fs::path const&))
>>> referenced by atf-check.cpp:469 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:469)
>>>               atf-check.o:(run_output_checks(std::__1::vector<(anonymous namespace)::output_check, std::__1::allocator<(anonymous namespace)::output_check>> const&, atf::fs::path const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&))
>>> referenced 4 more times

ld.lld: error: undefined symbol: std::__1::cout
>>> referenced by basic_ostream.h:619 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/__ostream/basic_ostream.h:619)
>>>               atf-check.o:(execute(char const* const*))
>>> referenced by atf-check.cpp:423 (/Users/kostik/work/DEV/src/contrib/atf/atf-sh/atf-check.cpp:423)
>>>               atf-check.o:(execute(char const* const*))
>>> referenced by basic_ostream.h:619 (/Users/kostik/work/DEV/obj/Users/kostik/work/DEV/src/amd64.amd64/tmp/usr/include/c++/v1/__ostream/basic_ostream.h:619)
>>>               atf-check.o:(execute(char const* const*))

ld.lld: error: too many errors emitted, stopping now (use --error-limit=0 to see all errors)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
--- atf-check.full ---
*** [atf-check.full] Error code 1

bmake[5]: stopped making "all" in /Users/kostik/work/DEV/src/libexec/atf/atf-check
bmake[5]: 1 error

XCXX=/opt/homebrew/opt/llvm@18/bin/clang \

What about with clang++? Otherwise the driver won't link in libc++.

XCXX=/opt/homebrew/opt/llvm@18/bin/clang \

What about with clang++? Otherwise the driver won't link in libc++.

Indeed, thank you. This allowed me to do buildworld + GENERIC buildkernel.

This passed the GitHub tests as well (which still checks older macOS), so I think this change is ok to land?

This isn't going to suffice. If you update your SDK to one that knows about 15.4 or later then you'll have the prototype in string.h and __MAC_OS_X_VERSION_MAX_ALLOWED will be 15.4 or higher, but we won't provide this implementation, so at compile time you'll get warning: 'strchrnul' is only available on macOS 15.4 or newer [-Wunguarded-availability-new] and at run time you'll get a segmentation fault from the symbol stub (Mach-O PLT-like thing) branching to NULL.

What we probably need is

#define strchrnul(p, ch) __freebsd_strchrnul(p, ch)

just prior to this definition so that ours doesn't collide with the system's that may or may not exist and all uses in bootstrap code are redirected to our implementation here.

This isn't going to suffice. If you update your SDK to one that knows about 15.4 or later then you'll have the prototype in string.h and __MAC_OS_X_VERSION_MAX_ALLOWED will be 15.4 or higher, but we won't provide this implementation, so at compile time you'll get warning: 'strchrnul' is only available on macOS 15.4 or newer [-Wunguarded-availability-new] and at run time you'll get a segmentation fault from the symbol stub (Mach-O PLT-like thing) branching to NULL.

What we probably need is

#define strchrnul(p, ch) __freebsd_strchrnul(p, ch)

just prior to this definition so that ours doesn't collide with the system's that may or may not exist and all uses in bootstrap code are redirected to our implementation here.

Yeah, the availability macros aren't really suited to our desire of "do you have this" that is more like a configure test. :( The hack is fine I guess.

jhb retitled this revision from cross-build: Don't provide strchrnul on macOS 15.4+. to cross-build: Workaround system-provided strchrnul on macOS 15.4+.Apr 21 2025, 6:46 PM
jhb edited the summary of this revision. (Show Details)
This revision is now accepted and ready to land.Apr 21 2025, 7:01 PM
tools/build/cross-build/include/mac/string.h
49

Wouldn't this need to be spelled as __freebsd_strchrnul ?

tools/build/cross-build/include/mac/string.h
49

No, the macro substitution applies to declarations and definitions too (which is why sometimes (foo)(arg1, arg2) is used to prevent the expansion of function-like macros in such instances).

Can we please have this committed?