Android找不到so,实际上apk中有的

时间:2025-01-26 07:39:00

解决apk中有.so,实际运行时找不到的问题

排查方向:

  • ①、.so安装位置是否实际存在文件(().nativeLibraryDir
  • ②、当前ARM架构适配配置或者匹配(armeabi-v7a, arm64-v8a, x86_64, ...
  • ③、加载方式是否正确[("so_name_but_no_'lib'_prefix")/("so_absolute_path")]
  • ④、Android版本(Android 31+),AGP版本(AGP 4.2.0+)参考下方配置 ↓↓↓
  • ⑤、android:extractNativeLibs=true是否配置(或者DSL选项useLegacyPackaging=true
  • ⑥、库文件没有访问权限
  1. Android .so存储位置
    可以通过如下方式获取so文件的加载位置
String so_path = context.getApplicationInfo().nativeLibraryDir; //查看加载 .so 的位置

举个栗子
系统应用:/system/lib/xxx[/vendor/lib (三方OEM厂商)] 或者 system/app/xxx/lib

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三方应用: data/app/[package-name]/lib
注意:package-name可能会是一串随机数字,可以通过如下adb命令行根据包名查看app的安装位置:

adb shell
pm list package -f |grep 

在这里插入图片描述
您可以通过如下代码查找.so文件所在的目录,参考自:

/wangbaochu/article/details/47805921

    /**
     * The function use to find so path for compatible android system
     * Android OS >= 2.3
     */
    public static String findLibrary1(Context context, String libName) {
        String result = null;
        ClassLoader classLoader = (context.getClassLoader());
        if (classLoader != null) {
            try {
                Method findLibraryMethod = classLoader.getClass().getMethod("findLibrary", new Class<?>[] { String.class });   
                if (findLibraryMethod != null) {
                    Object objPath = findLibraryMethod.invoke(classLoader, new Object[] { libName });
                    if (objPath != null && objPath instanceof String) {
                        result = (String) objPath;
                    }
                }
            } catch (NoSuchMethodException e) {
                Log.e("findLibrary1", e.toString());
            } catch (IllegalAccessException e) {
                Log.e("findLibrary1", e.toString());
            } catch (IllegalArgumentException e) {
                Log.e("findLibrary1", e.toString());
            } catch (InvocationTargetException e) {
                Log.e("findLibrary1", e.toString());
            } catch (Exception e) {
                Log.e("findLibrary1", e.toString());
            }
        }
 
        return result;
    }
 
    /**
     * The function use to find so path for compatible android system
     * Android OS <= 2.2
     */
    public static String findLibrary2(Context context, String libName) {
        String result = null;
        ClassLoader classLoader = (context.getClassLoader());
        if (classLoader != null) {
            try {
                Method findLibraryMethod = classLoader.getClass().getDeclaredMethod("findLibrary", new Class<?>[] { String.class });
                if (findLibraryMethod != null) {
                    if (!findLibraryMethod.isAccessible()) {
                        findLibraryMethod.setAccessible(true);
                    }
                    Object objPath = findLibraryMethod.invoke(classLoader, new Object[] { libName });
                    if (objPath != null && objPath instanceof String) {
                        result = (String) objPath;
                    }
                }
            } catch (NoSuchMethodException e) {
                Log.e("findLibrary2", e.toString());
            } catch (IllegalAccessException e) {
                Log.e("findLibrary2", e.toString());
            } catch (IllegalArgumentException e) {
                Log.e("findLibrary2", e.toString());
            } catch (InvocationTargetException e) {
                Log.e("findLibrary2", e.toString());
            } catch (Exception e) {
                Log.e("findLibrary2", e.toString());
            }
        }
 
        return result;
    }
  1. 查看Android打包配置是否指定了对应ARM架构
android {
    // ... 
    defaultConfig {
        // ...
        ndk {
            // 目前Android支持的ABIs
            abiFilters 'mips', 'mips64', 'x86', 'x86–64', 'arm64-v8a', 'armeabi', 'armeabi-v7a'
        }
    }
}

您可以通过如下代码获取当前设备支持的ABIs:

	/**
     * An ordered list of ABIs supported by this device
     *
     * @return ABI
     */
    public String[] getABIs() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return Build.SUPPORTED_ABIS;
        } else {
            return new String[]{Build.CPU_ABI, Build.CPU_ABI2};
        }
    }

或者如下adb命令获取:
在这里插入图片描述
关于ABI架构引用如下文章:

/p/68b52659a5f6

1.在android4.4以下版本的安装过程中的,是先把所有so库全部寻找出来,然后优先列出cpu类型(通过属性获得)目录下的so库,如果有其他的cpu类型下有跟手机cpu类型名称不一致的so库,则会将兼容cpu架构(通过.abi2属性获得)的另外的库也列出来。将列出来so库全部拷贝的系统指定目录,以供运行时加载。 即在android4.4以下版本,一个so库只要在 .abi2属性目录下至少存在一个就可以了。在android4.4以下的arm架构的设备 的属性通常是armeabi-v7a, .abi2的属性值是armeabi,并且armeabi-v7a设备一定兼容armeabi。但不排除某些设备的的属性为armeabi。

2.在android5.0及以上,由于增加了arm64的支持,app安装时的so库拷贝代码也修改了。修改成只拷贝一个最合适的目录下的so库到系统指定目录。 在arm架构下,64位cpu的优先级是arm64-v8a > armeabi-v7a > armeabi,32位cpu的优先级是armeabi-v7a > armeabi ,优先级可通过属性查看。由于android5.0是拷贝整体目录,所以在每一个目录下的,都必须要有完整的so,即所有app需要so库都要有。例如64位的cpu的设备上,
如果app目录里存在arm64-v8a子目录,则只拷贝该目录下的so库,其他目录的so,即使名称不一样,也不拷贝,如果arm64-v8a子目录的so库不全,则会报错。

  1. so加载方式不对
    目前有两种加载方式(以某个普通app下有个文件为例):
    ("so_name_but_no_'lib'_prefix") :会优先查找apk中的so目录,再查找系统目录,系统目录包括:/vendor/lib(64),/system/lib(64)
    ("so_absolute_path"): 直接加载制定路径下的so
System.loadLibrary("test");// 
String soRoot = context.getApplicationInfo().nativeLibraryDir;
System.load(soRoot + "/");// 
  1. Google官方更新进行的调整和修改

/guide/topics/manifest/application-element?hl=zh-cn

针对AGP >= 3.6.0需要在清单文件中添加如下配置

<application
	android:extractNativeLibs="true"
	... >
</application>    

针对AGP >= 4.2.0需要在DSL中添加如下配置(上面配置过时)

android {
    packagingOptions {
        jniLibs {
            useLegacyPackaging true
        }
    }
}
  1. 库文件没有访问权限
    .so文件有权限限制(文件读写权限/系统访问权限限制),不允许访问,可通过如下命令查看对应文件的访问权限:

在这里插入图片描述
关于权限说明参考如下:

/p/ba0d31fc078a

drwxr-xr-x:应该分解为四部分
d rwx r-x r-x

第一部分:d表示目录文件,-表示普通文件,l表示链接文件,p表示管道。
第二部分:root用户拥有的权限
第三部分:用户组拥有的权限
第四部分:其他用户拥有的权限

r表示可读,w表示可写,x表示可执行,r-x表示可读可执行不可写

查看文件权限方法:
ls -lO (大写的o) + 所在文件夹路径

举个栗子:
app需要引用系统的so库,当install形式的时候安装时,打开app需要使用/system/lib64目录下的so库时,提示不能访问。
原因分析:放到system/app下的app是可以找到的,但是普通的安装形式的app是没有权限访问的,所以需要需要声明公有so库才能使用

library "/system/lib64/" ("/system/lib64/") needed or
dlopened by"/system/lib64/" is not accessible for the namespace
"classloader-namespace"

解决方法:
修改system/core/rootdir/etc/
添加你要使用的的so到此问题件中。

cat  
....


也可以直接不编译在板子上修改,对应目录/system/etc

/2020/04/04/JNI%E6%97%A0%E6%B3%95%E8%AE%BF%E9%97%AEso%E6%8F%90%E7%A4%BAnot%20accessible/
/p/4be3d1dafbec