前言

从Android N后就没搞过Android了,所以看到一堆新东西头皮发麻,但是houdini说是对intel优化好,J4125本来性能就不够,要用这玩意就没办法了(

原本解决这类问题应该去看源码,但是太多了,为了一个小问题去翻全部的更是头皮发麻(

手头没足够空间去编译镜像,于是看到了redroid-script这个脚本,通过编写dockerfile在原镜像的基础上添加文件生成新的镜像,免去重新编译。但看到脚本内带有houdini相关,主函数却没有带,寻思可能存在问题,手动加上后发现在系统里确实无法加载。错误如该issue一致Native Bridge (Houdini) on Android 12,即

library "/system/lib64/arm64/nb/libtcb.so" ("/system/lib64/arm64/nb/libtcb.so") needed or dlopened by "/system/lib64/libhoudini.so" is not accessible for the namespace

正文

在dockerhub看到了编译带houdini的redroid镜像。拉下来研究了一下,发现houdini的库存在于vendor,是通过软链接到system的。随即我手动尝试后发现报错变更,不是libtcb了,而是

E linker  : library "/apex/com.android.art/lib/libnativebridge.so" ("/apex/com.android.art/lib/libnativebridge.so") needed or dlopened by "/system/lib64/libhoudini.so" is not accessible for the namespace

到这一步现有的方案就无法继续下去了,在那个镜像中搜寻相关houdini的文件也未果,与github上提取的houdini仓库基本无异。

以前的经验使我第一反应考虑的是selinux,毕竟在github上提取的houdini有sepolicy相关。但是发现redroid的selinux是disabled状态,即排除。

随即查阅了namespace相关,在安卓的文档中链接器命名空间发现,android11之前,还可以通过/system/etc下的ld.config.*.txt来调整,该文件为静态。但是从android11开始,改为使用linkerconfig进行动态生成。

一开始我认为根目录下的linkerconfig有可能是固定的,毕竟redroid不存在分区,对照编译好的镜像对ld.config.txt进行修改,然后放到/linkerconfig下。重启镜像发现文件恢复了。随即发现bin下是有linkerconfig这个可执行文件,用来生成相关配置。
既然是动态生成,也就是在系统启动的过程中,肯定是有调用linkerconfig对其进行生成的。
通过对相关rc文件进行查找,最终发现在init.rc中,存在

    # Generate ld.config.txt for early executed processes
    exec -- /system/bin/bootstrap/linkerconfig --target /linkerconfig/bootstrap
    exec -- /system/bin/sh -c "/system/bin/patch /linkerconfig/bootstrap/ld.config.txt < /system/etc/ld_config.patch"

    chmod 644 /linkerconfig/bootstrap/ld.config.txt
    copy /linkerconfig/bootstrap/ld.config.txt /linkerconfig/default/ld.config.txt
    chmod 644 /linkerconfig/default/ld.config.txt

也就是,先调用linkerconfig生成到/linkerconfig/bootstrap下,再把bootstrap挂载到/linkerconfig。原本认为linkerconfig生成应该是依赖某些json或者txt给出目录生成,在系统下找了一圈没看到。回到linkerconfig文档查看,发现编译的时候用linker.config.json定义好了。。一些公共库就定义在/system/etc/public.libraries.txt下。

那没办法改linkerconfig根据指定配置生成,就只能对其hack了(

先尝试了将改好的ld.config.txt放在etc下,在init.rc调用了生成后,加入copy将其复制过去。启动容器,查看日志,houdini可正常调用。
随后觉得直接复制现成的不够“优雅”,用稍微“优雅”一些的办法,做了个diff,调用patch命令对其打patch(

该方法持续到android14,(其实到13,但是13还没移除linkerconfig)打开android14 init.rc的时候发现,linkerconfig的执行文件在bin下找不到了。init.rc部分也变成了

    # Generate empty ld.config.txt for early executed processes which rely on
    # /system/lib libraries.
    write /linkerconfig/bootstrap/ld.config.txt \#
    write /linkerconfig/default/ld.config.txt \#
    chmod 644 /linkerconfig/bootstrap/ld.config.txt
    chmod 644 /linkerconfig/default/ld.config.txt

    # Mount bootstrap linker configuration as current
    mount none /linkerconfig/bootstrap /linkerconfig bind rec

谷歌更改了,谷歌认为没必要这么早就生成ld.config.txt。查询了相关更改Skip system/bin/bootstrap/linkerconfig写着减少存储使用和启动时间。。。
不是哥们,这能占多少啊,android猛堆配置的时代至于吗

有关commit也没用查到linkerconfig现在由什么生成了。没有linkerconfig执行文件,但是又生成了文件,肯定是哪个服务代替它做了这个。

而且在一开始的安卓的文档中链接器命名空间也没有记载,一看左侧栏写着供应商NDK<=AOSP 14。。。

那就没办法了,先采用最经典的二分法,android启动过程无非也就on init,on postfs那些。把patch命令逐步加在这部分区域,最后定位到了在on post-fs-data。
接着二分法,最后查到了apex相关。搜了一下,此事在init/README.md的perform_apex_config部分中亦有记载。。。
Performs tasks after APEXes are mounted. For example, creates data directories for the mounted APEXes, parses config file(s) from them, and updates linker configurations. Intended to be used only once when apexd notifies the mount event by setting apexd.status to ready. Use --bootstrap when invoking in the bootstrap mount namespace.
合着集成进init了,apexd还要更新一次命名空间配置,改为加在此后面,解决。

patch后出现了新的namespace报错,这次apex下的libart.so受到了影响,依旧加规则进ld.config.txt

做了一份补丁rote66/redroid_libhoudini_hack用于集成进redroid-script。