Android API31/32执行可执行文件报error=13权限拒绝如何解决
问题原因
你遇到的error=13, Permission denied和Android清单权限、运行时权限申请没有关系,核心是Android 12(API 31)开始强制落地的W^X(写时不可执行)安全策略:
- API 24/25等低版本Android系统中,应用私有目录下的
getFilesDir()、getCacheDir()路径默认带执行权限,你放在该路径下的Rust交叉编译二进制可以正常启动 - 从API 31开始,上述应用私有可写目录会被系统以
noexec标记挂载,哪怕你手动给二进制文件设置777权限,内核也会直接拦截执行请求,直接抛出权限拒绝错误。
解决方法
方案1(最稳定,官方兼容):将二进制放入应用原生库目录
这是官方支持的可执行文件存放路径,不会触发noexec限制,操作步骤:
- 重命名你编译好的Rust可执行文件,要求以
lib开头、后缀为.so,比如原文件名plugintest改为libplugintest.so - 根据CPU架构将文件放入Android工程对应jniLibs目录,比如ARM64架构放到
src/main/jniLibs/arm64-v8a/,32位ARM放到src/main/jniLibs/armeabi-v7a/,随APK打包 - 替换原有代码中二进制文件的获取逻辑,不要从files目录读取,直接从nativeLibraryDir获取:
// 替换原有pluginFilePath的初始化逻辑 File pluginFilePath = new File(getApplicationInfo().nativeLibraryDir, "libplugintest.so"); // 后续ProcessBuilder逻辑保持不变即可 ProcessBuilder processBuilder = new ProcessBuilder(); processBuilder.command(pluginFilePath.getCanonicalPath(), "-c", pingPath.getCanonicalPath(), "-lp" , binFilePath1.getCanonicalPath(), "-o", resultPath.getCanonicalPath()); Process process = processBuilder.start();
注意:不要把lib目录下的so文件拷贝到files/cache目录再执行,拷贝后依然会触发noexec限制。
方案2(适合动态下发插件场景):改造为原生库通过JNI调用
如果你需要动态下发Rust编译的程序、不能随APK打包,可以把Rust代码的编译目标改为cdylib,编译成标准的Android原生共享库,下发后通过System.load()加载,再通过JNI定义的接口调用对应逻辑,完全绕开文件执行的权限限制,全版本兼容。
避坑说明
- 不需要浪费时间申请
READ_EXTERNAL_STORAGE、MANAGE_EXTERNAL_STORAGE等存储权限,Android 10以后公共存储目录同样默认带noexec标记,即使有权限访问文件也无法执行 - 不需要尝试通过
chmod命令修改文件权限,目录挂载的noexec标记优先级高于文件本身的权限位,修改权限无法解决问题 - 不要尝试将二进制放到
/data/local/tmp等系统临时目录,普通第三方应用无权限操作该路径下的可执行文件
内容的提问来源于stack exchange,提问作者HiteshGs
相关产品推荐
相关产品推荐

