From 6f100d3f61d5b0f7c1ecaa23d1af1e215f88ca33 Mon Sep 17 00:00:00 2001 From: javierUDP Date: Thu, 15 Oct 2026 15:40:26 -0300 Subject: [PATCH 1/2] test(relinker): execute the AMD64-only conversion stubs on macOS The execution part of the AMD64-only converter tests was compiled for Linux x86-64 only, so the SSE4A, SHA-1, SHA-256, CLZERO and reciprocal stubs were never run on a macOS x86-64 build, unlike the ANDN lowering tests. Rosetta maps no memory below 4 GiB, so the CLZERO case that needs a 32-bit address is left out when that mapping is unavailable. --- .../codegen/tests/Amd64OnlyConverterTests.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp index c97cea6a0b..fa6930c21a 100644 --- a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp +++ b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp @@ -24,7 +24,7 @@ #include #include #include -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include #endif #include @@ -1289,7 +1289,7 @@ void linuxRipRelativePlacement() { } -#if defined(__linux__) && defined(__x86_64__) +#if (defined(__linux__) || defined(__APPLE__)) && defined(__x86_64__) std::uint64_t extrqReference(std::uint64_t value, std::uint64_t control) { const auto length = static_cast(control & 0x3f); const auto index = static_cast((control >> 8) & 0x3f); @@ -1304,6 +1304,12 @@ std::uint64_t insertqReference(std::uint64_t destination, std::uint64_t value, s return (destination & ~(mask << index)) | ((value & mask) << index); } +void* mapBuffer(const bool low) { + const int flags = MAP_PRIVATE | MAP_ANONYMOUS | (low ? MAP_32BIT : 0); + auto* buffer = mmap(nullptr, 4096, PROT_READ | PROT_WRITE, flags, -1, 0); + return buffer == MAP_FAILED ? nullptr : buffer; +} + constexpr std::uint64_t kStubScratch[2] = {0x0123456789abcdefull, 0xfedcba9876543210ull}; std::uint32_t rotr(const std::uint32_t value, const unsigned count) { @@ -1580,8 +1586,10 @@ void clzeroExecution() { } for (const auto& item : cases) { require(item.Match.has_value(), item.Name); - auto* buffer = static_cast(mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | (item.Low ? MAP_32BIT : 0), -1, 0)); - require(buffer != MAP_FAILED, "cannot map the CLZERO buffer"); + auto* buffer = static_cast(mapBuffer(item.Low)); + if (buffer == nullptr && item.Low) + continue; + require(buffer != nullptr, "cannot map the CLZERO buffer"); for (const std::size_t offset : {std::size_t{0x80}, std::size_t{0xAD}, std::size_t{0xFF}}) { std::memset(buffer, 0xA5, 4096); const auto address = reinterpret_cast(buffer + offset); From d67e7b7389e7d66a65c3208f1bdd935d1332d5f2 Mon Sep 17 00:00:00 2001 From: javierUDP Date: Thu, 15 Oct 2026 17:10:59 -0300 Subject: [PATCH 2/2] test(relinker): link the converter tests with a small macOS zero page The CLZERO case that needs a 32-bit address found no mapping because a 64-bit Mach-O executable reserves 4 GiB as `__PAGEZERO`, not because Rosetta withholds low memory: the same `mmap` succeeds once the executable is linked with a smaller zero page. The Apple x86-64 build links the test that way and the case runs with the other three, so the skip it had is gone and the Linux mapping failure stays fatal. The arm64 build keeps the 4 GiB zero page, which it requires to start. --- core/relinker/CMakeLists.txt | 3 +++ .../codegen/tests/Amd64OnlyConverterTests.cpp | 12 ++---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index d227fc017d..51dc17dbad 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -375,6 +375,9 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/include ${CMAKE_CURRENT_SOURCE_DIR}/io/include ) + if(APPLE AND (CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" OR (NOT CMAKE_OSX_ARCHITECTURES AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64"))) + target_link_options(amd64_only_converter_tests PRIVATE "LINKER:-pagezero_size,0x10000") + endif() add_test(NAME amd64_only_converter COMMAND amd64_only_converter_tests) add_test_executable(call_site_resolver_tests ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/CallSiteResolverTests.cpp diff --git a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp index fa6930c21a..b88e1323d8 100644 --- a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp +++ b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp @@ -1304,12 +1304,6 @@ std::uint64_t insertqReference(std::uint64_t destination, std::uint64_t value, s return (destination & ~(mask << index)) | ((value & mask) << index); } -void* mapBuffer(const bool low) { - const int flags = MAP_PRIVATE | MAP_ANONYMOUS | (low ? MAP_32BIT : 0); - auto* buffer = mmap(nullptr, 4096, PROT_READ | PROT_WRITE, flags, -1, 0); - return buffer == MAP_FAILED ? nullptr : buffer; -} - constexpr std::uint64_t kStubScratch[2] = {0x0123456789abcdefull, 0xfedcba9876543210ull}; std::uint32_t rotr(const std::uint32_t value, const unsigned count) { @@ -1586,10 +1580,8 @@ void clzeroExecution() { } for (const auto& item : cases) { require(item.Match.has_value(), item.Name); - auto* buffer = static_cast(mapBuffer(item.Low)); - if (buffer == nullptr && item.Low) - continue; - require(buffer != nullptr, "cannot map the CLZERO buffer"); + auto* buffer = static_cast(mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | (item.Low ? MAP_32BIT : 0), -1, 0)); + require(buffer != MAP_FAILED, "cannot map the CLZERO buffer"); for (const std::size_t offset : {std::size_t{0x80}, std::size_t{0xAD}, std::size_t{0xFF}}) { std::memset(buffer, 0xA5, 4096); const auto address = reinterpret_cast(buffer + offset);