Repository: libsdl-org/SDL (C)

Issue:
arm64 asm, __chkstk, and cmake
Our current MSVC code is missing `__chkstk` for arm64 and we
are blaming cmake for it referencing cmake issue reported at
https://gitlab.kitware.com/cmake/cmake/-/issues/18912

Any progress on this?

I found armasm support merges among cmake gitlab PRs. along
with some open issues - other and newer than the one above:
https://gitlab.kitware.com/cmake/cmake/-/merge_requests/7793
https://gitlab.kitware.com/cmake/cmake/-/issues/24317

@madebr ?



Discussion:
Any progress on this?
We are scoping work for the SDL 3.2.0 release, so please let us know if this is a showstopper for you.
Well, it _can_ be a showstopper. I don't have anything to test with, though.
Me either... let's leave this open until someone has a repro environment.
#11761 addresses this. I copied `__chkstk` from a arm64 SDL3.dll.
But it's 100% not tested.



Patch:
diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml
index 1398bca8381d5..84e1d97fe7a2f 100644
--- a/.github/workflows/generic.yml
+++ b/.github/workflows/generic.yml
@@ -207,7 +207,7 @@ jobs:
           ctest -VV --test-dir build/ -j2
       - name: "Build test apk's (CMake)"
         id: apks
-        if: ${{ steps.build.outcome == 'success' && matrix.platform.android-apks != '' }}
+        if: ${{ always() && steps.build.outcome == 'success' && matrix.platform.android-apks != '' }}
 #        shell: ${{ matrix.platform.shell }}
         run: |
           ${{ matrix.platform.source-cmd }}
@@ -218,7 +218,7 @@ jobs:
             -- ${{ matrix.platform.cmake-build-arguments }}
       - name: 'Install (CMake)'
         id: install
-        if: ${{ steps.build.outcome == 'success' }}
+        if: ${{ always() && steps.build.outcome == 'success' }}
 #        shell: ${{ matrix.platform.shell }}
         run: |
           ${{ matrix.platform.source-cmd }}
@@ -227,7 +227,7 @@ jobs:
           ( cd prefix; find . ) | LC_ALL=C sort -u
       - name: 'Package (CPack)'
         id: package
-        if: ${{ steps.build.outcome == 'success' }}
+        if: ${{ always() && steps.build.outcome == 'success' }}
 #        shell: ${{ matrix.platform.shell }}
         run: |
           # DMG creation on macOS occasionally fails, so try multiple times
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 94b1c9bfe0531..5c4f9cbd412f9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -498,16 +498,21 @@ else()
   sdl_include_directories(NO_EXPORT SYSTEM PRIVATE "$<BUILD_INTERFACE:${SDL3_SOURCE_DIR}/src/video/khronos>")
 endif()
 
-if(MSVC AND TARGET SDL3-shared)
+if(MSVC AND TARGET SDL3-shared AND NOT SDL_LIBC)
   if(SDL_CPU_X64)
     enable_language(ASM_MASM)
     set(asm_src "${SDL3_SOURCE_DIR}/src/stdlib/SDL_mslibc_x64.masm")
     target_compile_options(SDL3-shared PRIVATE "$<$<COMPILE_LANGUAGE:ASM_MASM>:/nologo>")
     set_property(SOURCE "${asm_src}" PROPERTY LANGUAGE "ASM_MASM")
     target_sources(SDL3-shared PRIVATE "${asm_src}")
-  elseif(SDL_CPU_ARM32 OR SDL_CPU_ARM64)
-    # FIXME: ARM assembler (armasm.exe/armasm64.exe) is NOT ASM_MASM, and does currently not work with CMake
-    # (https://gitlab.kitware.com/cmake/cmake/-/issues/18912)
+  elseif(SDL_CPU_ARM64)
+    enable_language(ASM_MARMASM)
+    set(asm_src "${SDL3_SOURCE_DIR}/src/stdlib/SDL_mslibc_arm64.masm")
+    target_compile_options(SDL3-shared PRIVATE "$<$<COMPILE_LANGUAGE:ASM_MARMASM>:/nologo>")
+    set_property(SOURCE "${asm_src}" PROPERTY LANGUAGE "ASM_MARMASM")
+    target_sources(SDL3-shared PRIVATE "${asm_src}")
+  elseif(SDL_CPU_ARM32)
+    # FIXME
   endif()
 endif()
 
