LD_LIBRARY_PATH ignored on Android sometimes

后端 未结 4 2042
有刺的猬
有刺的猬 2021-02-06 15:59

i have an android app which spawns many native executables dinamically linked with libraries i distribute with the package. To launch those binaries, i use the LD_LIBRARY_PATH e

相关标签:
4条回答
  • 2021-02-06 16:09

    I don't think the LD_LIBRARY_PATH will be ignored. The way you update the system environment is ok. The LD_LIBRARY_PATH will get updated under the root user.

    The solution will not be able to work on some machine may have several causes:

    1. The Android app is always run under its own user id. It is not run under root privilege. If you only modify the system variable of the root user. The application may still cannot find the correct library path in its own envrionment.

    2. Even if you correctly updated the LD_LIBRARY_PATH when the application initialize itself, you still need to make sure the application's corresponding user id do have the access to read those libraries.

    3. Some vendor will modify the Android kernel for some security reasons. They may disable the loading of 3rd-party libraries from external storage or something like that.

    0 讨论(0)
  • 2021-02-06 16:15

    Here is a simple wrapper I wrote about:

    #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;
    }
    

    To keep it readable, I drop most of error handling, unessential cleanup, and handling of special cases.

    Android.mk for this executable:

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE    := wrapper
    LOCAL_SRC_FILES := wrapper/main.c
    LOCAL_LDLIBS    := -llog
    
    include $(BUILD_EXECUTABLE)
    

    Note that you must take care of deployment: packaging this wrapper into the APK, extraction to some local path (never to USB storage or to /sdcard!), marking it as executable (chmod 777).

    These are the additional parameters you must supply when you build the executables you run through the wrapper. If you use ndk-build to build them, it looks as follows:

    LOCAL_C_FLAGS   += -fPIC -pie
    LOCAL_LDFLAGS   += -rdynamic 
    

    Note that you don't need to chmod for these executables anymore. Another trick: you can build the secondary executables into shared libraries, and the same wrapper will continue to work! This saves the trouble of deployment of these binaries. NDK and Android build will deliver them safely through libs/armeabi of the APK to your app's lib directory automagically.

    Update

    There seems to be a much easier solution, using the ProcessBuilder with modified environment: https://stackoverflow.com/a/8962189/192373.

    0 讨论(0)
  • 2021-02-06 16:26

    This is not a kernel related issue, but an LD related issue: The loading of libraries is entirely in user mode, not kernel mode, so the kernel would have nothing to do with it. The kernel is only responsible for transferring control to ld, which loops over the DYNAMIC section of the ELF binary, loading any libraries, and figuring out what to do with environment variables like LD_PRELOAD and LD_PRELOAD.

    The variables, however, can pose an inherent security risk: LD_LIBRARY_PATH enables you to put your own paths before the system defaults (/lib or - in Android, /system/lib). LD_PRELOAD is even worse, because it force loads (shoves) your library into the process, whether or not the process requested it - which can lead to injection of malicious code into a sensitive process. For this reason, this is not allowed on root Setuid processes. If you are in a root shell, however (i.e. on a rooted phone), most devices will allow it. The vendor might modify the LD, leading to your problems.

    What you can do to work around:

    • Easiest: remount /system as read/write (mount -o remount /system), and copy your libraries to /system/lib. Will require rooted phone

    • Smartest: Statically link your libraries into your binary, while maintaing the other (system) libraries dynamic

    Hope this helps,

    TG

    0 讨论(0)
  • 2021-02-06 16:34

    That's correct, you cannot rely on LD_LIBRARY_PATH on Android. You can use System.load() with any library, but this does not really help when you spawn child processes. You can use dlopen in your code, but usually it means that many lines of code must be rewritten.

    A funny trick is to convert your executables into shared libraries, and load them from wrapper executables which would dlopen the libraries in dependency order.

    It is highly recommended though to convert as many spawns as possible into in-process calls.

    0 讨论(0)
提交回复
热议问题