使用 binfmt_misc
到目前为止,我们讨论的是如何在 Intel x86 架构上加载 ARM 共享库。Houdini 还可以支持在 Intel 设备上运行独立的 ARM 应用程序。为此,它使用了一种称为 binfmt_misc 的机制。binfmt_misc 是 Linux 内核的一项功能,允许识别任意可执行文件格式并将其传递给某些用户空间应用程序,例如模拟器和虚拟机。
根据 Linux 内核文档,此内核功能允许您只需在 shell 中键入程序名称即可调用几乎任何程序。例如,这包括编译的 Java (TM)、Python 或 Emacs。要实现这一点,您必须告诉 binfmt_misc 应该使用哪个解释器来调用哪个二进制文件。binfmt_misc 通过将文件开头的某些字节与您提供的魔数字节序列(屏蔽掉指定的位)进行匹配来识别二进制类型。binfmt_misc 还可以识别文件名扩展名,例如 .com 或 .exe。
要使用此方法,首先我们必须挂载 binfmt_misc:
$ mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
要注册新的二进制类型,我们必须设置一个如下所示的字符串:
:name:type:offset:magic:mask:interpreter:flags
然后我们需要将其添加到 /proc/sys/fs/binfmt_misc/register。
以下是各字段的含义:
- name 是一个标识符字符串。将在
/proc/sys/fs/binfmt_misc目录下创建一个以此名称命名的新 /proc 文件。 - type 是识别类型。M 表示魔数(magic),E 表示扩展名(extension)。
- offset 是文件中魔数/掩码的偏移量,以字节为单位计数。如果省略它,则默认为 0(即,您写
:name:type::magic...)。 - magic 是 binfmt_misc 要匹配的字节序列。魔数字符串可能包含十六进制编码的字符,例如
\x0a或\xA4。在 shell 环境中,您应该写\\x0a以防止 shell 吃掉您的反斜杠。如果您选择匹配文件名扩展名,这是要识别的扩展名(不带点号,不允许使用\x0a特殊字符)。扩展名匹配区分大小写。 - mask 是一个(可选的,默认为所有 0xff)掩码。您可以通过提供类似魔数的字符串来屏蔽掉匹配中的某些位。
- interpreter 是应该以二进制文件作为第一个参数调用的程序(指定完整路径)。
- flags 是一个可选字段,控制解释器调用的几个方面。它是一个大写字母字符串,每个字母控制某个方面。
在 nativebridge.mk 中,它将一个 enable_nativebridge 脚本复制到系统文件夹。此文件用于在 Android-x86 中启用 Houdini。在 Android-x86 中,Houdini 默认未启用。这可以随时使用 Android-x86 设置应用程序中的选项来打开。当然,这在 AOSP 源代码中不受支持。当您在 Android-x86 中打开 Houdini 时,它会调用 enable_nativebridge 脚本。此脚本执行两件事:
- 它从第三方项目存储库下载 Houdini 到本地存储库,并将其安装到
/system/lib/arm系统目录中。它还将persist.sys.nativebridge属性设置为 1。 - 在此脚本的第二部分中,它在
/proc目录中创建 binfmt_misc 文件。
我们不会直接使用 enable_nativebridge 脚本,但我们希望在系统启动时运行 enable_nativebridge 的第二部分。通过第二部分,Houdini 在 Android 模拟器中默认启用。这可以通过将 enable_nativebridge 的第二部分添加到 device/generic/goldfish/init.goldfish.sh 来完成。以下是我们添加到 init.goldfish.sh 末尾的代码片段。这是用于在系统启动期间为 Android 模拟器设置环境的脚本:
...
# # Houdini integration (Native Bridge) #
houdini_bin=0
dest_dir=/system/lib$1/arm$1
binfmt_misc_dir=/proc/sys/fs/binfmt_misc
# if you don't see the files 'register' and 'status' in /proc/sys/fs/binfmt_misc
# then run the following command:
# mount -t binfmt_misc none /proc/sys/fs/binfmt_misc
# this is to add the supported binary formats via binfmt_misc
if [ ! -e $binfmt_misc_dir/register ]; then
mount -t binfmt_misc none $binfmt_misc_dir
fi
cd $binfmt_misc_dir
if [ -e register ]; then
# register Houdini for arm binaries
if [ -z "$1" ]; then
echo ':arm_exe:M::\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28::'"$dest_dir/houdini:P" > register
echo ':arm_dyn:M::\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x28::'"$dest_dir/houdini:P" > register
else
echo ':arm64_exe:M::\\x7f\\x45\\x4c\\x46\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xb7::'"$dest_dir/houdini64:P" > register
echo ':arm64_dyn:M::\\x7f\\x45\\x4c\\x46\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\xb7::'"$dest_dir/houdini64:P" > register
fi
if [ -e arm${1}_exe ]; then
houdini_bin=1
fi
else
log -pe -thoudini "No binfmt_misc support"
fi
if [ $houdini_bin -eq 0 ]; then
log -pe -thoudini "houdini$1 enabling failed!"
else
log -pi -thoudini "houdini$1 enabled"
fi
[ "$(getprop ro.zygote)" = "zygote64_32" -a -z "$1" ] && exec $0 64
在我们重新构建镜像并启动模拟器后,我们可以使用以下命令验证更改:
$ adb shell
root@x86emu:/ # ls /proc/sys/fs/binfmt_misc/
arm_dyn
arm_exe
register
status
我们可以看到我们注册了两种 binfmt_misc 类型:arm_dyn 和 arm_exe。/proc 文件 arm_dyn 用于加载共享库,arm_exe 用于加载 ARM 可执行文件:
root@x86emu:/ # cat /proc/sys/fs/binfmt_misc/arm_exe
enabled
interpreter /system/lib/arm/houdini
flags: P
offset 0
magic 7f454c46010101000000000000000000020028
如果我们查看 arm_exe 的内容,从上述输出中我们可以看到 /system/lib/arm/houdini 解释器用于解释 ARM 二进制文件。