[PATCH v4 0/3] MR10067: wineandroid: fix Android NDK/ARM build issues (resubmission with expanded rationale)
This series addresses several Android-specific build failures and toolchain interactions observed when building Wine with recent Android NDK versions. Some of these changes were previously proposed but not accepted due to insufficient explanation of the underlying behavior. This submission reintroduces those fixes with more detailed technical justification and adds a few additional related corrections discovered during further testing. The changes include: - Replace `ffs()` with `__builtin_ffs()` in ntdll. Android builds fail due to ffs() being used without `<strings.h>`, which Wine does not include. Using `__builtin_ffs()` avoids implicit declaration errors under C99 and matches existing usage elsewhere in the tree while preserving semantics. - Avoid pthread mutex attributes in winepulse on Android. `pthread_mutexattr_setprotocol()` may be exposed at configure time depending on the selected API level, but actual availability depends on the device's bionic libc. Robust mutex support is not provided by bionic. Configure-time detection can therefore select a code path that is not reliably supported at runtime. Use plain `pthread_mutex_init()` on Android and retain the existing attribute-based initialization on other platforms. - Force `--rosegment` for `wine-preloader` on Android (API < 29, NDK r22+). Since NDK r22, clang implicitly passes `-Wl,--no-rosegment` when targeting API levels below 29 ([NDK changelog](https://github.com/android/ndk/wiki/Changelog-r22) Issue 1196). Because wine-preloader links with `-Wl,-Ttext=0x7d400000`, this results in a `PT_LOAD` segment with a very large file offset to preserve the VMA-to-file-offset relationship, creating a sparse gap (`~0x7d400000`) and inflating the apparent ELF size to `~2GB`. `wine-preloader` is a static binary and does not depend on unwind/backtrace correctness, so inheriting `--no-rosegment` provides no benefit. Force `-Wl,--rosegment` when supported to keep the segment layout reasonable, without affecting other binaries. Overall, this series improves Android NDK compatibility, prevents false feature detection based on header exposure or API-level-dependent behavior, and fixes ARM assembler incompatibilities, while keeping the changes restricted to Android-specific paths. -- v4: wineandroid: force --rosegment for wine-preloader on Android (API < 29, NDK r22+) winepulse: avoid pthread mutex attributes on Android due to unreliable availability ntdll/unix: use __builtin_ffs instead of ffs in ldt_alloc_entry https://gitlab.winehq.org/wine/wine/-/merge_requests/10067
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Android NDK fails to build ntdll because ffs() is used without including <strings.h>, leading to an implicit function declaration error under C99: ``` .../dlls/ntdll/unix/unix_private.h:660:26: error: call to undeclared function 'ffs'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 660 | idx = idx * 32 + ffs( ~ldt_bitmap[idx] ) - 1; | ^ 1 error generated. make: *** [Makefile:250029: dlls/ntdll/unix/loadorder.o] Error 1 ``` Wine does not use <strings.h>, and other parts of the tree already rely on __builtin_ffs() for the same purpose. Replace ffs() with __builtin_ffs() in ldt_alloc_entry() to avoid the undeclared symbol on Android and to stay consistent with existing usage. The builtin maps directly to the compiler intrinsic and preserves the original semantics. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/ntdll/unix/unix_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 10f6bb2c63c..51e2e4f98a8 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -658,7 +658,7 @@ static inline WORD ldt_alloc_entry( LDT_ENTRY entry ) for (idx = 0; idx < ARRAY_SIZE(ldt_bitmap); idx++) { if (ldt_bitmap[idx] == ~0u) continue; - idx = idx * 32 + ffs( ~ldt_bitmap[idx] ) - 1; + idx = idx * 32 + __builtin_ffs( ~ldt_bitmap[idx] ) - 1; return ldt_update_entry( (idx << 3) | 7, entry ); } return 0; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10067
From: Twaik Yont <9674930+twaik@users.noreply.github.com> On Android, pthread mutex attribute support cannot be reliably determined at configure time. The NDK may expose pthread_mutexattr_setprotocol() and PTHREAD_PRIO_INHERIT based on the selected API level, causing configure checks to succeed. However, the actual availability depends on the device’s bionic libc, and the functionality may not be implemented at runtime on the target system. In addition, robust mutex support (pthread_mutexattr_setrobust()/PTHREAD_MUTEX_ROBUST) is not provided by bionic. Relying solely on configure-time detection can therefore select a code path that builds successfully but is not guaranteed to function correctly on the running Android system. Initialize pulse_mutex without special attributes on Android and keep the existing mutex attribute setup on other platforms. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/winepulse.drv/pulse.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 8033f0f851b..c0f29b58294 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -240,6 +240,7 @@ static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, static NTSTATUS pulse_process_attach(void *args) { +#ifndef __ANDROID__ pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); @@ -248,6 +249,9 @@ static NTSTATUS pulse_process_attach(void *args) if (pthread_mutex_init(&pulse_mutex, &attr) != 0) pthread_mutex_init(&pulse_mutex, NULL); +#else + pthread_mutex_init(&pulse_mutex, NULL); +#endif #ifdef _WIN64 if (NtCurrentTeb()->WowTebOffset) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10067
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Commit message: Android NDK r22 changed LLD behavior for targets below API 29 (see NDK changelog, Issue 1196). For such targets clang passes -Wl,--no-rosegment implicitly to the linker to improve backtrace correctness on older devices. As a result, wine-preloader is linked with --no-rosegment semantics. Since wine-preloader uses -Wl,-Ttext=0x7d400000, the linker assigns a very large file offset to the PT_LOAD segment containing .text in order to preserve the required relationship between virtual addresses and file offsets within the segment. This introduces a large sparse gap (around 0x7d400000) and inflates the apparent ELF size to ~2GB. wine-preloader is a static binary and does not require unwind/backtrace correctness, so there is no benefit in inheriting --no-rosegment. Force -Wl,--rosegment for wine-preloader when supported to keep the segment layout sane and the resulting ELF size reasonable, without affecting other binaries. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- configure | 30 ++++++++++++++++++++++++++++++ configure.ac | 1 + 2 files changed, 31 insertions(+) diff --git a/configure b/configure index e2d2079746e..07ecd3054ac 100755 --- a/configure +++ b/configure @@ -11939,6 +11939,36 @@ then : WINELOADER_LDFLAGS="-Wl,--export-dynamic" fi WINEPRELOADER_LDFLAGS="-static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7d400000" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,--rosegment" >&5 +printf %s "checking whether the compiler supports -Wl,--rosegment... " >&6; } +if test ${ac_cv_cflags__Wl___rosegment+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_wine_try_cflags_saved=$CFLAGS +CFLAGS="$CFLAGS -Wl,--rosegment" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(int argc, char **argv) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_cflags__Wl___rosegment=yes +else case e in #( + e) ac_cv_cflags__Wl___rosegment=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +CFLAGS=$ac_wine_try_cflags_saved ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl___rosegment" >&5 +printf "%s\n" "$ac_cv_cflags__Wl___rosegment" >&6; } +if test "x$ac_cv_cflags__Wl___rosegment" = xyes +then : + WINEPRELOADER_LDFLAGS="$WINEPRELOADER_LDFLAGS -Wl,--rosegment" +fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -lGLESv2" >&5 printf %s "checking for -lGLESv2... " >&6; } diff --git a/configure.ac b/configure.ac index 3137bc9a806..e3457400fdc 100644 --- a/configure.ac +++ b/configure.ac @@ -1001,6 +1001,7 @@ case $host_os in WINE_TRY_CFLAGS([-Wl,-z,defs],[UNIXLDFLAGS="$UNIXLDFLAGS -Wl,-z,defs"]) WINE_TRY_CFLAGS([-fPIC -Wl,--export-dynamic],[WINELOADER_LDFLAGS="-Wl,--export-dynamic"]) WINEPRELOADER_LDFLAGS="-static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7d400000" + WINE_TRY_CFLAGS([-Wl,--rosegment], [WINEPRELOADER_LDFLAGS="$WINEPRELOADER_LDFLAGS -Wl,--rosegment"]) WINE_CHECK_SONAME(GLESv2,glFlush) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10067
On Sun Feb 15 17:47:53 2026 +0000, Twaik Yont wrote:
Is there any reason for CI to fail on random tests? I mean almost every time I push commits it fails and the next push I add or remove commits it either fails on other tests or succeeds. Well, it was randomly fixed again.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129717
Except the "ffs -> __builtin_ffs" change it should not affect non-android platforms so I think it should be safe to merge. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_130166
@julliard Hi, just wanted to check on the status of this PR. Please let me know if any changes or additional information are needed from my side. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_131127
participants (2)
-
Twaik Yont -
Twaik Yont (@twaik)