fix(ci): inject LD_PRELOAD interceptor to redirect all x86_64 ELF executions to QEMU

This commit is contained in:
2026-09-14 00:13:23 +02:00
parent e8556839b6
commit 23bc802dae
3 changed files with 187 additions and 61 deletions
+93 -30
View File
@@ -34,36 +34,9 @@ jobs:
# On Linux ARM64, Flutter engine and Android AAPT2 need x86_64 emulation via QEMU.
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
echo "Setting up ARM64 Flutter environment & QEMU binfmt emulation..."
apt-get update -qq && apt-get install -y -qq qemu-user-static binfmt-support libc6-amd64-cross libstdc++6-amd64-cross
apt-get update -qq && apt-get install -y -qq qemu-user-static binfmt-support libc6-amd64-cross libstdc++6-amd64-cross gcc
# Mount binfmt_misc pseudo-filesystem (supported under --privileged)
mkdir -p /proc/sys/fs/binfmt_misc
if ! grep -qs '/proc/sys/fs/binfmt_misc' /proc/mounts; then
mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc 2>/dev/null || true
fi
# Cleanly unregister any conflicting or broken host binfmt handlers (e.g. host FEX)
if [ -f /proc/sys/fs/binfmt_misc/register ]; then
echo "=== Active binfmt_misc handlers before cleanup ==="
ls -la /proc/sys/fs/binfmt_misc/ || true
for entry in /proc/sys/fs/binfmt_misc/*; do
[ -f "$entry" ] || continue
name="$(basename "$entry")"
[ "$name" = "register" ] && continue
[ "$name" = "status" ] && continue
if grep -qi "fex" "$entry" 2>/dev/null || [ "$name" = "FEX-x86_64" ] || [ "$name" = "fex-x86_64" ] || [ "$name" = "x86_64" ] || [ "$name" = "qemu-x86_64" ]; then
echo "Unregistering conflicting binfmt handler: $name"
echo -1 > "$entry" 2>/dev/null || true
fi
done
# Register QEMU x86_64 emulator directly with OCF flags (Fix-binary)
echo ':qemu-x86_64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00:\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-x86_64-static:OCF' > /proc/sys/fs/binfmt_misc/register 2>/dev/null || true
echo "=== Active binfmt_misc handlers after registration ==="
ls -la /proc/sys/fs/binfmt_misc/ || true
fi
# Setup x86_64 dynamic libraries & prefix for QEMU emulation of AAPT2 and tools
# Setup x86_64 dynamic libraries & prefix for QEMU emulation
mkdir -p /etc/qemu-binfmt
ln -sf /usr/x86_64-linux-gnu /etc/qemu-binfmt/x86_64
mkdir -p /lib64 /usr/lib64
@@ -74,9 +47,99 @@ jobs:
export QEMU_LD_PREFIX=/usr/x86_64-linux-gnu
echo "QEMU_LD_PREFIX=/usr/x86_64-linux-gnu" >> "$GITHUB_ENV"
# Create transparent execve/posix_spawn interceptor to route ALL x86_64 binaries to QEMU
cat << 'EOF' > /tmp/qemu_intercept.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <stdint.h>
#include <spawn.h>
static int is_x86_64_elf(const char *path) {
if (!path) return 0;
int fd = open(path, O_RDONLY);
if (fd < 0) return 0;
unsigned char header[20];
ssize_t n = read(fd, header, sizeof(header));
close(fd);
if (n < 20) return 0;
if (header[0] != 0x7f || header[1] != 'E' || header[2] != 'L' || header[3] != 'F') return 0;
if (header[4] != 2 || header[5] != 1) return 0;
uint16_t machine = (uint16_t)(header[18] | (header[19] << 8));
return machine == 62;
}
static char **build_qemu_argv(const char *pathname, char *const argv[]) {
int argc = 0;
while (argv && argv[argc]) argc++;
char **new_argv = malloc((argc + 4) * sizeof(char *));
new_argv[0] = "/usr/bin/qemu-x86_64-static";
new_argv[1] = "-L";
new_argv[2] = "/usr/x86_64-linux-gnu";
new_argv[3] = (char *)pathname;
for (int i = 1; i < argc; i++) {
new_argv[i + 3] = argv[i];
}
new_argv[argc + 3] = NULL;
return new_argv;
}
int execve(const char *pathname, char *const argv[], char *const envp[]) {
static int (*real_execve)(const char *, char *const [], char *const []) = NULL;
if (!real_execve) real_execve = dlsym(RTLD_NEXT, "execve");
if (pathname && is_x86_64_elf(pathname)) {
char **new_argv = build_qemu_argv(pathname, argv);
return real_execve("/usr/bin/qemu-x86_64-static", new_argv, envp);
}
return real_execve(pathname, argv, envp);
}
int execv(const char *pathname, char *const argv[]) {
extern char **environ;
return execve(pathname, argv, environ);
}
int execvp(const char *file, char *const argv[]) {
extern char **environ;
return execve(file, argv, environ);
}
int execvpe(const char *file, char *const argv[], char *const envp[]) {
return execve(file, argv, envp);
}
int posix_spawn(pid_t *pid, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[]) {
static int (*real_posix_spawn)(pid_t *, const char *, const posix_spawn_file_actions_t *,
const posix_spawnattr_t *, char *const [], char *const []) = NULL;
if (!real_posix_spawn) real_posix_spawn = dlsym(RTLD_NEXT, "posix_spawn");
if (path && is_x86_64_elf(path)) {
char **new_argv = build_qemu_argv(path, argv);
return real_posix_spawn(pid, "/usr/bin/qemu-x86_64-static", file_actions, attrp, new_argv, envp);
}
return real_posix_spawn(pid, path, file_actions, attrp, argv, envp);
}
int posix_spawnp(pid_t *pid, const char *file,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[]) {
return posix_spawn(pid, file, file_actions, attrp, argv, envp);
}
EOF
gcc -O2 -shared -fPIC -o /usr/local/lib/libqemu_intercept.so /tmp/qemu_intercept.c -ldl
echo "/usr/local/lib/libqemu_intercept.so" > /etc/ld.so.preload
echo "Installed transparent QEMU execution interceptor into /etc/ld.so.preload"
QEMU_BIN=$(command -v qemu-x86_64-static || command -v qemu-x86_64)
# Dummy fallbacks for FEX binaries/sockets to eliminate unexpected errors
# Dummy fallbacks for FEX binaries/sockets
for fex in /usr/bin/FEXInterpreter /usr/bin/FEX /usr/local/bin/FEXInterpreter /usr/bin/FEXServer /usr/local/bin/FEXServer; do
printf '#!/bin/sh\nexit 0\n' > "$fex"
chmod +x "$fex" 2>/dev/null || true
+93 -30
View File
@@ -34,36 +34,9 @@ jobs:
# On Linux ARM64, Flutter engine and Android AAPT2 need x86_64 emulation via QEMU.
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
echo "Setting up ARM64 Flutter environment & QEMU binfmt emulation..."
apt-get update -qq && apt-get install -y -qq qemu-user-static binfmt-support libc6-amd64-cross libstdc++6-amd64-cross
apt-get update -qq && apt-get install -y -qq qemu-user-static binfmt-support libc6-amd64-cross libstdc++6-amd64-cross gcc
# Mount binfmt_misc pseudo-filesystem (supported under --privileged)
mkdir -p /proc/sys/fs/binfmt_misc
if ! grep -qs '/proc/sys/fs/binfmt_misc' /proc/mounts; then
mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc 2>/dev/null || true
fi
# Cleanly unregister any conflicting or broken host binfmt handlers (e.g. host FEX)
if [ -f /proc/sys/fs/binfmt_misc/register ]; then
echo "=== Active binfmt_misc handlers before cleanup ==="
ls -la /proc/sys/fs/binfmt_misc/ || true
for entry in /proc/sys/fs/binfmt_misc/*; do
[ -f "$entry" ] || continue
name="$(basename "$entry")"
[ "$name" = "register" ] && continue
[ "$name" = "status" ] && continue
if grep -qi "fex" "$entry" 2>/dev/null || [ "$name" = "FEX-x86_64" ] || [ "$name" = "fex-x86_64" ] || [ "$name" = "x86_64" ] || [ "$name" = "qemu-x86_64" ]; then
echo "Unregistering conflicting binfmt handler: $name"
echo -1 > "$entry" 2>/dev/null || true
fi
done
# Register QEMU x86_64 emulator directly with OCF flags (Fix-binary)
echo ':qemu-x86_64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00:\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-x86_64-static:OCF' > /proc/sys/fs/binfmt_misc/register 2>/dev/null || true
echo "=== Active binfmt_misc handlers after registration ==="
ls -la /proc/sys/fs/binfmt_misc/ || true
fi
# Setup x86_64 dynamic libraries & prefix for QEMU emulation of AAPT2 and tools
# Setup x86_64 dynamic libraries & prefix for QEMU emulation
mkdir -p /etc/qemu-binfmt
ln -sf /usr/x86_64-linux-gnu /etc/qemu-binfmt/x86_64
mkdir -p /lib64 /usr/lib64
@@ -74,9 +47,99 @@ jobs:
export QEMU_LD_PREFIX=/usr/x86_64-linux-gnu
echo "QEMU_LD_PREFIX=/usr/x86_64-linux-gnu" >> "$GITHUB_ENV"
# Create transparent execve/posix_spawn interceptor to route ALL x86_64 binaries to QEMU
cat << 'EOF' > /tmp/qemu_intercept.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <stdint.h>
#include <spawn.h>
static int is_x86_64_elf(const char *path) {
if (!path) return 0;
int fd = open(path, O_RDONLY);
if (fd < 0) return 0;
unsigned char header[20];
ssize_t n = read(fd, header, sizeof(header));
close(fd);
if (n < 20) return 0;
if (header[0] != 0x7f || header[1] != 'E' || header[2] != 'L' || header[3] != 'F') return 0;
if (header[4] != 2 || header[5] != 1) return 0;
uint16_t machine = (uint16_t)(header[18] | (header[19] << 8));
return machine == 62;
}
static char **build_qemu_argv(const char *pathname, char *const argv[]) {
int argc = 0;
while (argv && argv[argc]) argc++;
char **new_argv = malloc((argc + 4) * sizeof(char *));
new_argv[0] = "/usr/bin/qemu-x86_64-static";
new_argv[1] = "-L";
new_argv[2] = "/usr/x86_64-linux-gnu";
new_argv[3] = (char *)pathname;
for (int i = 1; i < argc; i++) {
new_argv[i + 3] = argv[i];
}
new_argv[argc + 3] = NULL;
return new_argv;
}
int execve(const char *pathname, char *const argv[], char *const envp[]) {
static int (*real_execve)(const char *, char *const [], char *const []) = NULL;
if (!real_execve) real_execve = dlsym(RTLD_NEXT, "execve");
if (pathname && is_x86_64_elf(pathname)) {
char **new_argv = build_qemu_argv(pathname, argv);
return real_execve("/usr/bin/qemu-x86_64-static", new_argv, envp);
}
return real_execve(pathname, argv, envp);
}
int execv(const char *pathname, char *const argv[]) {
extern char **environ;
return execve(pathname, argv, environ);
}
int execvp(const char *file, char *const argv[]) {
extern char **environ;
return execve(file, argv, environ);
}
int execvpe(const char *file, char *const argv[], char *const envp[]) {
return execve(file, argv, envp);
}
int posix_spawn(pid_t *pid, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[]) {
static int (*real_posix_spawn)(pid_t *, const char *, const posix_spawn_file_actions_t *,
const posix_spawnattr_t *, char *const [], char *const []) = NULL;
if (!real_posix_spawn) real_posix_spawn = dlsym(RTLD_NEXT, "posix_spawn");
if (path && is_x86_64_elf(path)) {
char **new_argv = build_qemu_argv(path, argv);
return real_posix_spawn(pid, "/usr/bin/qemu-x86_64-static", file_actions, attrp, new_argv, envp);
}
return real_posix_spawn(pid, path, file_actions, attrp, argv, envp);
}
int posix_spawnp(pid_t *pid, const char *file,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[]) {
return posix_spawn(pid, file, file_actions, attrp, argv, envp);
}
EOF
gcc -O2 -shared -fPIC -o /usr/local/lib/libqemu_intercept.so /tmp/qemu_intercept.c -ldl
echo "/usr/local/lib/libqemu_intercept.so" > /etc/ld.so.preload
echo "Installed transparent QEMU execution interceptor into /etc/ld.so.preload"
QEMU_BIN=$(command -v qemu-x86_64-static || command -v qemu-x86_64)
# Dummy fallbacks for FEX binaries/sockets to eliminate unexpected errors
# Dummy fallbacks for FEX binaries/sockets
for fex in /usr/bin/FEXInterpreter /usr/bin/FEX /usr/local/bin/FEXInterpreter /usr/bin/FEXServer /usr/local/bin/FEXServer; do
printf '#!/bin/sh\nexit 0\n' > "$fex"
chmod +x "$fex" 2>/dev/null || true
+1 -1
View File
@@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
org.gradle.jvmargs=-Xmx1536M -Djdk.lang.Process.launchMechanism=vfork
android.useAndroidX=true
android.enableJetifier=true