android: Manually load libraries with dlopen() and RTLD_GLOBAL on Android M
authorTobias Brunner <tobias@strongswan.org>
Wed, 17 Jun 2015 13:31:24 +0000 (15:31 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 28 Jul 2015 11:27:33 +0000 (13:27 +0200)
This fixes an issue when using the Android M preview.  Bionic's dynamic
linker was changed so that symbols in libraries loaded with RTLD_LOCAL
were not found anymore in dlsym(RTLD_DEFAULT, ...).  This is the case
for libraries loaded with System.loadLibrary(), therefore, the plugin
loader in libstrongswan was not able to resolve any symbols defined in
other libraries loaded later.  While this seems to have been broken
unintentionally for existing apps (fix at [1]), it will again be a
problem whenever we decide to increase targetSdkVersion beyond 22 (or
until that fix makes it into the system/emulator images).

Unfortunately, the dynamic loader in releases prior to Android 4.3 can't
load libandroidbridge without also loading its dependencies.

[1] https://github.com/android/platform_bionic/commit/1913352c6b

src/frontends/android/jni/libandroidbridge/android_jni.c
src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java

index 7ab9a24..a6412bd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
  * Copyright (C) 2012 Giuliano Grassi
  * Copyright (C) 2012 Ralf Sager
  * Hochschule fuer Technik Rapperswil
@@ -15,6 +15,8 @@
  * for more details.
  */
 
+#include <dlfcn.h>
+
 #include "android_jni.h"
 
 #include <library.h>
  */
 static JavaVM *android_jvm;
 
+static struct {
+       char name[32];
+       void *handle;
+} libs[] = {
+       { "libstrongswan.so", NULL },
+#ifdef USE_BYOD
+       { "libtncif.so", NULL },
+       { "libtnccs.so", NULL },
+       { "libimcv.so", NULL },
+#endif
+       { "libhydra.so", NULL },
+       { "libcharon.so", NULL },
+       { "libipsec.so", NULL },
+};
+
 jclass *android_charonvpnservice_class;
 jclass *android_charonvpnservice_builder_class;
 android_sdk_version_t android_sdk_version;
@@ -79,6 +96,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
        JNIEnv *env;
        jclass jversion;
        jfieldID jsdk_int;
+       int i;
 
        android_jvm = vm;
 
@@ -87,6 +105,15 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
                return -1;
        }
 
+       for (i = 0; i < countof(libs); i++)
+       {
+               libs[i].handle = dlopen(libs[i].name, RTLD_GLOBAL);
+               if (!libs[i].handle)
+               {
+                       return -1;
+               }
+       }
+
        androidjni_threadlocal = thread_value_create(attached_thread_cleanup);
 
        android_charonvpnservice_class =
@@ -109,6 +136,16 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
  */
 void JNI_OnUnload(JavaVM *vm, void *reserved)
 {
+       int i;
+
        androidjni_threadlocal->destroy(androidjni_threadlocal);
+
+       for (i = countof(libs) - 1; i >= 0; i--)
+       {
+               if (libs[i].handle)
+               {
+                       dlclose(libs[i].handle);
+               }
+       }
 }
 
index 236e759..482caa2 100644 (file)
@@ -42,6 +42,7 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.net.VpnService;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -732,22 +733,25 @@ public class CharonVpnService extends VpnService implements Runnable
 
        /*
         * The libraries are extracted to /data/data/org.strongswan.android/...
-        * during installation.
+        * during installation.  On newer releases most are loaded in JNI_OnLoad.
         */
        static
        {
-               System.loadLibrary("strongswan");
-
-               if (MainActivity.USE_BYOD)
+               if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
                {
-                       System.loadLibrary("tncif");
-                       System.loadLibrary("tnccs");
-                       System.loadLibrary("imcv");
-               }
+                       System.loadLibrary("strongswan");
+
+                       if (MainActivity.USE_BYOD)
+                       {
+                               System.loadLibrary("tncif");
+                               System.loadLibrary("tnccs");
+                               System.loadLibrary("imcv");
+                       }
 
-               System.loadLibrary("hydra");
-               System.loadLibrary("charon");
-               System.loadLibrary("ipsec");
+                       System.loadLibrary("hydra");
+                       System.loadLibrary("charon");
+                       System.loadLibrary("ipsec");
+               }
                System.loadLibrary("androidbridge");
        }
 }