Janky setup to run Flatpaks under FEX-Emu
  • Shell 91.8%
  • CMake 7.7%
  • Roff 0.5%
Find a file
2025-11-12 20:22:35 -03:00
overusr/lib Initial commit 2025-07-27 18:17:35 -03:00
.gitignore Initial commit 2025-07-27 18:17:35 -03:00
build-inner.sh Update fex (with terrible xxhash hack) 2025-11-12 20:22:35 -03:00
build.sh Update fex (with terrible xxhash hack) 2025-11-12 20:22:35 -03:00
fexwrap Update fex (with terrible xxhash hack) 2025-11-12 20:22:35 -03:00
LICENSE Initial commit 2025-07-27 18:17:35 -03:00
README.md Update fex (with terrible xxhash hack) 2025-11-12 20:22:35 -03:00
toolchain_x86_32.cmake Initial commit 2025-07-27 18:17:35 -03:00
toolchain_x86_64.cmake Initial commit 2025-07-27 18:17:35 -03:00

fexwrap

A janky temporary setup to run x86_64 Flatpaks (namely Steam) with FEX-Emu on aarch64.

"But at least we don't need a random distro rootfs anymore! xD"

How the fsck does it work and can it be turned into something less hacky?

All FEX itself needs to run is to have its own binaries (and a couple native aarch64 shared libraries it depends on) to be present on an otherwise x86_64 rootfs. To run with Full Potential™ i.e. with library forwarding (thunks), it just needs more host native libraries: the graphics drivers, libwayland, libdrm, etc.

The Freedesktop runtime being a usrmerged multiarch system, it's almost trivial to mount the runtimes into each other. First, we just need the x86_64 org.freedesktop.Platform to include an empty lib/aarch64-linux-gnu mountpoint and a lib/ld-linux-aarch64.so.1 -> aarch64-linux-gnu/ld-linux-aarch64.so.1 symlink. Something that, interestingly, the Sdk already has! — but we add that in an overlay mount. Then we mount the lib/aarch64-linux-gnu from the aarch64 Platform into the x86_64 one, mount the /fex overlay that contains our FEX build, and wrap the target program launch in a /fex/bin/FEX invocation. Boom, done, we don't even need any binfmt registration (!!!) since we don't ever exec aarch64 executables!

What this current setup does with that mechanism is, basically:

  • for the FEX build process (build.sh + build-inner.sh) we just run raw bwrap, using convenient /var/lib/flatpak/runtime/org.freedesktop.Sdk/x86_64/25.08/active/files type paths to mount everything;
  • for actually running the flatpaks, we set FLATPAK_BWRAP to a wrapper script fexwrap that injects a bunch of arguments for mounting overlays and launching under /fex/bin/FEX.

So the plan to make it more official would roughly look like the following:

  • add lib/aarch64-linux-gnu / lib/ld-linux-aarch64.so.1 to the x86_64 Platform (trivial)
  • teach Flatpak to mount the aarch64 Platform's lib subdirectory there (harder / more controversial) or just build an org.freedesktop.Sdk.Compat.aarch64/x86_64 (potentially easier but shame it would waste disk space on repeating the exact same files from the aarch64 Platform)
  • add an extension point for emulators, such as lib/emu or whatever (easy but with bikeshedding potential)
  • package FEX as an extension using said extension point (not upstream's job anyway)
  • teach Flatpak about running emulators (interesting)
  • teach Flatpak about mounting the native host GL extension, to enable thunks for GPU drivers (complicated by ldconfig, see bottom of this readme..)

Building and running

Getting x86 Mesa with the right driver

Unless you're running on a desktop board with a discrete GPU, you'll need this step. We'll be using mesa-git just because it's packaged as a nicely separate repo.

Either on a separate x86_64 machine or with qemu-user-static enabled, build mesa-git-extension editing elements/mesa.yml to use the GPU drivers your aarch64 machine needs that might be missing, e.g.

  - target_arch == "i686" or target_arch == "x86_64":
      gallium_drivers: softpipe,llvmpipe,zink,freedreno
      vulkan_drivers: swrast,freedreno

Build it for both i686 and x86_64, install it on your aarch64 machine.

If you're lazy, trust me, and are running on a Qualcomm Snapdragon device, you can instead download my build:

And just flatpak install it.

Well, also pin it to prevent automatic uninstallation:

flatpak pin org.freedesktop.Platform.GL.mesa-git
flatpak pin org.freedesktop.Platform.GL32.mesa-git

Building FEX for the Freedesktop Runtime

Get the build prerequisites (yes we're building with thunks / library forwarding support!):

flatpak install org.freedesktop.Sdk/aarch64/25.08 org.freedesktop.Sdk.Extension.llvm21/aarch64/25.08 org.freedesktop.Sdk/x86_64/25.08 org.freedesktop.Sdk.Compat.i386/x86_64/25.08 org.freedesktop.Sdk.Extension.toolchain-i386/x86_64/25.08

Also git clone https://github.com/FEX-Emu/FEX in this directory here.

Run ./build.sh to build! Hopefully it just works!

Edit fexwrap to make FEXWRAP_CHECKOUT actually point to the path to here (!!!)

(Yes, there's no good installability, intentionally since it's a temporary hack and so using it should remain awkward :p)

Running Steam

Get Steam:

flatpak install --arch=x86_64 flathub com.valvesoftware.Steam

And finally, run:

FLATPAK_BWRAP=<insert path to here>/fexwrap FLATPAK_GL_DRIVERS=mesa-git flatpak run --arch=x86_64 --env=SHARED_LIBRARY_GUARD=0 com.valvesoftware.Steam

Sometimes, it doesn't run on the first attempt (0.o) and the script freezes early before launching everything, in which case Ctrl-C and retry.

Thunks are kinda cursed

You can edit overlay/fex/share/fex-emu/Config.json to enable thunks, for example:

{
  "ThunksDB": { "GL": 1 },
  "Config": { "SilentLog": "0", "OutputLog": "stderr" }
}

and I've been able to run at least Portal 2 before even having built an x86_64 Mesa with the driver for my machine (freedreno).

But there are some unresolved cursed issues with thunks in this setup for now:

  • Somehow, cannot disable GL thunk just for steamwebhelper, because it stops loading with "libGL.so.1 not found"?! With "GL": 0 in the global config and with "GL": 1 in the steamwebhelper appconfig it loads fine. But the combo of "GL": 1 in Config and "GL": 0 in steamwebhelper AppConfig results in loading failure. Can leave it on and pass -cef-disable-gpu instead? :/
  • libGL (GLVND) normally finds the actual libGLX_mesa.so in this environment using ldconfig ( /run/flatpak/ld.so.conf.d/runtime-001-org.freedesktop.Platform.GL.default.conf is what actually points to /usr/lib/$ARCH-linux-gnu/GL/default/lib) but ldconfig is not cross-arch: if you feed the x86_64 one an aarch64 path, it ignores those libraries and warns about unknown architecture. So making HostThunks/libGL-host.so find aarch64 Mesa is hard. LD_LIBRARY_PATH is fully overwritten a thousand times on the way to the actual game binary by various Steam runtime scripts. HostEnv does not seem to affect this (I guess the rtld doesn't reread the environment all the time). The cursed workaround is to edit ~/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamLinuxRuntime/var/steam-runtime/run.sh changing export LD_LIBRARY_PATH="$steam_runtime_library_paths" to export LD_LIBRARY_PATH="$steam_runtime_library_paths:/usr/lib/aarch64-linux-gnu/GL/default/lib"