Files
DroidDeck/tools/linuxfs/preload/robust.c
The412Banner 4022351e22 SteamDeck: a standalone launcher for the native ARM64 Linux Steam client
Lifts Bannerlator's Linux/gamescope runtime out into an app of its own: the
Wayland compositor, the proot session, the runtime installer, the fake-evdev
controller path and the PulseAudio server, with none of the Wine, container or
store machinery around them.

One activity installs the runtime from the same winlator-contents catalog and a
second one runs the session: compositor on a SurfaceView, gamescope and Valve's
native arm64 client under proot, a physical pad republished into the fake-input
rings, and the session's milestones on screen until the first frame arrives.
2026-09-19 13:39:17 -04:00

80 lines
2.6 KiB
C

/*
* get_robust_list(2) for the session's own threads.
*
* Android's app policy refuses the call, and Steam's cross-process mutex insists on seeing the
* head glibc registered. The kernel's list protocol gives it away: a robust mutex this thread
* holds is linked directly behind the registered head, so locking a private one and following
* the link back finds the head without depending on libc's layout.
*/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <errno.h>
#include <linux/futex.h>
#include <pthread.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sys/uio.h>
#include <unistd.h>
static long (*real_syscall)(long, ...);
static struct robust_list_head *thread_robust_head(void) {
pthread_mutexattr_t attr;
pthread_mutex_t mutex;
struct robust_list_head *head = NULL;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
if (pthread_mutex_lock(&mutex) != 0) {
return NULL;
}
/* The mutex holds tids and lock words beside the link, so a word is only followed
* through a read that fails instead of faulting on a non-pointer. */
uintptr_t lo = (uintptr_t)&mutex, hi = lo + sizeof(mutex);
for (uintptr_t p = lo; p + sizeof(void *) <= hi && !head; p += sizeof(void *)) {
struct robust_list_head *candidate = *(struct robust_list_head **)p;
struct robust_list_head copy;
struct iovec local = {&copy, sizeof(copy)}, remote = {candidate, sizeof(copy)};
if ((uintptr_t)candidate & (sizeof(void *) - 1)) {
continue;
}
if (process_vm_readv(getpid(), &local, 1, &remote, 1, 0) != (ssize_t)sizeof(copy)) {
continue;
}
uintptr_t entry = (uintptr_t)copy.list.next;
if (entry >= lo && entry < hi && copy.futex_offset < 0 && copy.futex_offset > -256) {
head = candidate;
}
}
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex);
return head;
}
long syscall(long number, ...) {
va_list ap;
long args[6];
va_start(ap, number);
for (int i = 0; i < 6; i++) {
args[i] = va_arg(ap, long);
}
va_end(ap);
if (number == SYS_get_robust_list && (args[0] == 0 || args[0] == gettid())) {
struct robust_list_head *head = thread_robust_head();
if (!head) {
errno = ENOSYS;
return -1;
}
*(struct robust_list_head **)args[1] = head;
*(size_t *)args[2] = sizeof(*head);
return 0;
}
if (!real_syscall) {
real_syscall = (long (*)(long, ...)) dlsym(RTLD_NEXT, "syscall");
}
return real_syscall(number, args[0], args[1], args[2], args[3], args[4], args[5]);
}