@@ -1889,8 +1894,10 @@ elseif(WINDOWS)
   if(TARGET SDL3-shared AND MSVC AND NOT SDL_LIBC)
     # Prevent codegen that would use the VC runtime libraries.
     target_compile_options(SDL3-shared PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:/GS-> $<$<COMPILE_LANGUAGE:C,CXX>:/Gs1048576>)
+    target_compile_options(SDL_uclibc PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:/GS-> $<$<COMPILE_LANGUAGE:C,CXX>:/Gs1048576>)
     if(SDL_CPU_X86)
       target_compile_options(SDL3-shared PRIVATE "/arch:SSE")
+      target_compile_options(SDL3-SDL_uclibc PRIVATE "/arch:SSE")
     endif()
   endif()
 
@@ -3388,6 +3395,17 @@ if(SDL_SHARED)
         # (__rt_sdiv, __rt_udiv, __rt_sdiv64, _rt_udiv64, __dtou64, __u64tod, __i64tos)
         target_link_libraries(SDL3-shared PRIVATE msvcrt.lib)
       endif()
+      find_library(HAVE_ONECORE_LIB NAMES "onecore.lib")
+      if(HAVE_ONECORE_LIB)
+        # SDL_malloc.c: __imp_MapViewOfFileNuma2 referenced in function MapViewOfFile2
+        target_link_libraries(SDL3-shared PRIVATE onecore.lib)
+      endif()
+      find_library(HAVE_VOLATILEACCESSU_LIB NAMES "volatileaccessu.lib")
+      if(HAVE_VOLATILEACCESSU_LIB)
+        # SDL_malloc.c : RtlSetVolatileMemory referenced in function RtlFillVolatileMemory
+        # SDL_malloc.c : RtlFillDeviceMemory referenced in function RtlZeroDeviceMemory
+        target_link_libraries(SDL3-shared PRIVATE volatileaccessu.lib)
+      endif()
     endif()
     if(HAS_Q_NO_USE_LIBIRC)
       target_compile_options(SDL3-shared PRIVATE /Q_no-use-libirc)
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index e860035ae625a..ada62e856d8c7 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -43,7 +43,6 @@
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
-    <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
   </ImportGroup>
   <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
@@ -647,9 +646,6 @@
     <ClCompile Include="..\..\src\stdlib\SDL_memmove.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_memset.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_mslibc.c" />
-    <MASM Condition="'$(Platform)'=='x64'" Include="..\..\src\stdlib\SDL_mslibc_x64.masm">
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-    </MASM>
     <ClCompile Include="..\..\src\stdlib\SDL_murmur3.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_qsort.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_random.c" />
@@ -729,6 +725,5 @@
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
-    <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
   </ImportGroup>
 </Project>
diff --git a/cmake/sdlcompilers.cmake b/cmake/sdlcompilers.cmake
index ec3b6124cade0..c3d8c4702e6ad 100644
--- a/cmake/sdlcompilers.cmake
+++ b/cmake/sdlcompilers.cmake
@@ -34,7 +34,7 @@ function(SDL_AddCommonCompilerFlags TARGET)
     cmake_push_check_state()
     check_c_compiler_flag("/W3" COMPILER_SUPPORTS_W3)
     if(COMPILER_SUPPORTS_W3)
-      target_compile_options(${TARGET} PRIVATE "/W3")
+      target_compile_options(${TARGET} PRIVATE "$<$<COMPILE_LANGUAGE:C,CXX>:/W3>")
     endif()
     cmake_pop_check_state()
   endif()
@@ -131,7 +131,7 @@ function(SDL_AddCommonCompilerFlags TARGET)
     if(MSVC)
       check_c_compiler_flag(/WX HAVE_WX)
       if(HAVE_WX)
-        target_compile_options(${TARGET} PRIVATE "/WX")
+        target_compile_options(${TARGET} PRIVATE "$<$<COMPILE_LANGUAGE:C,CXX>:/WX>")
       endif()
     elseif(USE_GCC OR USE_CLANG OR USE_INTELCC OR USE_QNX)
       check_c_compiler_flag(-Werror HAVE_WERROR)
