linux binfmt_misc 机制(x86 模拟 arm64 环境)

0 阅读2分钟

使用 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 脚本。此脚本执行两件事:

  1. 它从第三方项目存储库下载 Houdini 到本地存储库,并将其安装到 /system/lib/arm 系统目录中。它还将 persist.sys.nativebridge 属性设置为 1。
  2. 在此脚本的第二部分中,它在 /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_dynarm_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 二进制文件。