[记录]Android终端直接运行qemu-user-static,附p7zip跑分效率参考[多图]

本文探讨了如何在Android终端使用qemu-user运行NDK编译的ELF程序,包括获取qemu-user-static、配置依赖库以及解决运行时问题,同时对比了不同架构间的效率比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘要

安卓基于linux内核,其终端也能够运行一些ELF,然而与各大发行版linux不同,Android使用的是谷歌安卓自己的bionic。网上有一些使用Termux环境安装qemu-user运行linux elf的案例,但termux环境终究不是纯粹的android shell。我在之前的一篇文章浅浅地涉及了Android Native Bridge API,而目前基于此API的成熟项目都会配备binfmt-misc,提供像qemu-user那样的功能。如果没有特殊需求的话,那些产品的binfmt-misc功能更完善成熟,效率更高,因此更值得首选;而如果需要切换到开源实现的话,从qemu-user入手是个不错的选择。

回到此篇的话题,经过测试,qemu-user-static不经修改基本可以直接在Android终端运行ndk编译的elf程序,但在某些方面上仍存在问题。下面介绍踩坑。

1. 获取qemu-user-static

在这里我为了方便直接使用现成的,你也可以自行编译以更好地适应Android的环境,编译必须要让编译产物是静态、无外部链接依赖,应该还要让其PIE。

在此页面中定位到qemu-user-static一处,可以看到其deb预构建包的历史版本和与构建版本:https://packages.debian.org/search?keywords=qemu

选择一个版本,比如unstable,接着就可以选择能在我们自己系统上运行的架构,我主要在基于x86_64的安卓上运行,因此选择amd64,此外我还会用x86的32位、以及在手机上测试运行,分别对应的是i386和arm64。

在出来的页面下有很多ftp站点链接,建议挑选亚洲的,右键另存为进行下载。

下载deb包直接用7zip打开,定位到usr/bin,这里就有很多模拟目标架构的qemu-user-static程序可供选择了,在x86_64安卓上我们主要运行arm64和arm的程序,按实际需求选择即可,需要留意里边部分文件是软链接。

2. 初步运行

到这里其实你直接adb连上安卓,adb shell终端运行就完事了。首先adb的环境,连接,这里就不讲了,直接从adb操作开始。

既然是用户态翻译运行程序,首先我们得有一个异构程序,这里我使用xda上有人编译的没有外部库依赖的静态arm64版本adb和fastboot。链接:https://androidfilehost.com/?a=show&w=files&flid=50704

用adb将它们push进去,赋予权限,然后直接像图示那样,将运行adb64的命令作为qemu-user-static的参数,运行qemu-aarch64-static,可以看到程序正常运行了。

再看看fastboot,我还打印了linux内核信息。

很好,就这样结束了?

3. 配置依赖库

当然不是,如果要运行接下来的p7zip程序7zr的话,你就会发现它报错。

所使用的p7zip:【Android 安装包优化】使用 lib7zr.so 动态库处理压缩文件 ( 修改 7zr 交叉编译脚本 Android.mk | 交叉编译 lib7zr.so 动态库 )_7z 动态库交叉编译-CSDN博客

如果我们在linux上的话p7zip大概会链接到自身架构指定的那个链接器路径,而网上也介绍这种情况,需要下载程序对应架构所需要的环境,就像box86运行x86程序前前要补齐i386系统库。

那么这里的p7zip是一个ndk构建的专门在安卓上运行的elf程序,而它指明了linker的路径为/system/bin/linker64,显然,这个x86安卓里这个路径是一个x86_64版本的linker,而不是arm64的版本。我们查看一下qemu-aarch64-static的帮助:

其中选项:

-L path              QEMU_LD_PREFIX       set the elf interpreter prefix to 'path'

正好是我们需要的,我们需要在这个选项指定的目录,补充程序的系统库依赖。

好了,去哪找依赖呢,我的程序是arm64程序,当然在arm64安卓里了。

这里我们来偷个懒,之前我的文章就介绍了houdini64拥有相应arm64文件夹,里边的库等等正好是我们所需的arm64二进制文件。我的安卓是genymotion里的安卓12模拟器,那就直接去mumu12模拟器提取我们所需要的库文件和linker64(补充:linker64不能偷懒,此linker64是改过linker寻库路径的,这可能导致我们部分程序不能运行)。之后在我这个x86安卓的一个目录下,新建system→lib64,在里边存放库;新建system→bin,在里边存放linker64。我就直接把这个目录创建在/data/local/tmp/下取名tmp_arm64,这样方便改权限。(注,此处偷懒仅限houdini12及以下版本)

我们补全依赖,指定QEMU_LD_PREFIX后,7zr运行成功。

那,我的arm64手机上运行x86_64程序的依赖又该怎么办?

如果是复杂的程序,那很可能要从x86_64安卓模拟器的系统里照猫画虎,收集x86_64的linker和库。收集时几点需要注意的:

  1. ①从安卓10开始许多库转移到了apex中;
  2. ②建议对要运行的程序使用patchelf来查看其依赖,再查看依赖库的依赖库,来确保补全依赖;
  3. ③程序可能有自己的依赖库,参考网上方法使用-E设置LD_PRELOAD变量;