diff --git a/src/SDL_hashtable.c b/src/SDL_hashtable.c
index a3f6613cd0096..571e376974177 100644
--- a/src/SDL_hashtable.c
+++ b/src/SDL_hashtable.c
@@ -22,8 +22,31 @@
 #include "SDL_hashtable.h"
 
 // XXX: We can't use SDL_assert here because it's going to call into hashtable code
-#include <assert.h>
-#define HT_ASSERT(x) assert(x)
+#ifdef NDEBUG
+#define HT_ASSERT(x) (void)(0)
+#else
+#if (defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN)) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
+#include <windows.h>
+#endif
+/* This is not declared in any header, although it is shared between some
+   parts of SDL, because we don't want anything calling it without an
+   extremely good reason. */
+extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
+static void HT_ASSERT_FAIL(const char *msg)
+{
+    const char *caption = "SDL_HashTable Assertion Failure!";
+    (void)caption;
+#if (defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN)) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
+    MessageBoxA(NULL, msg, caption, MB_OK | MB_ICONERROR);
+#elif defined(HAVE_STDIO_H)
+    fprintf(stderr, "\n\n%s\n%s\n\n", caption, msg);
+    fflush(stderr);
+#endif
+    SDL_TriggerBreakpoint();
+    SDL_ExitProcess(-1);
+}
+#define HT_ASSERT(x) if (!(x)) HT_ASSERT_FAIL("SDL_HashTable Assertion Failure: " #x)
+#endif
 
 typedef struct SDL_HashItem
 {
diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c
index 2fa85ec5f9920..f35f3e8bcb564 100644
--- a/src/dynapi/SDL_dynapi.c
+++ b/src/dynapi/SDL_dynapi.c
@@ -520,6 +520,9 @@ static void SDL_InitDynamicAPILocked(void)
         if (initialize_jumptable(SDL_DYNAPI_VERSION, &jump_table, sizeof(jump_table)) < 0) {
             // Now we're screwed. Should definitely abort now.
             dynapi_warn("Failed to initialize internal SDL dynapi. As this would otherwise crash, we have to abort now.");
+#ifndef NDEBUG
+            SDL_TriggerBreakpoint();
+#endif
             SDL_ExitProcess(86);
         }
     }
diff --git a/src/stdlib/SDL_malloc.c b/src/stdlib/SDL_malloc.c
index a4de7d24ac545..ea09d1e5cbf77 100644
--- a/src/stdlib/SDL_malloc.c
+++ b/src/stdlib/SDL_malloc.c
@@ -28,8 +28,8 @@
 #define LACKS_STRINGS_H
 #define LACKS_STRING_H
 #define LACKS_STDLIB_H
-#define FORCEINLINE
 #define ABORT
+#define NO_MALLOC_STATS 1
 #define USE_LOCKS 1
 #define USE_DL_PREFIX
 
