mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-15 03:37:09 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a6877423e |
No files matched your search
@@ -277,6 +277,12 @@ public final class LinuxRuntime {
|
||||
bind(cmd, fake.getPath() + ":" + entry[1]);
|
||||
}
|
||||
}
|
||||
// libpci prefers sysfs to procfs, and where the app may list /sys/bus/pci/devices but not
|
||||
// read the files of a device in it (a RedMagic's PCIe root port) it die()s the same way.
|
||||
// Every Chromium GPU process exits with it: the Steam client's, an Electron app's.
|
||||
if (unreadablePciDevice()) {
|
||||
bind(cmd, new File(root, "etc/droiddeck/empty").getPath() + ":/sys/bus/pci/devices");
|
||||
}
|
||||
bindGpuNode(context, cmd);
|
||||
bindAdrenoStats(cmd, extraBinds);
|
||||
bindCpuTemps(cmd, root);
|
||||
@@ -288,6 +294,16 @@ public final class LinuxRuntime {
|
||||
return specs;
|
||||
}
|
||||
|
||||
/** Whether /sys/bus/pci/devices lists a device whose vendor the app may not read. */
|
||||
private static boolean unreadablePciDevice() {
|
||||
File[] devices = new File("/sys/bus/pci/devices").listFiles();
|
||||
if (devices == null) return false;
|
||||
for (File device : devices) {
|
||||
if (!new File(device, "vendor").canRead()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* An app process may not open {@code /dev/dri} - the nodes exist but are labelled
|
||||
* {@code graphics_device}, which stock policy grants surfaceflinger and not us - yet libdrm and
|
||||
|
||||
@@ -428,7 +428,11 @@ def guest_environment(fex, env):
|
||||
result["FEX_APP_DATA_LOCATION"] = data + "/"
|
||||
result["FEX_APP_CACHE_LOCATION"] = CACHE_DIR + "/"
|
||||
result.setdefault("FEX_SILENTLOG", "0")
|
||||
result.setdefault("FEX_OUTPUTLOG", "stderr")
|
||||
# Empty: FEX writes to descriptor 2 itself. "stderr" has it write to its own duplicate, a low
|
||||
# number a program may dup2() its own descriptor onto: Chromium does so in each child it starts
|
||||
# and lists /proc/self/fd next, which FEX logs, into the child's IPC channel - the browser
|
||||
# never understood a child, and every Electron program hung without a window.
|
||||
result.setdefault("FEX_OUTPUTLOG", "")
|
||||
if fex["portable"]:
|
||||
result["FEX_PORTABLE"] = "1"
|
||||
else:
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* and Qt ask before they start one. Only in the desktop session.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -20,6 +19,8 @@
|
||||
#include <sys/statvfs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
#ifndef BINFMT_HANDLER
|
||||
#define BINFMT_HANDLER "/usr/local/bin/droiddeck-open"
|
||||
#endif
|
||||
@@ -96,7 +97,7 @@ static void handler_args(char **args, const char *path, char *const argv[], int
|
||||
|
||||
static void hand_off(const char *path, char *const argv[], char *const envp[]) {
|
||||
static int (*real)(const char *, char *const[], char *const[]);
|
||||
if (!real) real = (int (*)(const char *, char *const[], char *const[]))dlsym(RTLD_NEXT, "execve");
|
||||
if (!real) real = (int (*)(const char *, char *const[], char *const[]))bl_next("execve");
|
||||
int n = count(argv);
|
||||
char *args[n + 4];
|
||||
handler_args(args, path, argv, n);
|
||||
@@ -154,7 +155,7 @@ __attribute__((visibility("hidden"))) int bl_binfmt_spawn(pid_t *pid, const char
|
||||
|
||||
int execvpe(const char *file, char *const argv[], char *const envp[]) {
|
||||
static int (*real)(const char *, char *const[], char *const[]);
|
||||
if (!real) real = (int (*)(const char *, char *const[], char *const[]))dlsym(RTLD_NEXT, "execvpe");
|
||||
if (!real) real = (int (*)(const char *, char *const[], char *const[]))bl_next("execvpe");
|
||||
bl_binfmt_before_search(file, argv, envp);
|
||||
real(file, argv, envp);
|
||||
return bl_binfmt_after_search(file, argv, envp, errno);
|
||||
@@ -163,8 +164,8 @@ int execvpe(const char *file, char *const argv[], char *const envp[]) {
|
||||
int posix_spawnp(pid_t *pid, const char *file, const posix_spawn_file_actions_t *fa, const posix_spawnattr_t *attr,
|
||||
char *const argv[], char *const envp[]) {
|
||||
static spawn_fn real, spawn;
|
||||
if (!real) real = (spawn_fn)dlsym(RTLD_NEXT, "posix_spawnp");
|
||||
if (!spawn) spawn = (spawn_fn)dlsym(RTLD_NEXT, "posix_spawn");
|
||||
if (!real) real = (spawn_fn)bl_next("posix_spawnp");
|
||||
if (!spawn) spawn = (spawn_fn)bl_next("posix_spawn");
|
||||
char path[PATH_MAX];
|
||||
if (!desktop() || !lookup(file, path, sizeof path)) return real(pid, file, fa, attr, argv, envp);
|
||||
return bl_binfmt_spawn(pid, path, fa, attr, argv, envp, spawn);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
@@ -25,6 +24,8 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
#define DIR_TTL_NS 5000000000LL
|
||||
#define MAX_LISTINGS 16
|
||||
#define MAX_ENTRIES 100000
|
||||
@@ -334,7 +335,7 @@ static struct handle *lookup(DIR *d) {
|
||||
|
||||
#define REAL(name) static __typeof__(&name) next_##name
|
||||
#define NEXT(name) \
|
||||
if (next_##name == NULL) next_##name = (__typeof__(&name))dlsym(RTLD_NEXT, #name)
|
||||
if (next_##name == NULL) next_##name = (__typeof__(&name))bl_next(#name)
|
||||
|
||||
REAL(opendir);
|
||||
REAL(readdir64);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* descriptors stands in for the kernel's, and every other device is left to libdrm.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
@@ -17,6 +16,8 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
#define MAX_HANDLES 4096
|
||||
|
||||
/* DRM_IOCTL_GEM_CLOSE from drm.h: _IOW('d', 0x09, struct drm_gem_close { u32 handle, pad; }). */
|
||||
@@ -50,7 +51,7 @@ static int is_kgsl(int fd) {
|
||||
}
|
||||
|
||||
static void *real(const char *name) {
|
||||
void *fn = dlsym(RTLD_NEXT, name);
|
||||
void *fn = bl_next(name);
|
||||
if (!fn) {
|
||||
errno = ENOSYS;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Credit: fsync, the futex-based synchronization in Proton, by Elizabeth Figura and Paul Gofman;
|
||||
* futex_waitv(2) by André Almeida. */
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/futex.h>
|
||||
@@ -18,6 +17,8 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
#ifndef SYS_futex_waitv
|
||||
#define SYS_futex_waitv 449
|
||||
#endif
|
||||
@@ -112,12 +113,12 @@ static int fs_on(void) {
|
||||
}
|
||||
|
||||
static long fs_sys(long n, long a, long b, long c, long d, long e, long f) {
|
||||
if (!fs_real_syscall) fs_real_syscall = (long (*)(long, ...))dlsym(RTLD_NEXT, "syscall");
|
||||
if (!fs_real_syscall) fs_real_syscall = (long (*)(long, ...))bl_next("syscall");
|
||||
return fs_real_syscall(n, a, b, c, d, e, f);
|
||||
}
|
||||
|
||||
static void *fs_anon(size_t size) {
|
||||
if (!fs_real_mmap) fs_real_mmap = (void *(*)(void *, size_t, int, int, int, off_t))dlsym(RTLD_NEXT, "mmap");
|
||||
if (!fs_real_mmap) fs_real_mmap = (void *(*)(void *, size_t, int, int, int, off_t))bl_next("mmap");
|
||||
return fs_real_mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
}
|
||||
|
||||
@@ -341,9 +342,9 @@ static void fs_child(void) {
|
||||
}
|
||||
|
||||
__attribute__((constructor)) static void fs_init(void) {
|
||||
if (!fs_real_mmap) fs_real_mmap = (void *(*)(void *, size_t, int, int, int, off_t))dlsym(RTLD_NEXT, "mmap");
|
||||
if (!fs_real_mmap64) fs_real_mmap64 = (void *(*)(void *, size_t, int, int, int, off64_t))dlsym(RTLD_NEXT, "mmap64");
|
||||
if (!fs_real_munmap) fs_real_munmap = (int (*)(void *, size_t))dlsym(RTLD_NEXT, "munmap");
|
||||
if (!fs_real_mmap) fs_real_mmap = (void *(*)(void *, size_t, int, int, int, off_t))bl_next("mmap");
|
||||
if (!fs_real_mmap64) fs_real_mmap64 = (void *(*)(void *, size_t, int, int, int, off64_t))bl_next("mmap64");
|
||||
if (!fs_real_munmap) fs_real_munmap = (int (*)(void *, size_t))bl_next("munmap");
|
||||
if (fs_on()) pthread_atfork(fs_fork_prepare, fs_fork_parent, fs_child);
|
||||
}
|
||||
|
||||
@@ -571,7 +572,7 @@ static void fs_attach(const char *name, int fd, int create) {
|
||||
close(rfd);
|
||||
return;
|
||||
}
|
||||
if (!fs_real_mmap) fs_real_mmap = (void *(*)(void *, size_t, int, int, int, off_t))dlsym(RTLD_NEXT, "mmap");
|
||||
if (!fs_real_mmap) fs_real_mmap = (void *(*)(void *, size_t, int, int, int, off_t))bl_next("mmap");
|
||||
p = fs_real_mmap(NULL, FS_REGION, PROT_READ | PROT_WRITE, MAP_SHARED, rfd, 0);
|
||||
close(rfd);
|
||||
if (p == MAP_FAILED) {
|
||||
@@ -588,8 +589,8 @@ static void fs_attach(const char *name, int fd, int create) {
|
||||
|
||||
int shm_open(const char *name, int oflag, mode_t mode) {
|
||||
int fd, saved;
|
||||
if (!fs_real_shm_open) fs_real_shm_open = (int (*)(const char *, int, mode_t))dlsym(RTLD_NEXT, "shm_open");
|
||||
if (!fs_real_shm_unlink) fs_real_shm_unlink = (int (*)(const char *))dlsym(RTLD_NEXT, "shm_unlink");
|
||||
if (!fs_real_shm_open) fs_real_shm_open = (int (*)(const char *, int, mode_t))bl_next("shm_open");
|
||||
if (!fs_real_shm_unlink) fs_real_shm_unlink = (int (*)(const char *))bl_next("shm_unlink");
|
||||
if (!fs_real_shm_open) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@@ -605,7 +606,7 @@ int shm_open(const char *name, int oflag, mode_t mode) {
|
||||
|
||||
int shm_unlink(const char *name) {
|
||||
int r, saved;
|
||||
if (!fs_real_shm_unlink) fs_real_shm_unlink = (int (*)(const char *))dlsym(RTLD_NEXT, "shm_unlink");
|
||||
if (!fs_real_shm_unlink) fs_real_shm_unlink = (int (*)(const char *))bl_next("shm_unlink");
|
||||
if (!fs_real_shm_unlink) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@@ -622,7 +623,7 @@ int shm_unlink(const char *name) {
|
||||
|
||||
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off) {
|
||||
void *p;
|
||||
if (!fs_real_mmap) fs_real_mmap = (void *(*)(void *, size_t, int, int, int, off_t))dlsym(RTLD_NEXT, "mmap");
|
||||
if (!fs_real_mmap) fs_real_mmap = (void *(*)(void *, size_t, int, int, int, off_t))bl_next("mmap");
|
||||
p = fs_real_mmap(addr, len, prot, flags, fd, off);
|
||||
if (p == MAP_FAILED && bl_noexec_retry(prot, flags, fd)) p = fs_real_mmap(addr, len, prot & ~PROT_EXEC, flags, fd, off);
|
||||
if (p != MAP_FAILED && __builtin_expect(fs_fd >= 0, 0)) fs_track(p, len, flags, fd, off);
|
||||
@@ -631,7 +632,7 @@ void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off) {
|
||||
|
||||
void *mmap64(void *addr, size_t len, int prot, int flags, int fd, off64_t off) {
|
||||
void *p;
|
||||
if (!fs_real_mmap64) fs_real_mmap64 = (void *(*)(void *, size_t, int, int, int, off64_t))dlsym(RTLD_NEXT, "mmap64");
|
||||
if (!fs_real_mmap64) fs_real_mmap64 = (void *(*)(void *, size_t, int, int, int, off64_t))bl_next("mmap64");
|
||||
p = fs_real_mmap64(addr, len, prot, flags, fd, off);
|
||||
if (p == MAP_FAILED && bl_noexec_retry(prot, flags, fd)) p = fs_real_mmap64(addr, len, prot & ~PROT_EXEC, flags, fd, off);
|
||||
if (p != MAP_FAILED && __builtin_expect(fs_fd >= 0, 0)) fs_track(p, len, flags, fd, off);
|
||||
@@ -642,7 +643,7 @@ int munmap(void *addr, size_t len) {
|
||||
uintptr_t lo = (uintptr_t)addr, hi = lo + len;
|
||||
sigset_t old;
|
||||
int r, saved;
|
||||
if (!fs_real_munmap) fs_real_munmap = (int (*)(void *, size_t))dlsym(RTLD_NEXT, "munmap");
|
||||
if (!fs_real_munmap) fs_real_munmap = (int (*)(void *, size_t))bl_next("munmap");
|
||||
if (__builtin_expect(fs_fd < 0, 1) || !fs_overlaps(lo, hi)) return fs_real_munmap(addr, len);
|
||||
fs_block(&old);
|
||||
pthread_mutex_lock(&fs_mx);
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
/* The app opens one ring per configured controller; four is what its launcher offers. Nodes from
|
||||
* UINPUT_BASE up are the virtual pads made through libfakeinput's /dev/uinput stand-in. */
|
||||
#define RING_MAX 4
|
||||
@@ -208,8 +210,6 @@ static struct pad *pad_for_syslink(const char *path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *next_symbol(const char *name) { return dlsym(RTLD_NEXT, name); }
|
||||
|
||||
char *bl_cached_realpath(const char *path, char *resolved, char *(*real)(const char *, char *))
|
||||
__attribute__((visibility("hidden")));
|
||||
|
||||
@@ -232,7 +232,7 @@ char *realpath(const char *path, char *resolved) {
|
||||
memcpy(resolved, pad->syspath, strlen(pad->syspath) + 1);
|
||||
return resolved;
|
||||
}
|
||||
if (real_realpath == NULL) real_realpath = next_symbol("realpath");
|
||||
if (real_realpath == NULL) real_realpath = bl_next("realpath");
|
||||
if (real_realpath == NULL) return NULL;
|
||||
return bl_cached_realpath(path, resolved, real_realpath);
|
||||
}
|
||||
@@ -243,7 +243,7 @@ void *udev_device_new_from_syspath(void *udev, const char *syspath) {
|
||||
struct pad *pad = caller_is_winebus(caller) ? pad_for_syspath(syspath) : NULL;
|
||||
|
||||
if (pad != NULL) return pad;
|
||||
if (real_new == NULL) real_new = next_symbol("udev_device_new_from_syspath");
|
||||
if (real_new == NULL) real_new = bl_next("udev_device_new_from_syspath");
|
||||
if (real_new == NULL) return NULL;
|
||||
return real_new(udev, syspath);
|
||||
}
|
||||
@@ -252,7 +252,7 @@ void *udev_device_ref(void *device) {
|
||||
static void *(*real_ref)(void *);
|
||||
|
||||
if (pad_for_device(device) != NULL) return device;
|
||||
if (real_ref == NULL) real_ref = next_symbol("udev_device_ref");
|
||||
if (real_ref == NULL) real_ref = bl_next("udev_device_ref");
|
||||
if (real_ref == NULL) return NULL;
|
||||
return real_ref(device);
|
||||
}
|
||||
@@ -261,7 +261,7 @@ void *udev_device_unref(void *device) {
|
||||
static void *(*real_unref)(void *);
|
||||
|
||||
if (pad_for_device(device) != NULL) return NULL;
|
||||
if (real_unref == NULL) real_unref = next_symbol("udev_device_unref");
|
||||
if (real_unref == NULL) real_unref = bl_next("udev_device_unref");
|
||||
if (real_unref == NULL) return NULL;
|
||||
return real_unref(device);
|
||||
}
|
||||
@@ -271,7 +271,7 @@ const char *udev_device_get_devnode(void *device) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return pad->devnode;
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_devnode");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_devnode");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device);
|
||||
}
|
||||
@@ -281,7 +281,7 @@ const char *udev_device_get_syspath(void *device) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return pad->syspath;
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_syspath");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_syspath");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device);
|
||||
}
|
||||
@@ -291,7 +291,7 @@ const char *udev_device_get_sysname(void *device) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return pad->sysname;
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_sysname");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_sysname");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device);
|
||||
}
|
||||
@@ -301,7 +301,7 @@ const char *udev_device_get_subsystem(void *device) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return "input";
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_subsystem");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_subsystem");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device);
|
||||
}
|
||||
@@ -311,7 +311,7 @@ const char *udev_device_get_devtype(void *device) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return NULL;
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_devtype");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_devtype");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device);
|
||||
}
|
||||
@@ -321,7 +321,7 @@ const char *udev_device_get_action(void *device) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return NULL;
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_action");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_action");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device);
|
||||
}
|
||||
@@ -331,7 +331,7 @@ dev_t udev_device_get_devnum(void *device) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return pad->devnum;
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_devnum");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_devnum");
|
||||
if (real_get == NULL) return makedev(0, 0);
|
||||
return real_get(device);
|
||||
}
|
||||
@@ -349,7 +349,7 @@ void *udev_device_get_parent_with_subsystem_devtype(void *device, const char *su
|
||||
if (subsystem != NULL && strcmp(subsystem, "input") == 0 && devtype == NULL) return pad;
|
||||
return NULL;
|
||||
}
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_parent_with_subsystem_devtype");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_parent_with_subsystem_devtype");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device, subsystem, devtype);
|
||||
}
|
||||
@@ -359,7 +359,7 @@ const char *udev_device_get_sysattr_value(void *device, const char *attr) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return attr != NULL && strcmp(attr, "uevent") == 0 ? pad->uevent : NULL;
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_sysattr_value");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_sysattr_value");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device, attr);
|
||||
}
|
||||
@@ -369,7 +369,7 @@ const char *udev_device_get_property_value(void *device, const char *key) {
|
||||
struct pad *pad = pad_for_device(device);
|
||||
|
||||
if (pad != NULL) return key != NULL && strcmp(key, "ID_INPUT_JOYSTICK") == 0 ? "1" : NULL;
|
||||
if (real_get == NULL) real_get = next_symbol("udev_device_get_property_value");
|
||||
if (real_get == NULL) real_get = bl_next("udev_device_get_property_value");
|
||||
if (real_get == NULL) return NULL;
|
||||
return real_get(device, key);
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
* becomes a rename that will not replace an existing name, which is all the link promised there.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
@@ -26,6 +25,8 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
static int refused(int error) { return error == EPERM || error == EACCES; }
|
||||
|
||||
static int moves_instead(void) {
|
||||
@@ -45,7 +46,7 @@ __attribute__((visibility("hidden"))) int bl_link_lock(const char *oldpath, cons
|
||||
if (result == 0 || !refused(errno)) return result;
|
||||
if (moves_instead()) return renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_NOREPLACE);
|
||||
if (!lock_name(newpath)) return result;
|
||||
if (real_symlink == NULL) real_symlink = dlsym(RTLD_NEXT, "symlink");
|
||||
if (real_symlink == NULL) real_symlink = bl_next("symlink");
|
||||
return real_symlink(oldpath, newpath);
|
||||
}
|
||||
|
||||
@@ -53,14 +54,14 @@ __attribute__((visibility("hidden"))) int bl_link_lock(const char *oldpath, cons
|
||||
#if __SIZEOF_POINTER__ != 8
|
||||
int link(const char *oldpath, const char *newpath) {
|
||||
static int (*real_link)(const char *, const char *);
|
||||
if (real_link == NULL) real_link = dlsym(RTLD_NEXT, "link");
|
||||
if (real_link == NULL) real_link = bl_next("link");
|
||||
return bl_link_lock(oldpath, newpath, real_link(oldpath, newpath));
|
||||
}
|
||||
#endif
|
||||
|
||||
int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) {
|
||||
static int (*real_linkat)(int, const char *, int, const char *, int);
|
||||
if (real_linkat == NULL) real_linkat = dlsym(RTLD_NEXT, "linkat");
|
||||
if (real_linkat == NULL) real_linkat = bl_next("linkat");
|
||||
int result = real_linkat(olddirfd, oldpath, newdirfd, newpath, flags);
|
||||
if (result != 0 && refused(errno) && flags == 0 && moves_instead())
|
||||
return renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
* answer is "nobody", so it is reported unlocked rather than granted.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
@@ -28,6 +27,8 @@
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
/* The filesystem refuses the whole operation, rather than declining this particular lock. */
|
||||
static int unsupported(int error) {
|
||||
return error == ENOSYS || error == ENOTSUP || error == EOPNOTSUPP;
|
||||
@@ -37,7 +38,7 @@ int flock(int fd, int operation) {
|
||||
static int (*real_flock)(int, int);
|
||||
int result;
|
||||
|
||||
if (real_flock == NULL) real_flock = dlsym(RTLD_NEXT, "flock");
|
||||
if (real_flock == NULL) real_flock = bl_next("flock");
|
||||
if (real_flock == NULL) return 0;
|
||||
result = real_flock(fd, operation);
|
||||
if (result < 0 && unsupported(errno)) return 0;
|
||||
@@ -88,7 +89,7 @@ int fcntl(int fd, int cmd, ...) {
|
||||
arg = va_arg(ap, void *);
|
||||
va_end(ap);
|
||||
|
||||
if (real_fcntl == NULL) real_fcntl = dlsym(RTLD_NEXT, "fcntl");
|
||||
if (real_fcntl == NULL) real_fcntl = bl_next("fcntl");
|
||||
if (real_fcntl == NULL) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <arpa/inet.h>
|
||||
#include <spawn.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -24,6 +23,8 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
static const char *net_dir(void) {
|
||||
static char dir[256];
|
||||
if (!dir[0]) {
|
||||
@@ -124,7 +125,7 @@ __attribute__((visibility("hidden"))) int bl_udevmon_stand_in(int fd);
|
||||
|
||||
int bind(int fd, const struct sockaddr *addr, socklen_t len) {
|
||||
static bind_fn real;
|
||||
if (!real) real = (bind_fn)dlsym(RTLD_NEXT, "bind");
|
||||
if (!real) real = (bind_fn)bl_next("bind");
|
||||
if (bl_udevmon_stand_in(fd)) {
|
||||
if (addr == NULL || len < sizeof(struct sockaddr_nl) || addr->sa_family != AF_NETLINK) {
|
||||
errno = EINVAL;
|
||||
@@ -151,7 +152,7 @@ int bind(int fd, const struct sockaddr *addr, socklen_t len) {
|
||||
|
||||
int listen(int fd, int backlog) {
|
||||
static listen_fn real;
|
||||
if (!real) real = (listen_fn)dlsym(RTLD_NEXT, "listen");
|
||||
if (!real) real = (listen_fn)bl_next("listen");
|
||||
int ret = real(fd, backlog);
|
||||
if (ret == 0) record_port(fd, 0);
|
||||
return ret;
|
||||
@@ -233,7 +234,7 @@ static int guest_peer(int fd) {
|
||||
|
||||
int connect(int fd, const struct sockaddr *addr, socklen_t len) {
|
||||
static connect_fn real;
|
||||
if (!real) real = (connect_fn)dlsym(RTLD_NEXT, "connect");
|
||||
if (!real) real = (connect_fn)bl_next("connect");
|
||||
bind_before_loopback_connect(fd, addr, len);
|
||||
unsigned peer = loopback_family(addr, len) ? addr_port(addr, len) : 0;
|
||||
if (peer) record_port(fd, peer);
|
||||
@@ -249,7 +250,7 @@ int connect(int fd, const struct sockaddr *addr, socklen_t len) {
|
||||
|
||||
int accept4(int fd, struct sockaddr *addr, socklen_t *len, int flags) {
|
||||
static accept4_fn real;
|
||||
if (!real) real = (accept4_fn)dlsym(RTLD_NEXT, "accept4");
|
||||
if (!real) real = (accept4_fn)bl_next("accept4");
|
||||
for (;;) {
|
||||
int ret = real(fd, addr, len, flags);
|
||||
if (ret < 0) return ret;
|
||||
@@ -327,7 +328,7 @@ static int maybe_answer_lsof(const char *path, char *const argv[]) {
|
||||
|
||||
int execve(const char *path, char *const argv[], char *const envp[]) {
|
||||
static int (*real)(const char *, char *const[], char *const[]);
|
||||
if (!real) real = (int (*)(const char *, char *const[], char *const[]))dlsym(RTLD_NEXT, "execve");
|
||||
if (!real) real = (int (*)(const char *, char *const[], char *const[]))bl_next("execve");
|
||||
maybe_answer_lsof(path, argv);
|
||||
bl_binfmt_before(path, argv, envp);
|
||||
real(path, argv, envp);
|
||||
@@ -353,7 +354,7 @@ static int answer_lsof_via_spawn(const char *path, char *const argv[], pid_t *pi
|
||||
|
||||
int execvp(const char *file, char *const argv[]) {
|
||||
static int (*real)(const char *, char *const[]);
|
||||
if (!real) real = (int (*)(const char *, char *const[]))dlsym(RTLD_NEXT, "execvp");
|
||||
if (!real) real = (int (*)(const char *, char *const[]))bl_next("execvp");
|
||||
maybe_answer_lsof(file, argv);
|
||||
bl_binfmt_before_search(file, argv, environ);
|
||||
real(file, argv);
|
||||
@@ -362,7 +363,7 @@ int execvp(const char *file, char *const argv[]) {
|
||||
|
||||
int execv(const char *path, char *const argv[]) {
|
||||
static int (*real)(const char *, char *const[]);
|
||||
if (!real) real = (int (*)(const char *, char *const[]))dlsym(RTLD_NEXT, "execv");
|
||||
if (!real) real = (int (*)(const char *, char *const[]))bl_next("execv");
|
||||
maybe_answer_lsof(path, argv);
|
||||
bl_binfmt_before(path, argv, environ);
|
||||
real(path, argv);
|
||||
@@ -377,6 +378,6 @@ int posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *
|
||||
return answer_lsof_via_spawn(path, argv, pid);
|
||||
}
|
||||
static spawn_fn real;
|
||||
if (!real) real = (spawn_fn)dlsym(RTLD_NEXT, "posix_spawn");
|
||||
if (!real) real = (spawn_fn)bl_next("posix_spawn");
|
||||
return bl_binfmt_spawn(pid, path, fa, attr, argv, envp, real);
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
*/
|
||||
#define _GNU_SOURCE 1
|
||||
#include <arpa/inet.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <ifaddrs.h>
|
||||
@@ -32,6 +31,8 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
#define LINK_FILE "/etc/droiddeck-net"
|
||||
#define LO_NAME "lo"
|
||||
#define LO_INDEX 1
|
||||
@@ -68,7 +69,7 @@ typedef FILE *(*fopen_fn)(const char *, const char *);
|
||||
|
||||
static int read_link(struct link *link) {
|
||||
static fopen_fn real_fopen;
|
||||
if (real_fopen == NULL) real_fopen = (fopen_fn) dlsym(RTLD_NEXT, "fopen");
|
||||
if (real_fopen == NULL) real_fopen = (fopen_fn) bl_next("fopen");
|
||||
FILE *f = real_fopen ? real_fopen(LINK_FILE, "re") : NULL;
|
||||
if (f == NULL) return 0;
|
||||
|
||||
@@ -222,8 +223,8 @@ static freeifaddrs_fn real_freeifaddrs;
|
||||
int getifaddrs(struct ifaddrs **out) {
|
||||
typedef int (*getifaddrs_fn)(struct ifaddrs **);
|
||||
static getifaddrs_fn real;
|
||||
if (real == NULL) real = (getifaddrs_fn) dlsym(RTLD_NEXT, "getifaddrs");
|
||||
if (real_freeifaddrs == NULL) real_freeifaddrs = (freeifaddrs_fn) dlsym(RTLD_NEXT, "freeifaddrs");
|
||||
if (real == NULL) real = (getifaddrs_fn) bl_next("getifaddrs");
|
||||
if (real_freeifaddrs == NULL) real_freeifaddrs = (freeifaddrs_fn) bl_next("freeifaddrs");
|
||||
struct ifaddrs *list = NULL;
|
||||
int asked = !fresh(&ifaddrs_unusable_at);
|
||||
int rc = -1, saved = errno;
|
||||
@@ -273,7 +274,7 @@ void freeifaddrs(struct ifaddrs *list) {
|
||||
free((struct entry *) list - 1);
|
||||
return;
|
||||
}
|
||||
if (real_freeifaddrs == NULL) real_freeifaddrs = (freeifaddrs_fn) dlsym(RTLD_NEXT, "freeifaddrs");
|
||||
if (real_freeifaddrs == NULL) real_freeifaddrs = (freeifaddrs_fn) bl_next("freeifaddrs");
|
||||
real_freeifaddrs(list);
|
||||
}
|
||||
|
||||
@@ -286,7 +287,7 @@ struct name_block {
|
||||
struct if_nameindex *if_nameindex(void) {
|
||||
typedef struct if_nameindex *(*if_nameindex_fn)(void);
|
||||
static if_nameindex_fn real;
|
||||
if (real == NULL) real = (if_nameindex_fn) dlsym(RTLD_NEXT, "if_nameindex");
|
||||
if (real == NULL) real = (if_nameindex_fn) bl_next("if_nameindex");
|
||||
struct if_nameindex *list = NULL;
|
||||
int asked = !fresh(&nameindex_unusable_at);
|
||||
int saved = errno;
|
||||
@@ -323,12 +324,12 @@ void if_freenameindex(struct if_nameindex *list) {
|
||||
free(list);
|
||||
return;
|
||||
}
|
||||
((if_freenameindex_fn) dlsym(RTLD_NEXT, "if_freenameindex"))(list);
|
||||
((if_freenameindex_fn) bl_next("if_freenameindex"))(list);
|
||||
}
|
||||
|
||||
unsigned int if_nametoindex(const char *name) {
|
||||
typedef unsigned int (*if_nametoindex_fn)(const char *);
|
||||
unsigned int index = ((if_nametoindex_fn) dlsym(RTLD_NEXT, "if_nametoindex"))(name);
|
||||
unsigned int index = ((if_nametoindex_fn) bl_next("if_nametoindex"))(name);
|
||||
if (index != 0 || name == NULL) return index;
|
||||
int saved = errno;
|
||||
struct link link;
|
||||
@@ -340,7 +341,7 @@ unsigned int if_nametoindex(const char *name) {
|
||||
|
||||
char *if_indextoname(unsigned int index, char name[IF_NAMESIZE]) {
|
||||
typedef char *(*if_indextoname_fn)(unsigned int, char *);
|
||||
char *found = ((if_indextoname_fn) dlsym(RTLD_NEXT, "if_indextoname"))(index, name);
|
||||
char *found = ((if_indextoname_fn) bl_next("if_indextoname"))(index, name);
|
||||
if (found || name == NULL) return found;
|
||||
int saved = errno;
|
||||
struct link link;
|
||||
@@ -439,7 +440,7 @@ int ioctl(int fd, unsigned long request, ...) {
|
||||
int rc;
|
||||
if (bl_ntsync_ioctl(fd, request, arg, &rc)) return rc;
|
||||
if (bl_drm_gem_close(fd, request, arg, &rc)) return rc;
|
||||
if (real == NULL) real = (ioctl_fn) dlsym(RTLD_NEXT, "ioctl");
|
||||
if (real == NULL) real = (ioctl_fn) bl_next("ioctl");
|
||||
if (!bl_kgsl_poll(real, fd, request, arg, &rc)) rc = real(fd, request, arg);
|
||||
bl_kgsl_chip_id_fixup(request, arg, rc);
|
||||
if (arg == NULL || !is_link_query(request)) return rc;
|
||||
@@ -579,12 +580,12 @@ static FILE *open_stream(fopen_fn real, const char *path, const char *mode) {
|
||||
|
||||
FILE *fopen(const char *path, const char *mode) {
|
||||
static fopen_fn real;
|
||||
if (real == NULL) real = (fopen_fn) dlsym(RTLD_NEXT, "fopen");
|
||||
if (real == NULL) real = (fopen_fn) bl_next("fopen");
|
||||
return open_stream(real, path, mode);
|
||||
}
|
||||
|
||||
FILE *fopen64(const char *path, const char *mode) {
|
||||
static fopen_fn real;
|
||||
if (real == NULL) real = (fopen_fn) dlsym(RTLD_NEXT, "fopen64");
|
||||
if (real == NULL) real = (fopen_fn) bl_next("fopen64");
|
||||
return open_stream(real, path, mode);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* next.c: the definition a hook passes a call on to, as dlsym(RTLD_NEXT, name) finds it.
|
||||
*
|
||||
* Before glibc 2.34 a thread's first dlsym callocs dlerror's state. A hook first called from inside
|
||||
* an allocator that is still starting up - Chromium's PartitionAlloc calls syscall() from its first
|
||||
* malloc - re-enters the allocator there, and it waits for itself forever: every Electron and
|
||||
* Chromium program on Steam's x86 runtime (glibc 2.31) hung at start. There the libraries after this
|
||||
* one are searched by hand, with nothing allocated; an object or symbol that walk cannot handle is
|
||||
* left to dlsym.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* NEXT_TEST: tools/tests builds the walk on a newer glibc. */
|
||||
#if __GLIBC_PREREQ(2, 34) && !defined(NEXT_TEST)
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name) {
|
||||
return dlsym(RTLD_NEXT, name);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
struct search {
|
||||
const char *name;
|
||||
uint32_t hash;
|
||||
int passed;
|
||||
int unsure;
|
||||
void *found;
|
||||
};
|
||||
|
||||
static uint32_t gnu_hash(const char *name) {
|
||||
uint32_t h = 5381;
|
||||
for (; *name; name++) h = h * 33 + (unsigned char)*name;
|
||||
return h;
|
||||
}
|
||||
|
||||
/* glibc relocates these entries in place, but not every object's (the vDSO's). */
|
||||
static ElfW(Addr) address(const struct dl_phdr_info *info, ElfW(Addr) value) {
|
||||
return value < info->dlpi_addr ? info->dlpi_addr + value : value;
|
||||
}
|
||||
|
||||
/* The object's definition, as glibc 2.36 and later pick it: unversioned or the default version. */
|
||||
static void find(const struct dl_phdr_info *info, const ElfW(Dyn) *dyn, struct search *s) {
|
||||
const uint32_t *table = NULL;
|
||||
const ElfW(Sym) *symbols = NULL;
|
||||
const char *strings = NULL;
|
||||
const ElfW(Half) *versions = NULL;
|
||||
for (; dyn->d_tag != DT_NULL; dyn++) {
|
||||
if (dyn->d_tag == DT_GNU_HASH) table = (const uint32_t *)address(info, dyn->d_un.d_ptr);
|
||||
else if (dyn->d_tag == DT_SYMTAB) symbols = (const ElfW(Sym) *)address(info, dyn->d_un.d_ptr);
|
||||
else if (dyn->d_tag == DT_STRTAB) strings = (const char *)address(info, dyn->d_un.d_ptr);
|
||||
else if (dyn->d_tag == DT_VERSYM) versions = (const ElfW(Half) *)address(info, dyn->d_un.d_ptr);
|
||||
}
|
||||
if (!table || !symbols || !strings || !table[0]) {
|
||||
s->unsure = 1;
|
||||
return;
|
||||
}
|
||||
const uint32_t *buckets = (const uint32_t *)((const ElfW(Addr) *)(table + 4) + table[2]);
|
||||
const uint32_t *chain = buckets + table[0] - table[1];
|
||||
const ElfW(Sym) *only = NULL;
|
||||
int matches = 0, defaults = 0;
|
||||
for (uint32_t i = buckets[s->hash % table[0]]; i >= table[1] && i != 0; i++) {
|
||||
const ElfW(Sym) *sym = &symbols[i];
|
||||
if ((chain[i] | 1) == (s->hash | 1) && sym->st_shndx != SHN_UNDEF && sym->st_value != 0 &&
|
||||
ELF64_ST_BIND(sym->st_info) != STB_LOCAL && !strcmp(strings + sym->st_name, s->name)) {
|
||||
if (ELF64_ST_TYPE(sym->st_info) == STT_GNU_IFUNC || ELF64_ST_TYPE(sym->st_info) == STT_TLS) {
|
||||
s->unsure = 1;
|
||||
return;
|
||||
}
|
||||
if (!versions || (versions[i] & 0x7fff) < 2) {
|
||||
only = sym;
|
||||
defaults = 1;
|
||||
break;
|
||||
}
|
||||
matches++;
|
||||
if (!(versions[i] & 0x8000) && defaults++ == 0) only = sym;
|
||||
}
|
||||
if (chain[i] & 1) break;
|
||||
}
|
||||
/* Only hidden versions: dlsym's own rules decide. */
|
||||
if (defaults == 1) s->found = (void *)(info->dlpi_addr + only->st_value);
|
||||
else if (matches) s->unsure = 1;
|
||||
}
|
||||
|
||||
static int visit(struct dl_phdr_info *info, size_t size, void *data) {
|
||||
struct search *s = data;
|
||||
const ElfW(Dyn) *dyn = NULL;
|
||||
(void)size;
|
||||
for (int i = 0; i < info->dlpi_phnum; i++)
|
||||
if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) dyn = (const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
|
||||
if (!dyn) return 0;
|
||||
if (!s->passed) {
|
||||
s->passed = dyn == _DYNAMIC;
|
||||
return 0;
|
||||
}
|
||||
find(info, dyn, s);
|
||||
return s->found || s->unsure;
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name) {
|
||||
struct search s = {name, gnu_hash(name), 0, 0, NULL};
|
||||
dl_iterate_phdr(visit, &s);
|
||||
if (s.found || (s.passed && !s.unsure)) return s.found;
|
||||
return dlsym(RTLD_NEXT, name);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <sys/statvfs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
/* NOEXEC_TEST: tools/tests builds it for the host. */
|
||||
#if defined(__aarch64__) || defined(NOEXEC_TEST)
|
||||
|
||||
@@ -42,7 +44,7 @@ static int (*real_mprotect)(void *, size_t, int);
|
||||
|
||||
/* Looked up before anything runs: Wine calls mprotect from its signal handlers, where dlsym is unsafe. */
|
||||
__attribute__((constructor)) static void noexec_init(void) {
|
||||
real_mprotect = (int (*)(void *, size_t, int))dlsym(RTLD_NEXT, "mprotect");
|
||||
real_mprotect = (int (*)(void *, size_t, int))bl_next("mprotect");
|
||||
}
|
||||
|
||||
int mprotect(void *addr, size_t len, int prot) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* droiddeck-ntsync: a userspace implementation of the /dev/ntsync interface for kernels without the driver.
|
||||
* Credit: ntsync, the kernel driver and its Wine client, by Elizabeth Figura. */
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/futex.h>
|
||||
@@ -21,6 +20,8 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
struct ns_sem_args {
|
||||
uint32_t count;
|
||||
uint32_t max;
|
||||
@@ -194,7 +195,7 @@ static int ns_on(void) {
|
||||
}
|
||||
|
||||
static int ns_close_fd(int fd) {
|
||||
if (!ns_real_close) ns_real_close = (int (*)(int))dlsym(RTLD_NEXT, "close");
|
||||
if (!ns_real_close) ns_real_close = (int (*)(int))bl_next("close");
|
||||
return ns_real_close(fd);
|
||||
}
|
||||
|
||||
@@ -1242,7 +1243,7 @@ int bl_udevmon_recvmsg(int fd, struct msghdr *msg, int flags, ssize_t (*real_rec
|
||||
|
||||
ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
|
||||
static ssize_t (*real)(int, struct msghdr *, int);
|
||||
if (!real) real = (ssize_t (*)(int, struct msghdr *, int))dlsym(RTLD_NEXT, "recvmsg");
|
||||
if (!real) real = (ssize_t (*)(int, struct msghdr *, int))bl_next("recvmsg");
|
||||
/* udevmon.c's stand-in netlink sockets first: their messages are dressed as udevd's. */
|
||||
ssize_t answered;
|
||||
if (msg && bl_udevmon_recvmsg(fd, msg, flags, real, &answered)) return answered;
|
||||
@@ -1266,7 +1267,7 @@ int close(int fd) {
|
||||
|
||||
int dup2(int oldfd, int newfd) {
|
||||
static int (*real)(int, int);
|
||||
if (!real) real = (int (*)(int, int))dlsym(RTLD_NEXT, "dup2");
|
||||
if (!real) real = (int (*)(int, int))bl_next("dup2");
|
||||
if (oldfd != newfd && newfd >= 0) bl_fsync_fds_closed((unsigned int)newfd, (unsigned int)newfd);
|
||||
uint64_t *t = __atomic_load_n(&ns_fdt, __ATOMIC_ACQUIRE);
|
||||
if (t && oldfd != newfd && newfd >= 0 && newfd < NS_FDS && __atomic_load_n(&t[newfd], __ATOMIC_ACQUIRE)) ns_forget(newfd);
|
||||
@@ -1275,7 +1276,7 @@ int dup2(int oldfd, int newfd) {
|
||||
|
||||
int dup3(int oldfd, int newfd, int flags) {
|
||||
static int (*real)(int, int, int);
|
||||
if (!real) real = (int (*)(int, int, int))dlsym(RTLD_NEXT, "dup3");
|
||||
if (!real) real = (int (*)(int, int, int))bl_next("dup3");
|
||||
if (oldfd != newfd && newfd >= 0) bl_fsync_fds_closed((unsigned int)newfd, (unsigned int)newfd);
|
||||
uint64_t *t = __atomic_load_n(&ns_fdt, __ATOMIC_ACQUIRE);
|
||||
if (t && oldfd != newfd && newfd >= 0 && newfd < NS_FDS && __atomic_load_n(&t[newfd], __ATOMIC_ACQUIRE)) ns_forget(newfd);
|
||||
@@ -1291,7 +1292,7 @@ static void ns_forget_range(unsigned int first, unsigned int last) {
|
||||
|
||||
int close_range(unsigned int first, unsigned int last, int flags) {
|
||||
static int (*real)(unsigned int, unsigned int, int);
|
||||
if (!real) real = (int (*)(unsigned int, unsigned int, int))dlsym(RTLD_NEXT, "close_range");
|
||||
if (!real) real = (int (*)(unsigned int, unsigned int, int))bl_next("close_range");
|
||||
if (!(flags & CLOSE_RANGE_CLOEXEC)) {
|
||||
bl_fsync_fds_closed(first, last);
|
||||
ns_forget_range(first, last);
|
||||
@@ -1305,7 +1306,7 @@ int close_range(unsigned int first, unsigned int last, int flags) {
|
||||
|
||||
void closefrom(int lowfd) {
|
||||
static void (*real)(int);
|
||||
if (!real) real = (void (*)(int))dlsym(RTLD_NEXT, "closefrom");
|
||||
if (!real) real = (void (*)(int))bl_next("closefrom");
|
||||
if (lowfd >= 0) {
|
||||
bl_fsync_fds_closed((unsigned int)lowfd, ~0u);
|
||||
ns_forget_range((unsigned int)lowfd, ~0u);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
* A process status is served without the container's tracer in it; see tracer.c.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
@@ -33,6 +32,8 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
int bl_status_without_tracer(const char *path, int flags) __attribute__((visibility("hidden")));
|
||||
int bl_writable_retry(int dirfd, const char *path, int flags) __attribute__((visibility("hidden")));
|
||||
int bl_ntsync_open(const char *path, int flags) __attribute__((visibility("hidden")));
|
||||
@@ -76,7 +77,7 @@ int open(const char *path, int flags, ...) {
|
||||
}
|
||||
if ((fd = bl_status_without_tracer(path, flags)) >= 0) return fd;
|
||||
if ((fd = bl_ntsync_open(path, flags)) != -2) return fd;
|
||||
if (real_open == NULL) real_open = dlsym(RTLD_NEXT, "open");
|
||||
if (real_open == NULL) real_open = bl_next("open");
|
||||
if (real_open == NULL) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@@ -102,7 +103,7 @@ int openat(int dirfd, const char *path, int flags, ...) {
|
||||
}
|
||||
if ((fd = bl_status_without_tracer(path, flags)) >= 0) return fd;
|
||||
if ((fd = bl_ntsync_open(path, flags)) != -2) return fd;
|
||||
if (real_openat == NULL) real_openat = dlsym(RTLD_NEXT, "openat");
|
||||
if (real_openat == NULL) real_openat = bl_next("openat");
|
||||
if (real_openat == NULL) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* BL_NO_PATH_CACHE=1 turns it off.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
@@ -24,6 +23,8 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
#define PATH_CACHE_SLOTS 256
|
||||
#define PATH_CACHE_KEY_MAX 512
|
||||
#define PATH_CACHE_TTL_NS 2000000000LL
|
||||
@@ -148,7 +149,7 @@ static int cached_access(const char *path, int mode, int flags, int (*check)(con
|
||||
static int (*next_faccessat(void))(int, const char *, int, int) {
|
||||
static int (*real)(int, const char *, int, int);
|
||||
|
||||
if (real == NULL) real = (int (*)(int, const char *, int, int))dlsym(RTLD_NEXT, "faccessat");
|
||||
if (real == NULL) real = (int (*)(int, const char *, int, int))bl_next("faccessat");
|
||||
return real;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* the link back finds the head without depending on libc's layout.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/futex.h>
|
||||
@@ -19,6 +18,8 @@
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
#ifndef SYS_openat2
|
||||
#define SYS_openat2 437
|
||||
#endif
|
||||
@@ -123,7 +124,7 @@ long syscall(long number, ...) {
|
||||
return handled;
|
||||
}
|
||||
if (!real_syscall) {
|
||||
real_syscall = (long (*)(long, ...)) dlsym(RTLD_NEXT, "syscall");
|
||||
real_syscall = (long (*)(long, ...)) bl_next("syscall");
|
||||
}
|
||||
if (number == SYS_openat2) {
|
||||
if (!__atomic_load_n(&openat2_missing, __ATOMIC_RELAXED)) {
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
* card. Every other path is passed through untouched.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
/* A library root, or its steamapps, resolved to the directory that holds the games. Returns the
|
||||
* rewritten path in `buf`, or NULL when `path` is not one of ours and should be left alone. */
|
||||
static const char *content_of(const char *path, char *buf, size_t len) {
|
||||
@@ -64,7 +65,7 @@ static const char *content_of(const char *path, char *buf, size_t len) {
|
||||
char buf[4096]; \
|
||||
const char *content; \
|
||||
if (!real) \
|
||||
real = dlsym(RTLD_NEXT, #name); \
|
||||
real = bl_next(#name); \
|
||||
if (!real) \
|
||||
return -1; \
|
||||
content = content_of(path, buf, sizeof(buf)); \
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* missing entry therefore does not prove the operation did not happen.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
@@ -22,6 +21,8 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
static int (*next_fstat)(int, struct stat *);
|
||||
static int (*next_fallocate)(int, int, off_t, off_t);
|
||||
static int (*next_posix_fallocate)(int, off_t, off_t);
|
||||
@@ -93,7 +94,7 @@ static int on_selected_device(int fd, const struct stat *known) {
|
||||
initialize();
|
||||
if (!enabled) return 0;
|
||||
if (known == NULL) {
|
||||
if (next_fstat == NULL) next_fstat = dlsym(RTLD_NEXT, "fstat");
|
||||
if (next_fstat == NULL) next_fstat = bl_next("fstat");
|
||||
if (next_fstat == NULL || next_fstat(fd, &local) != 0) return 0;
|
||||
known = &local;
|
||||
}
|
||||
@@ -181,7 +182,7 @@ static int cannot_reserve(long long device) {
|
||||
|
||||
static void note_cannot_reserve(int fd) {
|
||||
struct stat st;
|
||||
if (next_fstat == NULL) next_fstat = dlsym(RTLD_NEXT, "fstat");
|
||||
if (next_fstat == NULL) next_fstat = bl_next("fstat");
|
||||
if (next_fstat == NULL || next_fstat(fd, &st) != 0 || cannot_reserve((long long)st.st_dev)) return;
|
||||
unsigned int slot = __atomic_load_n(&unreserved_count, __ATOMIC_RELAXED);
|
||||
while (slot < 16 && !__atomic_compare_exchange_n(&unreserved_count, &slot, slot + 1, 0, __ATOMIC_ACQ_REL,
|
||||
@@ -192,7 +193,7 @@ static void note_cannot_reserve(int fd) {
|
||||
|
||||
int fallocate(int fd, int mode, off_t offset, off_t length) {
|
||||
int entry_errno = errno;
|
||||
if (next_fallocate == NULL) next_fallocate = dlsym(RTLD_NEXT, "fallocate");
|
||||
if (next_fallocate == NULL) next_fallocate = bl_next("fallocate");
|
||||
if (next_fallocate == NULL) { errno = ENOSYS; return -1; }
|
||||
int selected = on_selected_device(fd, NULL);
|
||||
errno = entry_errno;
|
||||
@@ -208,7 +209,7 @@ int fallocate(int fd, int mode, off_t offset, off_t length) {
|
||||
|
||||
int posix_fallocate(int fd, off_t offset, off_t length) {
|
||||
int entry_errno = errno;
|
||||
if (next_posix_fallocate == NULL) next_posix_fallocate = dlsym(RTLD_NEXT, "posix_fallocate");
|
||||
if (next_posix_fallocate == NULL) next_posix_fallocate = bl_next("posix_fallocate");
|
||||
if (next_posix_fallocate == NULL) { errno = entry_errno; return ENOSYS; }
|
||||
int selected = on_selected_device(fd, NULL);
|
||||
errno = entry_errno;
|
||||
@@ -229,7 +230,7 @@ int posix_fallocate(int fd, off_t offset, off_t length) {
|
||||
|
||||
ssize_t pwrite(int fd, const void *buffer, size_t count, off_t offset) {
|
||||
int entry_errno = errno;
|
||||
if (next_pwrite == NULL) next_pwrite = dlsym(RTLD_NEXT, "pwrite");
|
||||
if (next_pwrite == NULL) next_pwrite = bl_next("pwrite");
|
||||
if (next_pwrite == NULL) { errno = ENOSYS; return -1; }
|
||||
initialize();
|
||||
uint64_t sequence = enabled ? add(&pwrite_seen, 1) : 1;
|
||||
@@ -254,14 +255,14 @@ ssize_t pwrite(int fd, const void *buffer, size_t count, off_t offset) {
|
||||
|
||||
int ftruncate(int fd, off_t length) {
|
||||
int entry_errno = errno;
|
||||
if (next_ftruncate == NULL) next_ftruncate = dlsym(RTLD_NEXT, "ftruncate");
|
||||
if (next_ftruncate == NULL) next_ftruncate = bl_next("ftruncate");
|
||||
if (next_ftruncate == NULL) { errno = ENOSYS; return -1; }
|
||||
int selected = on_selected_device(fd, NULL);
|
||||
uint64_t start = selected ? now_ns() : 0;
|
||||
struct stat st;
|
||||
int known = 0;
|
||||
if (steam_client()) {
|
||||
if (next_fstat == NULL) next_fstat = dlsym(RTLD_NEXT, "fstat");
|
||||
if (next_fstat == NULL) next_fstat = bl_next("fstat");
|
||||
/* staging_file() reads /proc/self/fd, a round trip through proot: skip it for a shrink, which
|
||||
* is passed through untouched either way. */
|
||||
known = next_fstat != NULL && next_fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
|
||||
@@ -293,7 +294,7 @@ int ftruncate(int fd, off_t length) {
|
||||
|
||||
int fstat(int fd, struct stat *out) {
|
||||
int entry_errno = errno;
|
||||
if (next_fstat == NULL) next_fstat = dlsym(RTLD_NEXT, "fstat");
|
||||
if (next_fstat == NULL) next_fstat = bl_next("fstat");
|
||||
if (next_fstat == NULL) { errno = ENOSYS; return -1; }
|
||||
initialize();
|
||||
errno = entry_errno;
|
||||
@@ -314,7 +315,7 @@ int fstat(int fd, struct stat *out) {
|
||||
|
||||
int fsync(int fd) {
|
||||
int entry_errno = errno;
|
||||
if (next_fsync == NULL) next_fsync = dlsym(RTLD_NEXT, "fsync");
|
||||
if (next_fsync == NULL) next_fsync = bl_next("fsync");
|
||||
if (next_fsync == NULL) { errno = ENOSYS; return -1; }
|
||||
int selected = on_selected_device(fd, NULL);
|
||||
errno = entry_errno;
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
#define STANDIN_DIR "/tmp/droiddeck-tracefs"
|
||||
#define KGSL_TOTAL "/sys/class/kgsl/kgsl/page_alloc"
|
||||
|
||||
@@ -54,10 +56,10 @@ static const char gpu_mem_total_format[] =
|
||||
static struct tracefs_instance *standin;
|
||||
|
||||
struct tracefs_instance *tracefs_instance_create(const char *name) {
|
||||
struct tracefs_instance *(*real)(const char *) = dlsym(RTLD_NEXT, "tracefs_instance_create");
|
||||
struct tracefs_instance *(*real)(const char *) = bl_next("tracefs_instance_create");
|
||||
struct tracefs_instance *instance = real ? real(name) : NULL;
|
||||
if (instance || !name) return instance;
|
||||
struct tracefs_instance *(*alloc)(const char *, const char *) = dlsym(RTLD_NEXT, "tracefs_instance_alloc");
|
||||
struct tracefs_instance *(*alloc)(const char *, const char *) = bl_next("tracefs_instance_alloc");
|
||||
if (!alloc) return NULL;
|
||||
char path[PATH_MAX];
|
||||
mkdir(STANDIN_DIR, 0700);
|
||||
@@ -69,7 +71,7 @@ struct tracefs_instance *tracefs_instance_create(const char *name) {
|
||||
|
||||
struct tep_handle *tracefs_local_events(const char *tracing_dir) {
|
||||
if (!tracing_dir || strncmp(tracing_dir, STANDIN_DIR "/", sizeof(STANDIN_DIR))) {
|
||||
struct tep_handle *(*real)(const char *) = dlsym(RTLD_NEXT, "tracefs_local_events");
|
||||
struct tep_handle *(*real)(const char *) = bl_next("tracefs_local_events");
|
||||
return real ? real(tracing_dir) : NULL;
|
||||
}
|
||||
struct tep_handle *(*tep_alloc)(void) = dlsym(RTLD_DEFAULT, "tep_alloc");
|
||||
@@ -84,7 +86,7 @@ int tracefs_iterate_raw_events(struct tep_handle *tep, struct tracefs_instance *
|
||||
if (!standin || instance != standin) {
|
||||
int (*real)(struct tep_handle *, struct tracefs_instance *, void *, int,
|
||||
int (*)(struct tep_event *, struct tep_record *, int, void *), void *) =
|
||||
dlsym(RTLD_NEXT, "tracefs_iterate_raw_events");
|
||||
bl_next("tracefs_iterate_raw_events");
|
||||
return real ? real(tep, instance, cpus, cpu_size, callback, context) : -1;
|
||||
}
|
||||
struct tep_event *(*find)(struct tep_handle *, int) = dlsym(RTLD_DEFAULT, "tep_find_event");
|
||||
|
||||
@@ -34,14 +34,26 @@ static const char *skip_digits(const char *s) {
|
||||
return end == s ? NULL : end;
|
||||
}
|
||||
|
||||
/* /proc/{self,thread-self,<pid>}[/task/<tid>]/status */
|
||||
static int is_own(const char *digits, const char *end) {
|
||||
long id = 0;
|
||||
for (; digits < end; digits++) {
|
||||
if (id > 99999999) return 0;
|
||||
id = id * 10 + (*digits - '0');
|
||||
}
|
||||
return id == (long)getpid() || id == syscall(SYS_gettid);
|
||||
}
|
||||
|
||||
/* /proc/{self,thread-self,<own pid or tid>}[/task/<tid>]/status. A program asks whether it is
|
||||
* traced about itself; another process's status is read as it is. Chromium opens each renderer's
|
||||
* and hands it over as a read-only file, which it checks: a memfd is open for writing, so Chromium
|
||||
* and every Electron program aborted as its first page connected. */
|
||||
static int is_status_path(const char *path) {
|
||||
const char *rest;
|
||||
if (path == NULL || strncmp(path, "/proc/", 6) != 0) return 0;
|
||||
path += 6;
|
||||
if (strncmp(path, "self/", 5) == 0) rest = path + 4;
|
||||
else if (strncmp(path, "thread-self/", 12) == 0) rest = path + 11;
|
||||
else if ((rest = skip_digits(path)) == NULL) return 0;
|
||||
else if ((rest = skip_digits(path)) == NULL || !is_own(path, rest)) return 0;
|
||||
if (strncmp(rest, "/task/", 6) == 0 && (rest = skip_digits(rest + 6)) == NULL) return 0;
|
||||
return strcmp(rest, "/status") == 0;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
struct stand_in {
|
||||
int fd; /* the descriptor handed out, -1 when the slot is free */
|
||||
int peer; /* the other end, kept open */
|
||||
@@ -131,7 +133,7 @@ int socket(int domain, int type, int protocol) {
|
||||
static int (*real_socket)(int, int, int);
|
||||
int fd;
|
||||
|
||||
if (real_socket == NULL) real_socket = dlsym(RTLD_NEXT, "socket");
|
||||
if (real_socket == NULL) real_socket = bl_next("socket");
|
||||
if (real_socket == NULL) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@@ -168,7 +170,7 @@ int getsockname(int fd, struct sockaddr *addr, socklen_t *len) {
|
||||
*len = sizeof(nl);
|
||||
return 0;
|
||||
}
|
||||
if (real_getsockname == NULL) real_getsockname = dlsym(RTLD_NEXT, "getsockname");
|
||||
if (real_getsockname == NULL) real_getsockname = bl_next("getsockname");
|
||||
if (real_getsockname == NULL) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@@ -187,7 +189,7 @@ int setsockopt(int fd, int level, int optname, const void *optval, socklen_t opt
|
||||
if (level == SOL_SOCKET && (optname == SO_ATTACH_FILTER || optname == SO_DETACH_FILTER))
|
||||
return 0;
|
||||
}
|
||||
if (real_setsockopt == NULL) real_setsockopt = dlsym(RTLD_NEXT, "setsockopt");
|
||||
if (real_setsockopt == NULL) real_setsockopt = bl_next("setsockopt");
|
||||
if (real_setsockopt == NULL) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
* session, and any other property, is passed straight on.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((visibility("hidden"))) void *bl_next(const char *name);
|
||||
|
||||
/* Xlib's types, without needing its headers in the build image. */
|
||||
typedef struct _XDisplay Display;
|
||||
typedef unsigned long Atom;
|
||||
@@ -41,13 +42,13 @@ static int following(void) {
|
||||
int XChangeProperty(Display *dpy, Window w, Atom property, Atom type, int format, int mode,
|
||||
const unsigned char *data, int nelements) {
|
||||
static change_property_fn real;
|
||||
if (!real) real = (change_property_fn)dlsym(RTLD_NEXT, "XChangeProperty");
|
||||
if (!real) real = (change_property_fn)bl_next("XChangeProperty");
|
||||
if (!real) return 0;
|
||||
if (following() && format == 32 && nelements == 4 && data) {
|
||||
static intern_atom_fn intern;
|
||||
static Display *atom_dpy;
|
||||
static Atom mode_control;
|
||||
if (!intern) intern = (intern_atom_fn)dlsym(RTLD_NEXT, "XInternAtom");
|
||||
if (!intern) intern = (intern_atom_fn)bl_next("XInternAtom");
|
||||
/* Atoms belong to a connection; the client's is the one that writes this. */
|
||||
if (intern && atom_dpy != dpy) {
|
||||
mode_control = intern(dpy, "GAMESCOPE_XWAYLAND_MODE_CONTROL", 1);
|
||||
|
||||
@@ -61,7 +61,7 @@ class BinfmtPreloadTest(unittest.TestCase):
|
||||
cls.library = cls.tmp / "libbinfmt.so"
|
||||
subprocess.run(["gcc", "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror", "-DBINFMT_TEST",
|
||||
'-DBINFMT_HANDLER="%s"' % cls.handler, "-o", str(cls.library),
|
||||
"-pthread"] + [str(PRELOAD / name) for name in ("binfmt.c", "net.c", "udevmon.c", "pathcache.c")] + ["-ldl"],
|
||||
"-pthread"] + [str(PRELOAD / name) for name in ("binfmt.c", "net.c", "udevmon.c", "pathcache.c", "next.c")] + ["-ldl"],
|
||||
check=True)
|
||||
source = cls.tmp / "program.c"
|
||||
source.write_text(PROGRAM)
|
||||
|
||||
@@ -16,7 +16,7 @@ class CdpGuardTest(unittest.TestCase):
|
||||
def setUpClass(cls):
|
||||
cls.build = tempfile.TemporaryDirectory()
|
||||
cls.library = Path(cls.build.name) / "net.so"
|
||||
subprocess.run(["cc", "-shared", "-fPIC", "-O2", str(SOURCE), str(SOURCE.parent / "binfmt.c"), "-x", "c", "-", "-ldl",
|
||||
subprocess.run(["cc", "-shared", "-fPIC", "-O2", str(SOURCE), str(SOURCE.parent / "binfmt.c"), str(SOURCE.parent / "next.c"), "-x", "c", "-", "-ldl",
|
||||
"-o", str(cls.library)],
|
||||
input=b"int bl_udevmon_stand_in(int fd) { return 0; }", check=True, capture_output=True)
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ class LaunchTest(FexTestCase):
|
||||
self.assertNotIn("FEX_ROOTFS", guest)
|
||||
self.assertEqual(guest["FEX_TSOENABLED"], "1")
|
||||
self.assertTrue(guest["FEX_APP_CONFIG_LOCATION"].endswith("/config/"))
|
||||
self.assertEqual((guest["FEX_SILENTLOG"], guest["FEX_OUTPUTLOG"]), ("0", "stderr"))
|
||||
self.assertEqual((guest["FEX_SILENTLOG"], guest["FEX_OUTPUTLOG"]), ("0", ""))
|
||||
guest = FEX["guest_environment"]({"portable": False}, {"LD_PRELOAD": str(arm), "FEX_PORTABLE": "1", "FEX_OUTPUTLOG": "/tmp/fex.log"})
|
||||
self.assertEqual(guest["FEX_OUTPUTLOG"], "/tmp/fex.log")
|
||||
self.assertNotIn("LD_PRELOAD", guest)
|
||||
|
||||
@@ -319,7 +319,7 @@ class FsyncPreloadTest(unittest.TestCase):
|
||||
source = base / "fsync-test.c"
|
||||
source.write_text(PROGRAM)
|
||||
subprocess.run(["cc", "-O2", "-Wall", "-fPIC", "-shared", "-pthread", "-o", str(cls.lib),
|
||||
str(PRELOAD / "fsync.c"), str(PRELOAD / "robust.c"), str(PRELOAD / "noexec.c"), "-ldl"], check=True)
|
||||
str(PRELOAD / "fsync.c"), str(PRELOAD / "robust.c"), str(PRELOAD / "noexec.c"), str(PRELOAD / "next.c"), "-ldl"], check=True)
|
||||
subprocess.run(["cc", "-O2", "-Wall", "-pthread", "-o", str(cls.exe), str(source)], check=True)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
PRELOAD = Path(__file__).resolve().parents[1] / "linuxfs/preload"
|
||||
|
||||
PROBE = r"""
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
|
||||
void *bl_next(const char *name) __attribute__((visibility("hidden")));
|
||||
void *__real_dlsym(void *handle, const char *name);
|
||||
|
||||
/* Lookups the walk left to dlsym. */
|
||||
static int walking;
|
||||
static long passed_on;
|
||||
|
||||
void *__wrap_dlsym(void *handle, const char *name) {
|
||||
passed_on += walking;
|
||||
return __real_dlsym(handle, name);
|
||||
}
|
||||
|
||||
void *probe_walk(const char *name) {
|
||||
walking = 1;
|
||||
void *found = bl_next(name);
|
||||
walking = 0;
|
||||
return found;
|
||||
}
|
||||
|
||||
void *probe_dlsym(const char *name) { return dlsym(RTLD_NEXT, name); }
|
||||
long probe_passed_on(void) { return passed_on; }
|
||||
"""
|
||||
|
||||
# Counts the allocations a thread makes, as an allocator that is still starting up would see them.
|
||||
COUNT = r"""
|
||||
#include <stddef.h>
|
||||
|
||||
extern void *__libc_malloc(size_t);
|
||||
extern void *__libc_calloc(size_t, size_t);
|
||||
extern void *__libc_realloc(void *, size_t);
|
||||
static __thread long made;
|
||||
|
||||
void *malloc(size_t size) { made++; return __libc_malloc(size); }
|
||||
void *calloc(size_t count, size_t size) { made++; return __libc_calloc(count, size); }
|
||||
void *realloc(void *old, size_t size) { made++; return __libc_realloc(old, size); }
|
||||
void count_reset(void) { made = 0; }
|
||||
long count_get(void) { return made; }
|
||||
"""
|
||||
|
||||
PROGRAM = r"""
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void *probe_walk(const char *name);
|
||||
void *probe_dlsym(const char *name);
|
||||
long probe_passed_on(void);
|
||||
void count_reset(void);
|
||||
long count_get(void);
|
||||
|
||||
static void *first_lookup(void *made) {
|
||||
long before = probe_passed_on();
|
||||
count_reset();
|
||||
void *found = probe_walk("openat");
|
||||
((long *)made)[0] = found ? count_get() : -1;
|
||||
((long *)made)[1] = probe_passed_on() - before;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int differ = 0;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
void *walked = probe_walk(argv[i]), *found = probe_dlsym(argv[i]);
|
||||
if (walked != found) {
|
||||
printf("%s: %p, dlsym %p\n", argv[i], walked, found);
|
||||
differ = 1;
|
||||
}
|
||||
}
|
||||
long made[2] = {0, 0};
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, first_lookup, made);
|
||||
pthread_join(thread, NULL);
|
||||
printf("allocations %ld, left to dlsym %ld\n", made[0], made[1]);
|
||||
return differ;
|
||||
}
|
||||
"""
|
||||
|
||||
NAMES = ["open", "openat", "read", "recvmsg", "syscall", "close_range", "fstatat", "stat", "pthread_create",
|
||||
"dlopen", "malloc", "calloc", "memcpy", "strlen", "environ", "no_such_symbol"]
|
||||
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith("linux") and shutil.which("cc"), "needs Linux and a C compiler")
|
||||
class NextTest(unittest.TestCase):
|
||||
"""bl_next built as on glibc before 2.34, where the walk replaces dlsym(RTLD_NEXT)."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.build = tempfile.TemporaryDirectory()
|
||||
build = Path(cls.build.name)
|
||||
(build / "probe.c").write_text(PROBE)
|
||||
(build / "count.c").write_text(COUNT)
|
||||
(build / "program.c").write_text(PROGRAM)
|
||||
cls.count = build / "libcount.so"
|
||||
cls.program = build / "program"
|
||||
subprocess.run(["cc", "-shared", "-fPIC", "-O2", "-DNEXT_TEST", "-Wl,--wrap=dlsym", "-o", str(build / "libprobe.so"),
|
||||
str(build / "probe.c"), str(PRELOAD / "next.c"), "-ldl"], check=True)
|
||||
subprocess.run(["cc", "-shared", "-fPIC", "-O2", "-o", str(cls.count), str(build / "count.c")], check=True)
|
||||
subprocess.run(["cc", "-O2", "-pthread", "-o", str(cls.program), str(build / "program.c"),
|
||||
str(build / "libprobe.so"), str(cls.count), "-Wl,-rpath," + str(build)], check=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.build.cleanup()
|
||||
|
||||
def run_program(self):
|
||||
return subprocess.run([str(self.program)] + NAMES, capture_output=True, text=True)
|
||||
|
||||
def test_finds_what_dlsym_finds(self):
|
||||
result = self.run_program()
|
||||
self.assertEqual(0, result.returncode, result.stdout + result.stderr)
|
||||
|
||||
def test_a_threads_first_lookup_walks_and_allocates_nothing(self):
|
||||
self.assertIn("allocations 0, left to dlsym 0\n", self.run_program().stdout)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -43,7 +43,7 @@ class NoexecPreloadTest(unittest.TestCase):
|
||||
cls.tmp = Path(tempfile.mkdtemp())
|
||||
cls.lib = cls.tmp / "libnoexec.so"
|
||||
subprocess.run(["cc", "-O2", "-Wall", "-Werror", "-fPIC", "-shared", "-pthread", "-DNOEXEC_TEST", "-o", str(cls.lib),
|
||||
str(PRELOAD / "noexec.c"), str(PRELOAD / "fsync.c"), str(PRELOAD / "robust.c"), "-ldl"], check=True)
|
||||
str(PRELOAD / "noexec.c"), str(PRELOAD / "fsync.c"), str(PRELOAD / "robust.c"), str(PRELOAD / "next.c"), "-ldl"], check=True)
|
||||
source = cls.tmp / "program.c"
|
||||
source.write_text(PROGRAM)
|
||||
for name in ("FEX", "game"):
|
||||
|
||||
@@ -81,7 +81,7 @@ class StoragePreloadTest(unittest.TestCase):
|
||||
(build / "program.c").write_text(PROGRAM)
|
||||
cls.storage = build / "storage.so"
|
||||
cls.fake = build / "fake.so"
|
||||
subprocess.run(["cc", "-shared", "-fPIC", "-O2", "-o", str(cls.storage), str(PRELOAD / "storage.c"), "-ldl"], check=True)
|
||||
subprocess.run(["cc", "-shared", "-fPIC", "-O2", "-o", str(cls.storage), str(PRELOAD / "storage.c"), str(PRELOAD / "next.c"), "-ldl"], check=True)
|
||||
subprocess.run(["cc", "-shared", "-fPIC", "-O2", "-o", str(cls.fake), str(build / "fake.c")], check=True)
|
||||
cls.steam = cls.dir / "steamrtarm64/steam"
|
||||
cls.steam.parent.mkdir()
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
PRELOAD = Path(__file__).resolve().parents[1] / "linuxfs/preload"
|
||||
|
||||
# Each path opened read-only through the preload, from a thread other than the main one: how it was
|
||||
# served, and its TracerPid line.
|
||||
PROGRAM = r"""
|
||||
import fcntl, json, os, sys, threading
|
||||
seen = {}
|
||||
def open_all():
|
||||
for path in sys.argv[1:]:
|
||||
path = path.replace("PID", str(os.getpid())).replace("TID", str(threading.get_native_id()))
|
||||
fd = os.open(path, os.O_RDONLY)
|
||||
try:
|
||||
text = os.read(fd, 65536).decode()
|
||||
seen[path] = {"read_only": fcntl.fcntl(fd, fcntl.F_GETFL) & os.O_ACCMODE == os.O_RDONLY,
|
||||
"file": os.readlink("/proc/self/fd/%d" % fd),
|
||||
"tracer": next(line for line in text.splitlines() if line.startswith("TracerPid:"))}
|
||||
finally:
|
||||
os.close(fd)
|
||||
thread = threading.Thread(target=open_all)
|
||||
thread.start()
|
||||
thread.join()
|
||||
print(json.dumps(seen))
|
||||
"""
|
||||
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith("linux") and shutil.which("cc"), "needs Linux and a C compiler")
|
||||
class TracerTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.build = tempfile.TemporaryDirectory()
|
||||
cls.library = Path(cls.build.name) / "libblsession.so"
|
||||
subprocess.run(["cc", "-shared", "-fPIC", "-O2", "-pthread", "-o", str(cls.library),
|
||||
*map(str, sorted(PRELOAD.glob("*.c"))), "-ldl"], check=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.build.cleanup()
|
||||
|
||||
def opened(self, *paths):
|
||||
result = subprocess.run([sys.executable, "-c", PROGRAM, *paths], env=dict(os.environ, LD_PRELOAD=str(self.library)),
|
||||
check=True, capture_output=True, text=True)
|
||||
return json.loads(result.stdout)
|
||||
|
||||
def test_a_process_reads_its_own_status_without_the_tracer(self):
|
||||
for path, seen in self.opened("/proc/self/status", "/proc/thread-self/status", "/proc/PID/status",
|
||||
"/proc/TID/status", "/proc/PID/task/TID/status").items():
|
||||
self.assertEqual("TracerPid:\t0", seen["tracer"], path)
|
||||
self.assertTrue(seen["file"].startswith("/memfd:status"), path)
|
||||
|
||||
def test_another_process_status_is_the_real_read_only_file(self):
|
||||
# Chromium hands a renderer its status as a read-only file and checks that it is one.
|
||||
path = "/proc/%d/status" % os.getpid()
|
||||
seen = self.opened(path)[path]
|
||||
self.assertTrue(seen["read_only"])
|
||||
self.assertEqual(path, seen["file"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user