Skip to content

Instantly share code, notes, and snippets.

@ReimuNotMoe
Created May 10, 2026 19:36
Show Gist options
  • Select an option

  • Save ReimuNotMoe/70fd569cf5370c430b30ee2b1192a023 to your computer and use it in GitHub Desktop.

Select an option

Save ReimuNotMoe/70fd569cf5370c430b30ee2b1192a023 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
// Idea by ReimuNotMoe <reimu@sudomaker.com>
// Written by ChatGPT
// Usage:
// gcc -O2 -Wall -Wextra -fPIC -shared plasma-software-rendering.c -ldl -o plasma-software-rendering.so
// cp plasma-software-rendering.so /usr/local/lib/
// export LD_PRELOAD=/usr/local/lib/plasma-software-rendering.so
#define _GNU_SOURCE
#include <dlfcn.h>
#include <errno.h>
#include <limits.h>
#include <spawn.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifdef __linux__
#include <sys/auxv.h>
#ifndef AT_SECURE
#define AT_SECURE 23
#endif
#endif
#ifndef SOFTGL_DRIVER
#define SOFTGL_DRIVER "llvmpipe"
#endif
#ifndef SOFTGL_FRAME_LIMIT_FPS
#define SOFTGL_FRAME_LIMIT_FPS 60
#endif
#ifndef SOFTGL_FRAME_LIMIT_STALE_RESET_NS
#define SOFTGL_FRAME_LIMIT_STALE_RESET_NS 1000000000ULL
#endif
extern char **environ;
typedef struct _XDisplay Display;
typedef unsigned long GLXDrawable;
typedef unsigned char GLubyte;
typedef void (*__GLXextFuncPtr)(void);
typedef void *EGLDisplay;
typedef void *EGLSurface;
typedef int EGLint;
typedef unsigned int EGLBoolean;
typedef void (*__eglMustCastToProperFunctionPointerType)(void);
typedef EGLBoolean (*eglSwapBuffersWithDamage_fn)(EGLDisplay, EGLSurface, EGLint *, EGLint);
static char *(*real_getenv_fn)(const char *name);
static char *(*real_secure_getenv_fn)(const char *name);
static int (*real_execve_fn)(const char *, char *const[], char *const[]);
static int (*real_execvpe_fn)(const char *, char *const[], char *const[]);
static int (*real_posix_spawn_fn)(
pid_t *, const char *,
const posix_spawn_file_actions_t *,
const posix_spawnattr_t *,
char *const[], char *const[]
);
static int (*real_posix_spawnp_fn)(
pid_t *, const char *,
const posix_spawn_file_actions_t *,
const posix_spawnattr_t *,
char *const[], char *const[]
);
static void (*real_glXSwapBuffers_fn)(Display *, GLXDrawable);
static void (*real_glXSwapIntervalEXT_fn)(Display *, GLXDrawable, int);
static int (*real_glXSwapIntervalMESA_fn)(unsigned int);
static int (*real_glXSwapIntervalSGI_fn)(int);
static __GLXextFuncPtr (*real_glXGetProcAddressARB_fn)(const GLubyte *);
static __GLXextFuncPtr (*real_glXGetProcAddress_fn)(const GLubyte *);
static EGLBoolean (*real_eglSwapInterval_fn)(EGLDisplay, EGLint);
static EGLBoolean (*real_eglSwapBuffers_fn)(EGLDisplay, EGLSurface);
static eglSwapBuffersWithDamage_fn real_eglSwapBuffersWithDamageEXT_fn;
static eglSwapBuffersWithDamage_fn real_eglSwapBuffersWithDamageKHR_fn;
static __eglMustCastToProperFunctionPointerType (*real_eglGetProcAddress_fn)(const char *);
static __thread int resolving;
static int target_cache = -1;
static const char *self_path;
static const char *self_base;
struct env_block {
char **envp;
char *ld_preload_value;
int allocated;
};
static const char *base_name(const char *s) {
const char *p = s ? strrchr(s, '/') : NULL;
return p ? p + 1 : s;
}
static int is_target_name(const char *base) {
return base &&
(strcmp(base, "plasmashell") == 0 ||
strcmp(base, "kscreenlocker_greet") == 0);
}
static int child_is_target(const char *file, char *const argv[]) {
if (is_target_name(base_name(file)))
return 1;
if (argv && argv[0] && is_target_name(base_name(argv[0])))
return 1;
return 0;
}
#define RESOLVE(fn) do { \
if (!real_##fn##_fn && !resolving) { \
resolving = 1; \
real_##fn##_fn = dlsym(RTLD_NEXT, #fn); \
resolving = 0; \
} \
} while (0)
static char *real_getenv_call(const char *name) {
RESOLVE(getenv);
return real_getenv_fn ? real_getenv_fn(name) : NULL;
}
static char *real_secure_getenv_call(const char *name) {
RESOLVE(secure_getenv);
if (real_secure_getenv_fn)
return real_secure_getenv_fn(name);
return real_getenv_call(name);
}
static int is_secure_exec(void) {
#ifdef __linux__
errno = 0;
unsigned long v = getauxval(AT_SECURE);
if (errno == 0 && v != 0)
return 1;
#endif
return 0;
}
static int is_target_process(void) {
if (target_cache >= 0)
return target_cache;
char path[PATH_MAX];
ssize_t n = readlink("/proc/self/exe", path, sizeof(path) - 1);
if (n > 0) {
path[n] = '\0';
if (is_target_name(base_name(path))) {
target_cache = 1;
return 1;
}
}
#ifdef __GLIBC__
extern char *program_invocation_short_name;
if (is_target_name(program_invocation_short_name)) {
target_cache = 1;
return 1;
}
#endif
target_cache = 0;
return 0;
}
static char *fake_mesa_env(const char *name, int secure_variant) {
if (!name || !is_target_process())
return NULL;
if (secure_variant && is_secure_exec())
return NULL;
if (strcmp(name, "LIBGL_ALWAYS_SOFTWARE") == 0)
return (char *)"true";
if (strcmp(name, "GALLIUM_DRIVER") == 0)
return (char *)SOFTGL_DRIVER;
/* Qt Quick: avoid the threaded render loop running as fast as llvmpipe can render
when swap/present does not block. The basic loop is timer-driven. */
if (strcmp(name, "QSG_RENDER_LOOP") == 0)
return (char *)"basic";
return NULL;
}
char *getenv(const char *name) {
char *v = fake_mesa_env(name, 0);
return v ? v : real_getenv_call(name);
}
char *secure_getenv(const char *name) {
char *v = fake_mesa_env(name, 1);
return v ? v : real_secure_getenv_call(name);
}
char *__secure_getenv(const char *name) {
return secure_getenv(name);
}
static int target_should_force_vsync(void) {
return is_target_process();
}
static uint64_t monotonic_ns(void) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
return 0;
return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
}
static void sleep_until_ns(uint64_t deadline_ns) {
struct timespec ts;
ts.tv_sec = (time_t)(deadline_ns / 1000000000ULL);
ts.tv_nsec = (long)(deadline_ns % 1000000000ULL);
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) != 0) {
if (errno != EINTR)
break;
}
}
static void throttle_target_frame(void) {
#if SOFTGL_FRAME_LIMIT_FPS > 0
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static uint64_t next_frame_ns;
const uint64_t interval_ns = 1000000000ULL / (uint64_t)SOFTGL_FRAME_LIMIT_FPS;
uint64_t now;
uint64_t deadline;
if (!target_should_force_vsync() || interval_ns == 0)
return;
now = monotonic_ns();
if (now == 0)
return;
pthread_mutex_lock(&lock);
if (next_frame_ns == 0 || next_frame_ns + SOFTGL_FRAME_LIMIT_STALE_RESET_NS < now)
next_frame_ns = now;
if (now > next_frame_ns)
next_frame_ns = now;
deadline = next_frame_ns;
next_frame_ns = deadline + interval_ns;
pthread_mutex_unlock(&lock);
if (deadline > now)
sleep_until_ns(deadline);
#else
(void)target_should_force_vsync;
#endif
}
static int procname_eq(const GLubyte *name, const char *s) {
return name && strcmp((const char *)name, s) == 0;
}
static void *resolve_default_next(const char *name) {
void *p = NULL;
if (!resolving) {
resolving = 1;
p = dlsym(RTLD_NEXT, name);
resolving = 0;
}
return p;
}
static void resolve_glx_getproc(void) {
if (!real_glXGetProcAddressARB_fn)
real_glXGetProcAddressARB_fn = resolve_default_next("glXGetProcAddressARB");
if (!real_glXGetProcAddress_fn)
real_glXGetProcAddress_fn = resolve_default_next("glXGetProcAddress");
}
static __GLXextFuncPtr real_glx_getproc_any(const char *name) {
resolve_glx_getproc();
if (real_glXGetProcAddressARB_fn) {
__GLXextFuncPtr p = real_glXGetProcAddressARB_fn((const GLubyte *)name);
if (p)
return p;
}
if (real_glXGetProcAddress_fn) {
__GLXextFuncPtr p = real_glXGetProcAddress_fn((const GLubyte *)name);
if (p)
return p;
}
return NULL;
}
static void resolve_glx_swap_interval(void) {
if (!real_glXSwapIntervalEXT_fn)
real_glXSwapIntervalEXT_fn = resolve_default_next("glXSwapIntervalEXT");
if (!real_glXSwapIntervalMESA_fn)
real_glXSwapIntervalMESA_fn = resolve_default_next("glXSwapIntervalMESA");
if (!real_glXSwapIntervalSGI_fn)
real_glXSwapIntervalSGI_fn = resolve_default_next("glXSwapIntervalSGI");
if (!real_glXSwapIntervalEXT_fn)
real_glXSwapIntervalEXT_fn = (void (*)(Display *, GLXDrawable, int))real_glx_getproc_any("glXSwapIntervalEXT");
if (!real_glXSwapIntervalMESA_fn)
real_glXSwapIntervalMESA_fn = (int (*)(unsigned int))real_glx_getproc_any("glXSwapIntervalMESA");
if (!real_glXSwapIntervalSGI_fn)
real_glXSwapIntervalSGI_fn = (int (*)(int))real_glx_getproc_any("glXSwapIntervalSGI");
}
static void force_glx_vsync(Display *dpy, GLXDrawable drawable) {
if (!target_should_force_vsync())
return;
resolve_glx_swap_interval();
if (real_glXSwapIntervalEXT_fn) {
real_glXSwapIntervalEXT_fn(dpy, drawable, 1);
return;
}
if (real_glXSwapIntervalMESA_fn) {
(void)real_glXSwapIntervalMESA_fn(1);
return;
}
if (real_glXSwapIntervalSGI_fn)
(void)real_glXSwapIntervalSGI_fn(1);
}
void glXSwapBuffers(Display *dpy, GLXDrawable drawable) {
if (!real_glXSwapBuffers_fn)
real_glXSwapBuffers_fn = resolve_default_next("glXSwapBuffers");
force_glx_vsync(dpy, drawable);
throttle_target_frame();
if (real_glXSwapBuffers_fn)
real_glXSwapBuffers_fn(dpy, drawable);
}
void glXSwapIntervalEXT(Display *dpy, GLXDrawable drawable, int interval) {
(void)interval;
resolve_glx_swap_interval();
if (real_glXSwapIntervalEXT_fn)
real_glXSwapIntervalEXT_fn(dpy, drawable, target_should_force_vsync() ? 1 : interval);
}
int glXSwapIntervalMESA(unsigned int interval) {
resolve_glx_swap_interval();
if (real_glXSwapIntervalMESA_fn)
return real_glXSwapIntervalMESA_fn(target_should_force_vsync() ? 1U : interval);
return 0;
}
int glXSwapIntervalSGI(int interval) {
resolve_glx_swap_interval();
if (real_glXSwapIntervalSGI_fn)
return real_glXSwapIntervalSGI_fn(target_should_force_vsync() ? 1 : interval);
return 0;
}
__GLXextFuncPtr glXGetProcAddressARB(const GLubyte *procName) {
if (target_should_force_vsync()) {
if (procname_eq(procName, "glXSwapIntervalEXT"))
return (__GLXextFuncPtr)glXSwapIntervalEXT;
if (procname_eq(procName, "glXSwapIntervalMESA"))
return (__GLXextFuncPtr)glXSwapIntervalMESA;
if (procname_eq(procName, "glXSwapIntervalSGI"))
return (__GLXextFuncPtr)glXSwapIntervalSGI;
if (procname_eq(procName, "glXSwapBuffers"))
return (__GLXextFuncPtr)glXSwapBuffers;
}
resolve_glx_getproc();
return real_glXGetProcAddressARB_fn ? real_glXGetProcAddressARB_fn(procName) : NULL;
}
__GLXextFuncPtr glXGetProcAddress(const GLubyte *procName) {
if (target_should_force_vsync()) {
if (procname_eq(procName, "glXSwapIntervalEXT"))
return (__GLXextFuncPtr)glXSwapIntervalEXT;
if (procname_eq(procName, "glXSwapIntervalMESA"))
return (__GLXextFuncPtr)glXSwapIntervalMESA;
if (procname_eq(procName, "glXSwapIntervalSGI"))
return (__GLXextFuncPtr)glXSwapIntervalSGI;
if (procname_eq(procName, "glXSwapBuffers"))
return (__GLXextFuncPtr)glXSwapBuffers;
}
resolve_glx_getproc();
return real_glXGetProcAddress_fn ? real_glXGetProcAddress_fn(procName) : NULL;
}
static void resolve_egl_getproc(void) {
if (!real_eglGetProcAddress_fn)
real_eglGetProcAddress_fn = resolve_default_next("eglGetProcAddress");
}
static void resolve_egl_swap(void) {
if (!real_eglSwapInterval_fn)
real_eglSwapInterval_fn = resolve_default_next("eglSwapInterval");
if (!real_eglSwapBuffers_fn)
real_eglSwapBuffers_fn = resolve_default_next("eglSwapBuffers");
if (!real_eglSwapBuffersWithDamageEXT_fn)
real_eglSwapBuffersWithDamageEXT_fn = (eglSwapBuffersWithDamage_fn)resolve_default_next("eglSwapBuffersWithDamageEXT");
if (!real_eglSwapBuffersWithDamageKHR_fn)
real_eglSwapBuffersWithDamageKHR_fn = (eglSwapBuffersWithDamage_fn)resolve_default_next("eglSwapBuffersWithDamageKHR");
resolve_egl_getproc();
if (!real_eglSwapInterval_fn && real_eglGetProcAddress_fn)
real_eglSwapInterval_fn = (EGLBoolean (*)(EGLDisplay, EGLint))real_eglGetProcAddress_fn("eglSwapInterval");
if (!real_eglSwapBuffers_fn && real_eglGetProcAddress_fn)
real_eglSwapBuffers_fn = (EGLBoolean (*)(EGLDisplay, EGLSurface))real_eglGetProcAddress_fn("eglSwapBuffers");
if (!real_eglSwapBuffersWithDamageEXT_fn && real_eglGetProcAddress_fn)
real_eglSwapBuffersWithDamageEXT_fn = (eglSwapBuffersWithDamage_fn)real_eglGetProcAddress_fn("eglSwapBuffersWithDamageEXT");
if (!real_eglSwapBuffersWithDamageKHR_fn && real_eglGetProcAddress_fn)
real_eglSwapBuffersWithDamageKHR_fn = (eglSwapBuffersWithDamage_fn)real_eglGetProcAddress_fn("eglSwapBuffersWithDamageKHR");
}
static void force_egl_vsync(EGLDisplay dpy) {
if (!target_should_force_vsync())
return;
resolve_egl_swap();
if (real_eglSwapInterval_fn)
(void)real_eglSwapInterval_fn(dpy, 1);
}
EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) {
resolve_egl_swap();
if (real_eglSwapInterval_fn)
return real_eglSwapInterval_fn(dpy, target_should_force_vsync() ? 1 : interval);
return 1;
}
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
resolve_egl_swap();
force_egl_vsync(dpy);
throttle_target_frame();
if (real_eglSwapBuffers_fn)
return real_eglSwapBuffers_fn(dpy, surface);
return 0;
}
EGLBoolean eglSwapBuffersWithDamageEXT(
EGLDisplay dpy,
EGLSurface surface,
EGLint *rects,
EGLint n_rects
) {
resolve_egl_swap();
force_egl_vsync(dpy);
throttle_target_frame();
if (real_eglSwapBuffersWithDamageEXT_fn)
return real_eglSwapBuffersWithDamageEXT_fn(dpy, surface, rects, n_rects);
if (real_eglSwapBuffers_fn)
return real_eglSwapBuffers_fn(dpy, surface);
return 0;
}
EGLBoolean eglSwapBuffersWithDamageKHR(
EGLDisplay dpy,
EGLSurface surface,
EGLint *rects,
EGLint n_rects
) {
resolve_egl_swap();
force_egl_vsync(dpy);
throttle_target_frame();
if (real_eglSwapBuffersWithDamageKHR_fn)
return real_eglSwapBuffersWithDamageKHR_fn(dpy, surface, rects, n_rects);
if (real_eglSwapBuffers_fn)
return real_eglSwapBuffers_fn(dpy, surface);
return 0;
}
__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) {
if (target_should_force_vsync()) {
if (procname && strcmp(procname, "eglSwapInterval") == 0)
return (__eglMustCastToProperFunctionPointerType)eglSwapInterval;
if (procname && strcmp(procname, "eglSwapBuffers") == 0)
return (__eglMustCastToProperFunctionPointerType)eglSwapBuffers;
if (procname && strcmp(procname, "eglSwapBuffersWithDamageEXT") == 0)
return (__eglMustCastToProperFunctionPointerType)eglSwapBuffersWithDamageEXT;
if (procname && strcmp(procname, "eglSwapBuffersWithDamageKHR") == 0)
return (__eglMustCastToProperFunctionPointerType)eglSwapBuffersWithDamageKHR;
}
resolve_egl_getproc();
return real_eglGetProcAddress_fn ? real_eglGetProcAddress_fn(procname) : NULL;
}
static int token_is_our_so(const char *tok) {
if (!tok || !*tok || !self_base)
return 0;
if (self_path && strcmp(tok, self_path) == 0)
return 1;
return strcmp(base_name(tok), self_base) == 0;
}
static char *strip_own_from_ld_preload(const char *old, int *changed) {
*changed = 0;
if (!old || !*old)
return NULL;
char *copy = strdup(old);
char *out = malloc(strlen(old) + 1);
if (!copy || !out) {
free(copy);
free(out);
errno = ENOMEM;
return NULL;
}
out[0] = '\0';
for (char *p = copy; *p;) {
while (*p == ':' || *p == ' ' || *p == '\t')
++p;
if (!*p)
break;
char *start = p;
while (*p && *p != ':' && *p != ' ' && *p != '\t')
++p;
char saved = *p;
*p = '\0';
if (token_is_our_so(start)) {
*changed = 1;
} else {
if (out[0])
strcat(out, ":");
strcat(out, start);
}
if (!saved)
break;
++p;
}
free(copy);
if (!*changed || out[0])
return out;
free(out);
return NULL;
}
static int make_sanitized_env(
char *const in_envp[],
int keep_preload,
struct env_block *out
) {
out->envp = (char **)in_envp;
out->ld_preload_value = NULL;
out->allocated = 0;
if (keep_preload)
return 0;
char *const *src = in_envp ? in_envp : environ;
size_t n = 0;
int idx = -1;
for (; src[n]; ++n) {
if (strncmp(src[n], "LD_PRELOAD=", 11) == 0)
idx = (int)n;
}
if (idx < 0)
return 0;
int changed = 0;
char *new_ld = strip_own_from_ld_preload(src[idx] + 11, &changed);
if (!changed) {
free(new_ld);
return 0;
}
char **dst = calloc(n + 1, sizeof(char *));
if (!dst) {
free(new_ld);
errno = ENOMEM;
return -1;
}
size_t j = 0;
for (size_t i = 0; i < n; ++i) {
if ((int)i == idx) {
if (new_ld && *new_ld) {
size_t len = strlen(new_ld) + 12;
out->ld_preload_value = malloc(len);
if (!out->ld_preload_value) {
free(dst);
free(new_ld);
errno = ENOMEM;
return -1;
}
snprintf(out->ld_preload_value, len, "LD_PRELOAD=%s", new_ld);
dst[j++] = out->ld_preload_value;
}
} else {
dst[j++] = (char *)src[i];
}
}
dst[j] = NULL;
free(new_ld);
out->envp = dst;
out->allocated = 1;
return 0;
}
static void free_env_block(struct env_block *b) {
if (b->allocated)
free(b->envp);
free(b->ld_preload_value);
}
static void strip_current_env_if_target(void) {
if (!is_target_process())
return;
const char *old = real_getenv_call("LD_PRELOAD");
int changed = 0;
char *new_ld = strip_own_from_ld_preload(old, &changed);
if (changed) {
if (new_ld && *new_ld)
setenv("LD_PRELOAD", new_ld, 1);
else
unsetenv("LD_PRELOAD");
}
free(new_ld);
}
int execve(const char *path, char *const argv[], char *const envp[]) {
RESOLVE(execve);
if (!real_execve_fn) {
errno = ENOSYS;
return -1;
}
struct env_block b;
if (make_sanitized_env(envp, child_is_target(path, argv), &b) < 0)
return -1;
int r = real_execve_fn(path, argv, b.envp);
int saved = errno;
free_env_block(&b);
errno = saved;
return r;
}
int execv(const char *path, char *const argv[]) {
return execve(path, argv, environ);
}
int execvpe(const char *file, char *const argv[], char *const envp[]) {
RESOLVE(execvpe);
if (!real_execvpe_fn) {
errno = ENOSYS;
return -1;
}
struct env_block b;
if (make_sanitized_env(envp, child_is_target(file, argv), &b) < 0)
return -1;
int r = real_execvpe_fn(file, argv, b.envp);
int saved = errno;
free_env_block(&b);
errno = saved;
return r;
}
int execvp(const char *file, char *const argv[]) {
return execvpe(file, argv, environ);
}
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[]
) {
RESOLVE(posix_spawn);
if (!real_posix_spawn_fn)
return ENOSYS;
struct env_block b;
if (make_sanitized_env(envp, child_is_target(path, argv), &b) < 0)
return errno;
int r = real_posix_spawn_fn(pid, path, file_actions, attrp, argv, b.envp);
free_env_block(&b);
return r;
}
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[]
) {
RESOLVE(posix_spawnp);
if (!real_posix_spawnp_fn)
return ENOSYS;
struct env_block b;
if (make_sanitized_env(envp, child_is_target(file, argv), &b) < 0)
return errno;
int r = real_posix_spawnp_fn(pid, file, file_actions, attrp, argv, b.envp);
free_env_block(&b);
return r;
}
__attribute__((constructor))
static void softgl_preload_init(void) {
Dl_info di;
if (dladdr((void *)&softgl_preload_init, &di) && di.dli_fname) {
self_path = di.dli_fname;
self_base = base_name(di.dli_fname);
}
strip_current_env_if_target();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment