LD_LIBRARY_PATH 有时在 Android 上被忽略

Sim*_*lli 5 java android native shared-libraries android-ndk

我有一个 android 应用程序,它生成许多与我随包分发的库动态链接的本机可执行文件。要启动这些二进制文件,我使用 LD_LIBRARY_PATH 环境变量让他们知道从中加载库的位置,但在某些设备上这根本不起作用,LD_LIBRARY_PATH 已正确更新,但二进制文件无论如何都无法找到库. 这不是我可以复制的,因为在我的两台设备(带有库存 rom 的 Galaxy Nexus 和 Nexus 7)上它运行良好。

我尝试了很多方法,例如我产卵:

LD_LIBRARY_PATH=/my/package/custom/libs:$LD_LIBRARY_PATH && cd /binary/directory && ./binary
Run Code Online (Sandbox Code Playgroud)

和 :

    String[] envp = { "LD_LIBRARY_PATH=" + libPath + ":$LD_LIBRARY_PATH" };

    Process process = Runtime.getRuntime().exec( "su", envp );

    writer = new DataOutputStream( process.getOutputStream() );
    reader = new BufferedReader( new InputStreamReader( process.getInputStream() ) );

    writer.writeBytes( "export LD_LIBRARY_PATH=" + libPath + ":$LD_LIBRARY_PATH\n" );
    writer.flush();
Run Code Online (Sandbox Code Playgroud)

但是在这些设备上似乎没有任何工作......所以我开始认为这是一个与内核相关的问题,一些内核(比如我的)使用 LD_LIBRARY_PATH,其他内核不使用(简单地忽略它,或者他们是仅使用在应用程序启动时设置的 LD_LIBRARY_PATH,因此无法在运行时更改它)。

我也尝试使用 System.load 但它没有用,可能是因为那些库不是 JNI ......在开始考虑使用静态链接的二进制文件之前,我可以尝试一些东西吗?

Ale*_*ohn 5

这是我写的一个简单的包装器:

#include <android/log.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>

typedef int (*main_t)(int argc, char** argv);

static int help(const char* argv0)
{
    printf("%s: simple wrapper to work around LD_LIBRARY_PATH\n\n", argv0);
    printf("Args: executable, list all the libraries you need to load in dependency order, executable again, optional parameters\n");
    printf("example: %s /data/local/ttte /data/data/app/com.testwrapper/lib/ttt.so /data/local/ttte 12345\n", argv0);
    printf("Note: the executable should be built with CFLAGS=\"-fPIC -pie\", LDFLAGS=\"-rdynamic\"\n");

    return -1;
}

int main(int argc, char** argv)
{
    int rc, nlibs;
    void *dl_handle;

    if (argc < 2)
    {
        return help(argv[0]);
    }

    __android_log_print(ANDROID_LOG_DEBUG, "wrapper", "running '%s'", argv[1]);

    for (nlibs = 2; ; nlibs++)
    {
        if (nlibs >= argc)
        {
            return help(argv[0]);
        }

        __android_log_print(ANDROID_LOG_DEBUG, "wrapper", "loading '%s'", argv[nlibs]);
        dl_handle = dlopen(argv[nlibs], 0); // do not keep the handle, except for the last
        __android_log_print(ANDROID_LOG_DEBUG, "wrapper", "loaded '%s' -> %p", argv[nlibs], dl_handle);
        if (strcmp(argv[1], argv[nlibs]) == 0)
        {
            break;
        }
    }

    main_t pmain = (main_t)dlsym(dl_handle, "main");
    __android_log_print(ANDROID_LOG_DEBUG, "wrapper", "found '%s' -> %p", "main", pmain);
    rc = pmain(argc - nlibs, argv + nlibs);

//   we are exiting the process anyway, don't need to clean the handles actually

//   __android_log_print(3, "wrapper", "closing '%s'", argv[1]);
//   dlclose(dl_handle);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

为了保持可读性,我删除了大部分错误处理、不必要的清理和特殊情况的处理。

Android.mk 对于这个可执行文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := wrapper
LOCAL_SRC_FILES := wrapper/main.c
LOCAL_LDLIBS    := -llog

include $(BUILD_EXECUTABLE)
Run Code Online (Sandbox Code Playgroud)

请注意,您必须注意部署:将其打包wrapper到 APK 中,提取到某个本地路径(不要到 USB 存储或到/sdcard!),将其标记为可执行文件 ( chmod 777)。

这些是您在构建通过wrapper. 如果ndk-build用于构建它们,则如下所示:

LOCAL_C_FLAGS   += -fPIC -pie
LOCAL_LDFLAGS   += -rdynamic 
Run Code Online (Sandbox Code Playgroud)

请注意,您不再需要chmod这些可执行文件。另一个技巧:您可以将辅助可执行文件构建到共享库中,并且相同的包装器将继续工作!这样就省去了部署这些二进制文件的麻烦。NDK 和 Android 版本将通过 APK 的 libs/armeabi 自动将它们安全地传送到您的应用程序的 lib 目录。

更新

似乎有一个更简单的解决方案,使用带有修改环境的ProcessBuilderhttps : //stackoverflow.com/a/8962189/192373