@@ -816,6 +816,7 @@ struct mallinfo {
   inlining are defined as macros, so these aren't used for them.
 */
 
+#if 0 /* SDL */
 #ifndef FORCEINLINE
   #if defined(__GNUC__)
 #define FORCEINLINE __inline __attribute__ ((always_inline))
@@ -823,6 +824,7 @@ struct mallinfo {
     #define FORCEINLINE __forceinline
   #endif
 #endif
+#endif /* SDL */
 #ifndef NOINLINE
   #if defined(__GNUC__)
     #define NOINLINE __attribute__ ((noinline))
@@ -835,13 +837,17 @@ struct mallinfo {
 
 #ifdef __cplusplus
 extern "C" {
+#if 0 /* SDL */
 #ifndef FORCEINLINE
  #define FORCEINLINE inline
 #endif
+#endif /* SDL */
 #endif /* __cplusplus */
+#if 0 /* SDL */
 #ifndef FORCEINLINE
  #define FORCEINLINE
 #endif
+#endif /* SDL_FORCE_INLINE */
 
 #if !ONLY_MSPACES
 
@@ -1697,20 +1703,20 @@ static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
 #else /* WIN32 */
 
 /* Win32 MMAP via VirtualAlloc */
-static FORCEINLINE void* win32mmap(size_t size) {
+SDL_FORCE_INLINE void* win32mmap(size_t size) {
   void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
   return (ptr != 0)? ptr: MFAIL;
 }
 
 /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
-static FORCEINLINE void* win32direct_mmap(size_t size) {
+SDL_FORCE_INLINE void* win32direct_mmap(size_t size) {
   void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
                            PAGE_READWRITE);
   return (ptr != 0)? ptr: MFAIL;
 }
 
 /* This function supports releasing coalesed segments */
-static FORCEINLINE int win32munmap(void* ptr, size_t size) {
+SDL_FORCE_INLINE int win32munmap(void* ptr, size_t size) {
   MEMORY_BASIC_INFORMATION minfo;
   char* cptr = (char*)ptr;
   while (size) {
@@ -1863,7 +1869,7 @@ static FORCEINLINE int win32munmap(void* ptr, size_t size) {
 
 #elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))
 /* Custom spin locks for older gcc on x86 */
-static FORCEINLINE int x86_cas_lock(int *sl) {
+SDL_FORCE_INLINE int x86_cas_lock(int *sl) {
   int ret;
   int val = 1;
   int cmp = 0;
@@ -1874,7 +1880,7 @@ static FORCEINLINE int x86_cas_lock(int *sl) {
   return ret;
 }
 
-static FORCEINLINE void x86_clear_lock(int* sl) {
+SDL_FORCE_INLINE void x86_clear_lock(int* sl) {
   assert(*sl != 0);
   int prev = 0;
   int ret;
@@ -1952,14 +1958,14 @@ struct malloc_recursive_lock {
 #define MLOCK_T  struct malloc_recursive_lock
 static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0};
 
-static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) {
+SDL_FORCE_INLINE void recursive_release_lock(MLOCK_T *lk) {
   assert(lk->sl != 0);
   if (--lk->c == 0) {
     CLEAR_LOCK(&lk->sl);
   }
 }
 
-static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) {
+SDL_FORCE_INLINE int recursive_acquire_lock(MLOCK_T *lk) {
   THREAD_ID_T mythreadid = CURRENT_THREAD;
   int spins = 0;
   for (;;) {
@@ -1980,7 +1986,7 @@ static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) {
   }
 }
 
-static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) {
+SDL_FORCE_INLINE int recursive_try_lock(MLOCK_T *lk) {
   THREAD_ID_T mythreadid = CURRENT_THREAD;
   if (*((volatile int *)(&lk->sl)) == 0) {
     if (!CAS_LOCK(&lk->sl)) {
diff --git a/src/stdlib/SDL_mslibc.c b/src/stdlib/SDL_mslibc.c
index a527bf75d6bdf..6698403fe8904 100644
--- a/src/stdlib/SDL_mslibc.c
+++ b/src/stdlib/SDL_mslibc.c
@@ -728,14 +728,6 @@ void __declspec(naked) _alloca_probe_16(void)
 
 #endif // _M_IX86
 
-#ifdef _M_ARM64
-
-void __chkstk(void);
-void __chkstk() {
-}
-
-#endif
-
 #endif // MSC_VER
 
 #ifdef __ICL
diff --git a/src/stdlib/SDL_mslibc_arm64.masm b/src/stdlib/SDL_mslibc_arm64.masm
new file mode 100644
index 0000000000000..a769cc106f5ff
--- /dev/null
+++ b/src/stdlib/SDL_mslibc_arm64.masm
@@ -0,0 +1,26 @@
+TeStackLimit EQU 0x00010
+PAGE_SIZE equ 0x1000
+
+    AREA CODE, READONLY
+
+    EXPORT __chkstk
+
+__chkstk PROC
+    ldr                          x17,[x18, #TeStackLimit]
+    subs                         x16,sp,x15, LSL  #0x4
+    csel                         x16,xzr,x16,cc
+    cmp                          x16,x17
+    b.cc                         chkstk_start_loop
+    ret
+chkstk_start_loop
+    and                          x16,x16,#-PAGE_SIZE
+chkstk_loop
+    sub                          x17,x17,#0x1, LSL #12
+    ldr                          xzr,[x17]
+    cmp                          x17,x16
+    b.ne                         chkstk_loop
+    ret
+
+    ENDP
+
+    END
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index ab43c13d3a850..50da685f7eb57 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -368,7 +368,7 @@ static size_t SDL_ScanUnsignedLongLongInternal(const char *text, int count, int
             negative = *text == '-';
             ++text;
         }
-        if ((radix == 0 || radix == 16) && *text == '0') {
+        if ((radix == 0 || radix == 16) && *text == '0' && text[1] != '\0') {
             ++text;
             if (*text == 'x' || *text == 'X') {
                 radix = 16;


Test patch:
diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c
index a2ac6719884b7..26317584f19bc 100644
--- a/test/testautomation_stdlib.c
+++ b/test/testautomation_stdlib.c
@@ -1324,6 +1324,9 @@ static int SDLCALL stdlib_strtox(void *arg)
     STRTOX_TEST_CASE(SDL_strtoull, unsigned long long, FMT_PRILLu, "-uvwxyz", 32, -991, 3);
     STRTOX_TEST_CASE(SDL_strtoull, unsigned long long, FMT_PRILLu, "ZzZzZzZzZzZzZzZzZzZzZzZzZ", 36, ullong_max, 25);
 
+    STRTOX_TEST_CASE(SDL_strtoull, unsigned long long, FMT_PRILLu, "0", 0, 0, 1);
+    STRTOX_TEST_CASE(SDL_strtoull, unsigned long long, FMT_PRILLu, "0", 10, 0, 1);
+    STRTOX_TEST_CASE(SDL_strtoull, unsigned long long, FMT_PRILLu, "-0", 0, 0, 2);
     STRTOX_TEST_CASE(SDL_strtoull, unsigned long long, FMT_PRILLu, "-0", 10, 0, 2);
     STRTOX_TEST_CASE(SDL_strtoull, unsigned long long, FMT_PRILLu, " - 1", 0, 0, 0); // invalid input
 
@@ -1335,6 +1338,10 @@ static int SDLCALL stdlib_strtox(void *arg)
     // Since the CI runs the tests against a variety of targets, this should be fine in practice.
 
     if (sizeof(long) == 4) {
+        STRTOX_TEST_CASE(SDL_strtol, long, "%ld", "0", 0, 0, 1);
+        STRTOX_TEST_CASE(SDL_strtol, long, "%ld", "0", 10, 0, 1);
+        STRTOX_TEST_CASE(SDL_strtol, long, "%ld", "-0", 0, 0, 2);
+        STRTOX_TEST_CASE(SDL_strtol, long, "%ld", "-0", 10, 0, 2);
         STRTOX_TEST_CASE(SDL_strtol, long, "%ld", "2147483647", 10, 2147483647, 10);
         STRTOX_TEST_CASE(SDL_strtol, long, "%ld", "2147483648", 10, 2147483647, 10);
         STRTOX_TEST_CASE(SDL_strtol, long, "%ld", "-2147483648", 10, -2147483647L - 1, 11);
@@ -1347,6 +1354,10 @@ static int SDLCALL stdlib_strtox(void *arg)
     }
 
     if (sizeof(long long) == 8) {
+        STRTOX_TEST_CASE(SDL_strtoll, long long, FMT_PRILLd, "0", 0, 0LL, 1);
+        STRTOX_TEST_CASE(SDL_strtoll, long long, FMT_PRILLd, "0", 10, 0LL, 1);
+        STRTOX_TEST_CASE(SDL_strtoll, long long, FMT_PRILLd, "-0", 0, 0LL, 2);
+        STRTOX_TEST_CASE(SDL_strtoll, long long, FMT_PRILLd, "-0", 10, 0LL, 2);
         STRTOX_TEST_CASE(SDL_strtoll, long long, FMT_PRILLd, "9223372036854775807", 10, 9223372036854775807LL, 19);
         STRTOX_TEST_CASE(SDL_strtoll, long long, FMT_PRILLd, "9223372036854775808", 10, 9223372036854775807LL, 19);
         STRTOX_TEST_CASE(SDL_strtoll, long long, FMT_PRILLd, "-9223372036854775808", 10, -9223372036854775807LL - 1, 20);