[PATCH 0/5] 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: - Disable IPX detection on Android. The NDK exposes `<linux/ipx.h>` and `SOL_IPX` via UAPI headers, which causes `HAS_IPX` to be enabled at build time. In practice, bionic does not provide documented or implemented IPX socket support, and typical Android kernels ship without IPX enabled. Guard the detection with `!__ANDROID__` to avoid enabling a non-functional code path based solely on header-level defines. - 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. - Emit ARM-specific ELF directive syntax in winebuild. GNU as requires `%function` / `%progbits` for `.type` and `.section` on ARM, whereas most other ELF architectures use `@function` / `@progbits`. Adjust winebuild output when targeting `CPU_ARM` to avoid assembler errors (including with clang's integrated assembler). 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. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Android NDK exposes <linux/ipx.h> and defines SOL_IPX via UAPI headers, which makes our build-time probe treat IPX as available and enables HAS_IPX. In practice this is a false positive on Android: bionic/libc does not provide any documented or implemented IPX socket support, downstream environments (e.g. Termux) commonly disable it, and device kernels typically ship without IPX enabled. Guard the SOL_IPX-based detection with !ANDROID in ntdll, ws2_32, and server code so we don’t compile in IPX paths on Android due to header-only defines. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/ntdll/unix/socket.c | 2 +- dlls/ws2_32/unixlib.c | 2 +- server/sock.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 62b5f3a8504..392968adca2 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -54,7 +54,7 @@ # include <linux/types.h> # endif # include <linux/ipx.h> -# ifdef SOL_IPX +# if defined(SOL_IPX) && !defined(__ANDROID__) # define HAS_IPX # endif #endif diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c index e0e1263e025..dcf0daff52e 100644 --- a/dlls/ws2_32/unixlib.c +++ b/dlls/ws2_32/unixlib.c @@ -75,7 +75,7 @@ # include <linux/types.h> # endif # include <linux/ipx.h> -# ifdef SOL_IPX +# if defined(SOL_IPX) && !defined(__ANDROID__) # define HAS_IPX # endif #endif diff --git a/server/sock.c b/server/sock.c index 7785d3c7706..bb379c20451 100644 --- a/server/sock.c +++ b/server/sock.c @@ -74,7 +74,7 @@ # include <linux/types.h> # endif # include <linux/ipx.h> -# ifdef SOL_IPX +# if defined(SOL_IPX) && !defined(__ANDROID__) # define HAS_IPX # endif #endif -- GitLab 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 e56bf276421..5fee2caee23 100755 --- a/configure +++ b/configure @@ -11938,6 +11938,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 5d23fb4268b..dabd05d6914 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
From: Twaik Yont <9674930+twaik@users.noreply.github.com> GNU as uses architecture-specific syntax for certain ELF directive arguments. On ARM, the second argument of .type and the section type in .section must be written using “%function” / “%object” and “%progbits”, while most other ELF architectures use the “@function” / “@progbits” form. winebuild currently emits “@function” and “@progbits” unconditionally, which is not accepted by the ARM assembler (including clang’s integrated assembler) and leads to assembly errors. Emit the ARM-specific “%function” and “%progbits” forms when targeting CPU_ARM, and keep the existing “@…” syntax for other architectures. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- tools/winebuild/utils.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c index 1f0cde60bf9..2d2e59ae01d 100644 --- a/tools/winebuild/utils.c +++ b/tools/winebuild/utils.c @@ -780,7 +780,10 @@ void output_function_header( const char *func, int global ) if (global) output( "\t.globl %s\n", name ); break; default: - output( "\t.type %s,@function\n", name ); + if (target.cpu == CPU_ARM) + output( "\t.type %s,%%function\n", name ); + else + output( "\t.type %s,@function\n", name ); if (global) output( "\t.globl %s\n\t.hidden %s\n", name, name ); break; } @@ -890,7 +893,10 @@ void output_gnu_stack_note(void) case PLATFORM_APPLE: break; default: - output( "\t.section .note.GNU-stack,\"\",@progbits\n" ); + if (target.cpu == CPU_ARM) + output( "\t.section .note.GNU-stack,\"\",%%progbits\n" ); + else + output( "\t.section .note.GNU-stack,\"\",@progbits\n" ); break; } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10067
GNU as requires %function / %progbits for .type and .section on ARM, whereas most other ELF architectures use @function / @progbits. Adjust winebuild output when targeting CPU_ARM to avoid assembler errors (including with clang's integrated assembler).
This should never happen, winebuild-generated ARM code is supposed to be built as PE. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129363
On Tue Feb 10 20:17:51 2026 +0000, Alexandre Julliard wrote:
GNU as requires %function / %progbits for .type and .section on ARM, whereas most other ELF architectures use @function / @progbits. Adjust winebuild output when targeting CPU_ARM to avoid assembler errors (including with clang's integrated assembler). This should never happen, winebuild-generated ARM code is supposed to be built as PE. Probably I was wrong about targetting elf but error is still there.
...
/home/twaik/.termux-build/wine-devel/src/tools/install-sh -m 644 tools/wine/wine.pl.UTF-8.man /data/data/com.termux/files/usr/opt/wine-devel/share/man/pl.UTF-8/man1/wine.1
STRIPPROG="llvm-strip" /home/twaik/.termux-build/wine-devel/src/tools/install-sh tools/wine/wine /data/data/com.termux/files/usr/opt/wine-devel/bin/wine
/home/twaik/.termux-build/wine-devel/host-build/tools/winebuild/winebuild -w --implib -o dlls/aclui/libaclui.a -b \
arm-linux-androideabi --export /home/twaik/.termux-build/wine-devel/src/dlls/aclui/aclui.spec
tmp699ae6ef/libaclui-00000000.s:2:27: error: expected STT_<TYPE_IN_UPPER_CASE>, '#<type>', '%<type>' or "<type>"
.type CreateSecurityPage,@function
^
tmp699ae6ef/libaclui-00000000.s:9:30: error: expected '%<type>' or "<type>"
.section .note.GNU-stack,"",@progbits
^
winebuild: /home/twaik/.termux-build/_cache/android-r29-api-24-v3/bin/clang failed with status 1
make: *** [Makefile:1722: dlls/aclui/libaclui.a] Error 1
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129368
On Tue Feb 10 20:19:48 2026 +0000, Twaik Yont wrote:
Probably I was wrong about targetting elf but the error is still there. ``` ... /home/twaik/.termux-build/wine-devel/src/tools/install-sh -m 644 tools/wine/wine.pl.UTF-8.man /data/data/com.termux/files/usr/opt/wine-devel/share/man/pl.UTF-8/man1/wine.1 STRIPPROG="llvm-strip" /home/twaik/.termux-build/wine-devel/src/tools/install-sh tools/wine/wine /data/data/com.termux/files/usr/opt/wine-devel/bin/wine /home/twaik/.termux-build/wine-devel/host-build/tools/winebuild/winebuild -w --implib -o dlls/aclui/libaclui.a -b \ arm-linux-androideabi --export /home/twaik/.termux-build/wine-devel/src/dlls/aclui/aclui.spec tmp699ae6ef/libaclui-00000000.s:2:27: error: expected STT_<TYPE_IN_UPPER_CASE>, '#<type>', '%<type>' or "<type>" .type CreateSecurityPage,@function ^ tmp699ae6ef/libaclui-00000000.s:9:30: error: expected '%<type>' or "<type>" .section .note.GNU-stack,"",@progbits ^ winebuild: /home/twaik/.termux-build/_cache/android-r29-api-24-v3/bin/clang failed with status 1 make: *** [Makefile:1722: dlls/aclui/libaclui.a] Error 1 ``` For the reference, `/home/twaik/.termux-build/_cache/android-r29-api-24-v3/bin/clang` is Android NDK's clang, copied by termux scripts, so I am assuming winebuild invoking build target cross-compiler and not mingw.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129369
On Tue Feb 10 20:21:41 2026 +0000, Twaik Yont wrote:
For the reference, `/home/twaik/.termux-build/_cache/android-r29-api-24-v3/bin/clang` is Android NDK's clang, copied by termux scripts, so I am assuming winebuild invoking build target cross-compiler and not mingw. By saying `still there` I mean before the patch.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129370
On Tue Feb 10 20:23:41 2026 +0000, Twaik Yont wrote:
By saying `still there` I mean before the patch. This shouldn't happen in current Wine, you are using an old version.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129371
On Tue Feb 10 20:30:19 2026 +0000, Alexandre Julliard wrote:
This shouldn't happen in current Wine, you are using an old version. The log is from 10.7, I can try with modern version, ignoring the patch.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129374
On Tue Feb 10 20:33:24 2026 +0000, Twaik Yont wrote:
The log is from 10.7, I can try with modern version, ignoring the patch. Ok, seems like it is no longer reproducible on 11.2. Should I remove it or you can simple cherry pick other commits?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129376
On Tue Feb 10 20:45:20 2026 +0000, Twaik Yont wrote:
Ok, seems like it is no longer reproducible on 11.2. Should I remove it or you can simply cherry pick other commits? You should remove it, and make sure to test all your patches against the commit that your MR is based on.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129383
Is the IPX change necessary to fix a build failure? If so, would it be more appropriate to check for some additional symbol instead of __ANDROID__? If not, I don't think that disabling IPX support on the Unix side is going to actually change anything? We still expose it on the PE side, but actually creating a socket will fail. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10067#note_129406
participants (4)
-
Alexandre Julliard (@julliard) -
Elizabeth Figura (@zfigura) -
Twaik Yont -
Twaik Yont (@twaik)