Houdini: run Arm 32-bit and 64-bit applications on an x86_64 system

Intel Houdini is a dynamic binary translation layer to run Arm applications on x86_64 systems. Its targeted platform is Android, but you can use it for _other_ platforms too. It’s much faster than Qemu at that task.

Houdini 6 with AArch64 on x86_64 compatibility is available at http://dl.android-x86.org/houdini/6_z/houdini.sfs and for AArch32 on x86_64 at http://dl.android-x86.org/houdini/6_y/houdini.sfs.

The patch function at https://everydaywithlinux.blogspot.com/2012/11/patch-strings-in-binary-files-with-sed.html will be used here.

mkdir arm64_x64
mkdir armv7_x64
unsquashfs houdini-6_z.sfs
cp squashfs-root/houdini64 arm64_x64/
cp squashfs-root/cpuinfo arm64_x64/
cp squashfs-root/linker64 arm64_x64/
rm -rf squashfs-root

unsquashfs houdini-6_y.sfs
cp squashfs-root/houdini armv7_x64/
cp squashfs-root/cpuinfo armv7_x64/
cp squashfs-root/linker armv7_x64/
rm -rf squashfs-root

First, the SquashFS image that Houdini ships in is unpacked.

patch_strings_in_file arm64_x64/houdini64 "/system/lib64/arm64/cpuinfo" "/usr/lib/houdini/cpu64"
patch_strings_in_file arm64_x64/houdini64 "/system/lib64/arm64/linker64" "/lib/ld-linux-aarch64.so.1"
patch_strings_in_file arm64_x64/houdini64 "/system/lib64/arm64/libaeabi_map.so" ""

patch_strings_in_file armv7_x64/houdini "/system/lib/arm/cpuinfo" "/usr/lib/houdini/cpu32"
patch_strings_in_file armv7_x64/houdini "/system/lib/arm/linker" "/usr/lib/houdini/ld.so"
patch_strings_in_file armv7_x64/houdini "/system/lib/arm/libaeabi_map.so" ""

Then, patches are made, note here that we patched the library loader path to /usr/lib/houdini/ld.so for 32-bit Arm binaries, that’s because we don’t have enough bytes to put in the full path.

:arm_exe:M::\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28::/usr/libexec/houdini:POC
:armv7_exe:M::\x7f\x45\x4c\x46\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28::/usr/libexec/houdini:POC
:arm64_exe:M::\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7::/usr/libexec/houdini64:POC
:arm64_exe_2:M::\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\xb7::/usr/libexec/houdini64:POC
:arm64_exe_3:M::\x7f\x45\x4c\x46\x02\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7::/usr/libexec/houdini64:POC

That’s the content of the houdini.conf file that’ll be used by binfmt_misc to redirect the attempts to run Arm applications to Houdini.

mkdir -p /usr/libexec
install -m 755 arm64_x64/houdini64 $DESTDIR/usr/libexec/houdini64
install -m 755 armv7_x64/houdini $DESTDIR/usr/libexec/houdini

mkdir -p /usr/lib/houdini
install -m 644 arm64_x64/cpuinfo $DESTDIR/usr/lib/houdini/cpu64
install -m 644 armv7_x64/cpuinfo $DESTDIR/usr/lib/houdini/cpu32
ln -s /lib/ld-linux-armhf.so.3 $DESTDIR/usr/lib/houdini/ld.so

mkdir -p /etc/binfmt.d
install -m 644 houdini.conf $DESTDIR/etc/binfmt.d/houdini.conf

systemctl restart systemd-binfmt.service

You can then install to your system this way, or by making a proper package. (to run as root)

You’ll then have to copy over those files to the Arm chroot that your distribution will run in too. For 32-bit Arm, it’s also needed to link /usr/lib/houdini/ld.so to your library loader.

You can then set the bind mounts to /sys, /dev, /dev/pts and /proc. Afterwards, you can chroot and use the Arm environment.

In case of Linux distributions using the musl C library, you’ll have to link /lib/ld-linux-armhf.so.3 and /lib/ld-linux-aarch64.so.1 to your libc path for the architecture. (on musl, the C library and the library loader are the same executable)

Note: If you try to access to the TPIDRURW MSR when using Houdini running a 32-bit Arm application, you’ll get a crash. This MSR is only used in practice when using Wine to run a 32-bit Arm Windows app.

Leave a comment

Your email address will not be published. Required fields are marked *