4. 补充说明

4.1 为何上述偷懒仅限houdini12及以下版本?

在houdini13之前,houdini可执行文件与libhoudini是分开的,houdini可执行文件就像一个独立的qemu-user-static,没有任何外部依赖,因此arm文件夹下先是包含houdini所需的arm库环境,然后里边的nb文件夹才是libhoudini的环境,nb文件夹中的库并不是原arm库,而是类似wrapper/proxy/thunking的魔改库。

这部分可以参考Berberis的介绍文档:https://cs.android.com/android/platform/superproject/main/+/main:frameworks/libs/binary_translation/docs/berberis_modules.pdf

在houdini13+和ndk_translation中,可执行文件变成需要依赖其对接Android Api的库了,因此猜测其arm库环境便混合在一起,不需要分成两套了。

4.2 可否在32位上模拟运行64位的架构?

在后边的环节中,我均无法实现,qemu-user-static的tcg会有以下报错:

../accel/tcg/user-exec.c:492: page_set_flags: Assertion `start < end' failed.

补充:可以,但存在部分程序有上述报错,可能与qemu有关;在某些环节,比如7z压缩,速度会异常慢,详见附录。

4.3 能否直接像注册houdini/ndk_translation那样注册binfmt_misc到安卓?

尽管我未测试,但我认为默认预构建的版本不能,在我实践过程中,默认QEMU_LD_PREFIX就不对了,且在后边出现了arm64平台上qemu-i386/arm-static无法直接运行,提示:

Unable to find a guest_base to satisfy all guest address mapping requirements 00000000-ffffffff

我需要修改GUEST_BASE才可让其运行。因此,qemu-user-static编译前仍需一些修改和配置编译参数,才可直接应用于安卓的binfmt_misc功能,尤其是指定其配套环境的位置,设置模拟的cpuinfo等。

4.4 box86/box64可以这么直接用吗?

截至当前box86/box64还未完全支持静态构建,因此termux环境对这俩是必要的;待它们能构建静态版本后我再测试。

5. p7zip跑分效率结果

声明:跑分仅作参考,影响因素与衡量因素是有很多的,此处计算效率取:目标测试分数 / 翻译器运行于何种架构上的原生分数。分数取最终结果里的Tot一栏的Rating MIPS值。

x86_64翻译运行arm64 (i5-1137g7 in vbox64, 4c4t)

翻译器/数据

Native原生x86_64

Houdini64 12.0.0

QEMU x86_64 8.2.2

分数(取Tot)

12371

7991

3317

效率比

100%

64.6%

26.8%

x86_64/x86 翻译运行arm

翻译器/数据

Native原生x86_64

QEMU x86_64 8.2.2

分数(取Tot)

12371

3262

效率比

100%

26.4%

翻译器/数据

Native原生x86

Houdini 12.0.0

QEMU x86 8.2.2

分数(取Tot)

11020

9066

2961

效率比

100%

82.3%

26.9%

x86_64 翻译运行x86

翻译器/数据

Native原生x86_64

QEMU x86_64 8.2.2

分数(取Tot)

12371

3860

效率比

100%

31.2%

arm64翻译运行x86_64/x86/arm(sdm865,8c8t) QEMU arm64 8.2.2

翻译器/数据

Native原生arm64

QEMU

Guest: x86_64

QEMU

Guest: x86

QEMU

Guest: arm

分数(取Tot)

13667

2035

1494

2952

效率比

100%

14.9%

10.9%

21.6%

附录

Native原生运行 x86_64/ x86

转译参考:houdini12.0.0,需要其依赖的lib和linker,直接在mumu12模拟器环境就行了,64位翻译arm64,32位翻译arm

X86_64版qemu-user-static,翻译arm64,arm

X86版qemu-user-static,翻译arm

特殊:x86_64版翻译x86

Native原生运行 arm64/arm

arm64版qemu-user-static,翻译x86_64、x86、arm

补充:32位可以模拟64位架构,但7z压缩效率堪忧,解压效率倒是可以,很奇怪。

32位x86安卓10,7zr解压缩3个文件。使用qemu-user-static 8.2.2测试tcg翻译性能,time命令计时:

x86原生:压缩6.5s,解压:0.25s

arm翻译:压缩18.5s,解压:0.78s

x86_64翻译:压缩154.5s,解压:1.05s

arm64翻译:压缩442.8s,解压:1.17s

x86安卓10环境,模拟arm,x86_64,arm64

原生

arm

x86_64

arm64不测了,压缩分数估计只有个位数。

测试32位arm环境,安卓11,7zr压缩4个文件,使用qemu-user-static 8.2.5测试tcg翻译性能,time命令计时:

压缩:

arm原生:0.84s

x86翻译:773.56s

arm64翻译:遥遥无期

x86_64翻译:qemu报错,qemu9.0有signal11错误

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值