From c27787a7db406ddff2872479fc1030b5dc6fff29 Mon Sep 17 00:00:00 2001 From: mugurc Date: Mon, 12 Oct 2026 15:12:03 +0300 Subject: [PATCH] fix(libc): build the host-facing libc code on macOS feof_unlocked, ferror_unlocked, getc_unlocked and putc_unlocked are macros on macOS, so they are called without the global scope qualifier. The remaining differences are the ones Darwin's headers and libSystem have: - CLOCK_REALTIME_COARSE and st_mtim do not exist; the write mark uses CLOCK_REALTIME and st_mtimespec. - tm_zone is a non-const char*. - __cxa_thread_atexit returns void, so the wrapper reports success. - Darwin's stack protector reads __stack_chk_guard from libSystem; the guard of the guest libc has the postfixed name, which receives the NID of the original. - APS5_EXPORT spells the symbols with the leading underscore of Mach-O. - GuestArenaSetProtection and PosixProtection are built on macOS as on Linux. - The fclose failure test fills /dev/full, which exists on Linux only. exception_runtime relies on the Itanium unwinder of libc, which is not registered on macOS (the host libc++abi is used), so its test is only registered elsewhere. --- core/libs/prx/libc/CMakeLists.txt | 8 +++---- core/libs/prx/libc/include/FileStream.hpp | 4 ++-- .../prx/libc/include/general/ExportMacros.hpp | 6 ++++- core/libs/prx/libc/src/CxxAbiSupport.cpp | 22 +++++++++++-------- core/libs/prx/libc/src/General.cpp | 8 +++++++ core/libs/prx/libc/src/StandardStreams.cpp | 4 ++-- core/libs/prx/libc/src/Time.cpp | 4 ++++ .../prx/libc/src/specifics/gcc/StackGuard.cpp | 4 ++++ core/libs/tests/Formatting.cpp | 7 ++++++ core/libs/tests/GuestFiopen.cpp | 2 +- 10 files changed, 50 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index d53c2d04611..68e970b46d8 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -817,10 +817,6 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$") endif() if(UNIX) - add_test_executable(exception_runtime_tests ${LIBS_INCLUDE_DIR}/tests/ExceptionRuntime.cpp) - target_include_directories(exception_runtime_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(exception_runtime_tests PRIVATE libc) - add_test(NAME exception_runtime COMMAND exception_runtime_tests) add_test_executable(exception_personality_tests ${LIBS_INCLUDE_DIR}/tests/ExceptionPersonality.cpp) target_include_directories(exception_personality_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(exception_personality_tests PRIVATE libc) @@ -835,6 +831,10 @@ if(BUILD_TESTING) add_test(NAME guest_path_lifetime COMMAND guest_path_lifetime_tests) set_tests_properties(guest_path_lifetime PROPERTIES TIMEOUT 20) if(NOT APPLE) + add_test_executable(exception_runtime_tests ${LIBS_INCLUDE_DIR}/tests/ExceptionRuntime.cpp) + target_include_directories(exception_runtime_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(exception_runtime_tests PRIVATE libc) + add_test(NAME exception_runtime COMMAND exception_runtime_tests) enable_language(C) add_test_executable(exception_c_cleanup_tests ${LIBS_INCLUDE_DIR}/tests/ExceptionCCleanup.cpp ${LIBS_INCLUDE_DIR}/tests/ExceptionCCleanup.c) set_source_files_properties(${LIBS_INCLUDE_DIR}/tests/ExceptionCCleanup.c PROPERTIES COMPILE_OPTIONS -fexceptions) diff --git a/core/libs/prx/libc/include/FileStream.hpp b/core/libs/prx/libc/include/FileStream.hpp index 7a81d29f076..1056a6d4c64 100644 --- a/core/libs/prx/libc/include/FileStream.hpp +++ b/core/libs/prx/libc/include/FileStream.hpp @@ -228,8 +228,8 @@ class FileStream { const bool endOfFile = std::feof(GetHandle()) != 0; const bool error = std::ferror(GetHandle()) != 0; #else - const bool endOfFile = ::feof_unlocked(GetHandle()) != 0; - const bool error = ::ferror_unlocked(GetHandle()) != 0; + const bool endOfFile = feof_unlocked(GetHandle()) != 0; + const bool error = ferror_unlocked(GetHandle()) != 0; #endif _guest.flags = static_cast((_guest.flags & ~0x60) | (endOfFile ? 0x20 : 0) | (error || guestError ? 0x40 : 0)); diff --git a/core/libs/prx/libc/include/general/ExportMacros.hpp b/core/libs/prx/libc/include/general/ExportMacros.hpp index 49ab7fbf8ce..c175c74f2bf 100644 --- a/core/libs/prx/libc/include/general/ExportMacros.hpp +++ b/core/libs/prx/libc/include/general/ExportMacros.hpp @@ -1,7 +1,11 @@ #ifndef CORE_LIBS_PRX_LIBC_INCLUDE_GENERAL_EXPORTMACROS_HPP #define CORE_LIBS_PRX_LIBC_INCLUDE_GENERAL_EXPORTMACROS_HPP -#if defined(__GNUC__) || defined(__clang__) +#if defined(__APPLE__) + #define APS5_EXPORT(exportName, funcName) \ + __asm__(".globl \"_" exportName "_nid_no_patch_cut\"\n\t" \ + ".set \"_" exportName "_nid_no_patch_cut\", _" #funcName) +#elif defined(__GNUC__) || defined(__clang__) #define APS5_EXPORT(exportName, funcName) \ __asm__(".globl \"" exportName "_nid_no_patch_cut\"\n\t" \ ".set \"" exportName "_nid_no_patch_cut\", " #funcName) diff --git a/core/libs/prx/libc/src/CxxAbiSupport.cpp b/core/libs/prx/libc/src/CxxAbiSupport.cpp index 1cbe9c38cd0..ccecea01a89 100644 --- a/core/libs/prx/libc/src/CxxAbiSupport.cpp +++ b/core/libs/prx/libc/src/CxxAbiSupport.cpp @@ -74,18 +74,22 @@ int APS5_VABI __cxa_thread_atexit_impl_nid_postfix(void (APS5_VABI *func)(void*) auto* context = new (std::nothrow) ThreadAtexitContext{func, arg}; if (context == nullptr) return -1; - const int result = __cxxabiv1::__cxa_thread_atexit( - [](void* opaque) { - auto* context = static_cast(opaque); - const auto destructor = context->destructor; - void* object = context->object; - delete context; - destructor(object); - }, - context, dso); + const auto run = [](void* opaque) { + auto* context = static_cast(opaque); + const auto destructor = context->destructor; + void* object = context->object; + delete context; + destructor(object); + }; +#ifdef __APPLE__ + __cxxabiv1::__cxa_thread_atexit(run, context, dso); + return 0; +#else + const int result = __cxxabiv1::__cxa_thread_atexit(run, context, dso); if (result != 0) delete context; return result; +#endif } int APS5_VABI __cxa_thread_atexit_nid_postfix(void (APS5_VABI *func)(void*), void* arg, void* dso) { diff --git a/core/libs/prx/libc/src/General.cpp b/core/libs/prx/libc/src/General.cpp index 26c1935c5ae..6bbe2aafc79 100644 --- a/core/libs/prx/libc/src/General.cpp +++ b/core/libs/prx/libc/src/General.cpp @@ -376,7 +376,11 @@ bool NotBefore(const WriteMark& time, const WriteMark& mark) { using WriteMark = timespec; WriteMark Now() { timespec now; +#ifdef __APPLE__ + clock_gettime(CLOCK_REALTIME, &now); +#else clock_gettime(CLOCK_REALTIME_COARSE, &now); +#endif return now; } bool NotBefore(const WriteMark& time, const WriteMark& mark) { @@ -415,7 +419,11 @@ void FlushPath(const std::filesystem::path& path) { #else std::optional WriteTime(const std::filesystem::path& path) { struct stat data; +#ifdef __APPLE__ + if (::stat(path.c_str(), &data) == 0) return data.st_mtimespec; +#else if (::stat(path.c_str(), &data) == 0) return data.st_mtim; +#endif if (errno == ENOENT || errno == ENOTDIR) return std::nullopt; throw std::system_error(errno, std::generic_category(), "Reading the write time of " + path.string()); } diff --git a/core/libs/prx/libc/src/StandardStreams.cpp b/core/libs/prx/libc/src/StandardStreams.cpp index 2cd656f01fa..4d8c34a019c 100644 --- a/core/libs/prx/libc/src/StandardStreams.cpp +++ b/core/libs/prx/libc/src/StandardStreams.cpp @@ -92,7 +92,7 @@ int APS5_VABI getc_unlocked_nid_postfix(FileStream* stream) { #ifdef _WIN32 const int result = _fgetc_nolock(native); #else - const int result = ::getc_unlocked(native); + const int result = getc_unlocked(native); #endif stream->SyncUnlockedStatus(); stream->KeepDirectoryReadError(result == -1); @@ -115,7 +115,7 @@ int APS5_VABI putc_unlocked_nid_postfix(int value, FileStream* stream) { #ifdef _WIN32 const int result = _fputc_nolock(value, GetNativeStream(stream)); #else - const int result = ::putc_unlocked(value, GetNativeStream(stream)); + const int result = putc_unlocked(value, GetNativeStream(stream)); #endif stream->SyncUnlockedStatus(); return result; diff --git a/core/libs/prx/libc/src/Time.cpp b/core/libs/prx/libc/src/Time.cpp index c3499f8047b..f732111f277 100644 --- a/core/libs/prx/libc/src/Time.cpp +++ b/core/libs/prx/libc/src/Time.cpp @@ -101,7 +101,11 @@ std::tm toHostTm(const GuestTm& guest) { host.tm_isdst = guest.tm_isdst; #ifndef _WIN32 host.tm_gmtoff = static_cast(guest.tm_gmtoff); +#ifdef __APPLE__ + host.tm_zone = const_cast(guest.tm_zone); +#else host.tm_zone = guest.tm_zone; +#endif #endif return host; } diff --git a/core/libs/prx/libc/src/specifics/gcc/StackGuard.cpp b/core/libs/prx/libc/src/specifics/gcc/StackGuard.cpp index bae022e0013..a18ff70dc90 100644 --- a/core/libs/prx/libc/src/specifics/gcc/StackGuard.cpp +++ b/core/libs/prx/libc/src/specifics/gcc/StackGuard.cpp @@ -1,4 +1,8 @@ #include #include +#ifdef __APPLE__ +extern "C" std::uintptr_t __stack_chk_guard_nid_postfix = 0xDEADBEEFCAFEBABEull; +#else std::uintptr_t __stack_chk_guard = 0xDEADBEEFCAFEBABEull; +#endif diff --git a/core/libs/tests/Formatting.cpp b/core/libs/tests/Formatting.cpp index f22d7880dd8..987a120d950 100644 --- a/core/libs/tests/Formatting.cpp +++ b/core/libs/tests/Formatting.cpp @@ -10,6 +10,13 @@ #include #include #include +#include + +#if defined(__clang__) && !defined(_WIN32) +#define __builtin_sysv_va_list va_list +#define __builtin_sysv_va_start va_start +#define __builtin_sysv_va_end va_end +#endif extern "C" { int APS5_VABI snprintf_nid_postfix(char*, size_t, const char*, ...); diff --git a/core/libs/tests/GuestFiopen.cpp b/core/libs/tests/GuestFiopen.cpp index 276459ec07b..044265f11c6 100644 --- a/core/libs/tests/GuestFiopen.cpp +++ b/core/libs/tests/GuestFiopen.cpp @@ -152,7 +152,7 @@ int main() { Require(stream != nullptr && std::fgetc(stream->GetHandle()) == 'k'); fclose_nid_postfix(stream); -#ifndef _WIN32 +#ifdef __linux__ auto* full = std::fopen("/dev/full", "wb"); Require(full != nullptr); auto* fullStream = new FileStream(full, true);