[PATCH v2 0/4] MR10683: wineandroid: startup and JNI fixes (part of !10569)
This MR contains a subset of changes extracted from MR !10569 (first 4 commits after merge-base). Included changes: 1. Fix 64-bit crash in JNI setCursor call * Pass NULL instead of integer 0 for jobject argument in CallVoidMethod * Prevents invalid jobject usage and JNI abort on 64-bit platforms 2. Lower targetSdkVersion to 28 * Avoid Android 10 W^X restrictions (API 29+) affecting exec and memory mappings * Add Java 1.8 compile options required by the adjusted SDK level 3. Fix Wine loader path and library lookup * Update WINELOADER to match current Wine layout * Extend LD_LIBRARY_PATH to support loading libraries directly from APK * Restore correct startup on modern Android/Wine setups 4. Move environment setup to Java layer and simplify wine_init * Replace JNI environment passing with setenv() in WineActivity * Simplify native initialization and remove environment array handling * Preserve LD_LIBRARY_PATH update and WINEDEBUGLOG behavior Reason: This MR is split out from !10569 to simplify review by isolating Android startup and stability changes into a smaller, focused set. -- v2: wineandroid: move environment setup to Java and drop obsolete env vars https://gitlab.winehq.org/wine/wine/-/merge_requests/10683
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Pass NULL as a jobject for the pixel array argument when size is zero. Passing integer 0 to CallVoidMethod() in a varargs call is undefined and on 64-bit platforms may be interpreted as an invalid jobject, triggering a JNI abort. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 6131ee627b5..77050337273 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -1009,7 +1009,7 @@ static NTSTATUS setCursor_ioctl( void *data, DWORD in_size, DWORD out_size, ULON res->hotspotx, res->hotspoty, array ); (*jni_env)->DeleteLocalRef( jni_env, array ); } - else (*jni_env)->CallVoidMethod( jni_env, object, method, res->id, 0, 0, 0, 0, 0 ); + else (*jni_env)->CallVoidMethod( jni_env, object, method, res->id, 0, 0, 0, 0, NULL ); unwrap_java_call(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10683
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Android 10 enforces W^X restrictions for apps targeting API level 29+, including removal of execute permission for the app home directory and restrictions on execve() and executable mappings. See: https://developer.android.com/about/versions/10/behavior-changes-10#execute-... Lower targetSdkVersion to 28 so the current Wine Android startup model continues to work on modern devices while the codebase is being adapted to these restrictions. Set Java compile options to 1.8 as required by the Android build after lowering compileSdkVersion. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/build.gradle.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dlls/wineandroid.drv/build.gradle.in b/dlls/wineandroid.drv/build.gradle.in index 56ffd06ca82..d83082bb8c5 100644 --- a/dlls/wineandroid.drv/build.gradle.in +++ b/dlls/wineandroid.drv/build.gradle.in @@ -113,6 +113,7 @@ android { applicationId "org.winehq.wine" minSdkVersion 26 + targetSdkVersion 28 versionCode 1 versionName "@PACKAGE_VERSION@" } @@ -126,6 +127,12 @@ android main.res.srcDirs = [ "res" ] main.manifest.srcFile get_srcdir() + "/AndroidManifest.xml" } + + compileOptions + { + sourceCompatibility JavaVersion.VERSION_1_8 + compileOptions.targetCompatibility JavaVersion.VERSION_1_8 + } } configurations.configureEach -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10683
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Update WINELOADER to match the current Wine layout. Also extend LD_LIBRARY_PATH to include libraries from the APK, since modern Android versions may load native libraries directly from the APK instead of extracting them to the filesystem. Support for loading shared libraries directly from an APK via dlopen("apk!/lib/...") is available since API level 23. See: https://android.googlesource.com/platform/bionic/+/master/android-changes-fo... This restores correct startup on recent Wine versions and Android devices. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/WineActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/wineandroid.drv/WineActivity.java b/dlls/wineandroid.drv/WineActivity.java index fd73ff009bf..0dc7df5ca53 100644 --- a/dlls/wineandroid.drv/WineActivity.java +++ b/dlls/wineandroid.drv/WineActivity.java @@ -116,7 +116,7 @@ private void loadWine( String cmdline ) File libdir = new File( getFilesDir(), wine_abi + "/lib" ); File dlldir = new File( libdir, "wine" ); File prefix = new File( getFilesDir(), "prefix" ); - File loader = new File( bindir, "wine" ); + File loader = new File( dlldir, get_so_dir(wine_abi) + "/wine" ); String locale = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry() + ".UTF-8"; @@ -124,7 +124,7 @@ private void loadWine( String cmdline ) env.put( "WINELOADER", loader.toString() ); env.put( "WINEPREFIX", prefix.toString() ); env.put( "WINEDLLPATH", dlldir.toString() ); - env.put( "LD_LIBRARY_PATH", libdir.toString() + ":" + getApplicationInfo().nativeLibraryDir ); + env.put( "LD_LIBRARY_PATH", libdir.toString() + ":" + getPackageResourcePath() + "!/lib/" + wine_abi + ":" + getApplicationInfo().nativeLibraryDir ); env.put( "LC_ALL", locale ); env.put( "LANG", locale ); env.put( "PATH", bindir.toString() + ":" + System.getenv( "PATH" )); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10683
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Move environment setup to WineActivity using setenv() and remove the environment array parameter from wine_init(). The startup variables are now set directly in the JVM process instead of being marshaled through JNI and reconstructed on the native side. wine_init() now reads the process environment via getenv(), keeping the LD_LIBRARY_PATH update and WINEDEBUGLOG handling while removing the JNI-side environment reconstruction. Drop setting WINELOADER, WINEDLLPATH and PATH, which are no longer required with the current Wine startup design. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/ntdll/unix/loader.c | 49 ++++++++----------------- dlls/wineandroid.drv/WineActivity.java | 51 ++++++++++++++------------ 2 files changed, 42 insertions(+), 58 deletions(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 9d70aa421f0..8d062ffdda0 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -1886,12 +1886,13 @@ DECLSPEC_EXPORT jobject java_object = 0; DECLSPEC_EXPORT unsigned short java_gdt_sel = 0; /* main Wine initialisation */ -static jstring wine_init_jni( JNIEnv *env, jobject obj, jobjectArray cmdline, jobjectArray environment ) +static jstring wine_init_jni( JNIEnv *env, jobject obj, jobjectArray cmdline ) { char **argv; char *str; - char error[1024]; + char error[1024], *winedebuglog = NULL; int i, argc, length; + void (*update_func)( const char * ); /* get the command line array */ @@ -1918,39 +1919,19 @@ static jstring wine_init_jni( JNIEnv *env, jobject obj, jobjectArray cmdline, jo /* set the environment variables */ - if (environment) + // Activity always modifies LD_LIBRARY_PATH in order to load libraries + // from custom location in JVM process. + update_func = dlsym( RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH" ); + if (update_func) update_func( getenv("LD_LIBRARY_PATH") ); + + winedebuglog = getenv("WINEDEBUGLOG"); + if (winedebuglog) { - int count = (*env)->GetArrayLength( env, environment ); - for (i = 0; i < count - 1; i += 2) + int fd = open( winedebuglog, O_WRONLY | O_CREAT | O_APPEND, 0666 ); + if (fd != -1) { - jobject var_obj = (*env)->GetObjectArrayElement( env, environment, i ); - jobject val_obj = (*env)->GetObjectArrayElement( env, environment, i + 1 ); - const char *var = (*env)->GetStringUTFChars( env, var_obj, NULL ); - - if (val_obj) - { - const char *val = (*env)->GetStringUTFChars( env, val_obj, NULL ); - setenv( var, val, 1 ); - if (!strcmp( var, "LD_LIBRARY_PATH" )) - { - void (*update_func)( const char * ) = dlsym( RTLD_DEFAULT, - "android_update_LD_LIBRARY_PATH" ); - if (update_func) update_func( val ); - } - else if (!strcmp( var, "WINEDEBUGLOG" )) - { - int fd = open( val, O_WRONLY | O_CREAT | O_APPEND, 0666 ); - if (fd != -1) - { - dup2( fd, 2 ); - close( fd ); - } - } - (*env)->ReleaseStringUTFChars( env, val_obj, val ); - } - else unsetenv( var ); - - (*env)->ReleaseStringUTFChars( env, var_obj, var ); + dup2( fd, 2 ); + close( fd ); } } @@ -1981,7 +1962,7 @@ jint JNI_OnLoad( JavaVM *vm, void *reserved ) { static const JNINativeMethod method = { - "wine_init", "([Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/String;", wine_init_jni + "wine_init", "([Ljava/lang/String;)Ljava/lang/String;", wine_init_jni }; JNIEnv *env; diff --git a/dlls/wineandroid.drv/WineActivity.java b/dlls/wineandroid.drv/WineActivity.java index 0dc7df5ca53..3bb27c13f93 100644 --- a/dlls/wineandroid.drv/WineActivity.java +++ b/dlls/wineandroid.drv/WineActivity.java @@ -31,6 +31,7 @@ import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; +import android.system.ErrnoException; import android.util.Log; import android.view.InputDevice; import android.view.KeyEvent; @@ -52,9 +53,12 @@ import java.util.Locale; import java.util.Map; +import static android.system.Os.setenv; +import static android.system.Os.getenv; + public class WineActivity extends Activity { - private native String wine_init( String[] cmdline, String[] env ); + private native String wine_init( String[] cmdline ); public native void wine_desktop_changed( int width, int height ); public native void wine_config_changed( int dpi ); public native void wine_surface_changed( int hwnd, Surface surface, boolean opengl ); @@ -107,12 +111,23 @@ private String get_so_dir( String abi ) return ""; } + private void putenv( String name, String val ) + { + try + { + setenv( name, val, true ); + } + catch ( Exception e ) + { + e.printStackTrace(); + } + } + private void loadWine( String cmdline ) { copyAssetFiles(); String wine_abi = get_wine_abi(); - File bindir = new File( getFilesDir(), wine_abi + "/bin" ); File libdir = new File( getFilesDir(), wine_abi + "/lib" ); File dlldir = new File( libdir, "wine" ); File prefix = new File( getFilesDir(), "prefix" ); @@ -120,14 +135,10 @@ private void loadWine( String cmdline ) String locale = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry() + ".UTF-8"; - HashMap<String,String> env = new HashMap<String,String>(); - env.put( "WINELOADER", loader.toString() ); - env.put( "WINEPREFIX", prefix.toString() ); - env.put( "WINEDLLPATH", dlldir.toString() ); - env.put( "LD_LIBRARY_PATH", libdir.toString() + ":" + getPackageResourcePath() + "!/lib/" + wine_abi + ":" + getApplicationInfo().nativeLibraryDir ); - env.put( "LC_ALL", locale ); - env.put( "LANG", locale ); - env.put( "PATH", bindir.toString() + ":" + System.getenv( "PATH" )); + putenv( "WINEPREFIX", prefix.toString() ); + putenv( "LD_LIBRARY_PATH", libdir.toString() + ":" + getPackageResourcePath() + "!/lib/" + wine_abi + ":" + getApplicationInfo().nativeLibraryDir ); + putenv( "LC_ALL", locale ); + putenv( "LANG", locale ); if (cmdline == null) { @@ -140,8 +151,8 @@ private void loadWine( String cmdline ) if (winedebug != null) { File log = new File( getFilesDir(), "log" ); - env.put( "WINEDEBUG", winedebug ); - env.put( "WINEDEBUGLOG", log.toString() ); + putenv( "WINEDEBUG", winedebug ); + putenv( "WINEDEBUGLOG", log.toString() ); Log.i( LOGTAG, "logging to " + log.toString() ); log.delete(); } @@ -151,25 +162,17 @@ private void loadWine( String cmdline ) System.load( dlldir.toString() + get_so_dir(wine_abi) + "/ntdll.so" ); prefix.mkdirs(); - runWine( cmdline, env ); + runWine( loader.toString(), cmdline ); } - private final void runWine( String cmdline, HashMap<String,String> environ ) + private final void runWine( String loader, String cmdline ) { - String[] env = new String[environ.size() * 2]; - int j = 0; - for (Map.Entry<String,String> entry : environ.entrySet()) - { - env[j++] = entry.getKey(); - env[j++] = entry.getValue(); - } - - String[] cmd = { environ.get( "WINELOADER" ), + String[] cmd = { loader, "c:\\windows\\system32\\explorer.exe", "/desktop=shell,,android", cmdline }; - String err = wine_init( cmd, env ); + String err = wine_init( cmd ); Log.e( LOGTAG, err ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10683
On Thu Apr 16 18:55:41 2026 +0000, Alexandre Julliard wrote:
It should be possible to simplify this nowadays. I expect that at least WINELOADER, WINEDLLPATH and PATH are no longer necessary. Done.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10683#note_136573
participants (2)
-
Twaik Yont -
Twaik Yont (@twaik)