mirror of
https://github.com/yuzu-emu/AppImageKit-checkrt.git
synced 2024-12-12 21:04:21 +01:00
load exec() wrapper if optional runtimes are present
This commit is contained in:
parent
2b645a7f55
commit
bce42d0560
@ -1,19 +1,34 @@
|
||||
https://github.com/AppImage/AppImageKit/blob/ddcb5b98073ec251cbddda7a4eaa17545f06bbff/AppRun.c
|
||||
|
||||
--- a/AppRun.c
|
||||
+++ b/AppRun.c
|
||||
@@ -156,6 +156,7 @@
|
||||
@@ -164,6 +164,10 @@
|
||||
char *old_env;
|
||||
const int length = 2047;
|
||||
char new_env[8][length+1];
|
||||
size_t length;
|
||||
const char *format;
|
||||
+ checkrt(usr_in_appdir);
|
||||
+
|
||||
+ if (optional_ld_preload)
|
||||
+ putenv(optional_ld_preload);
|
||||
|
||||
/* https://docs.python.org/2/using/cmdline.html#envvar-PYTHONHOME */
|
||||
snprintf(new_env[0], length, "PYTHONHOME=%s/usr/", appdir);
|
||||
@@ -164,7 +165,7 @@
|
||||
snprintf(new_env[1], length, "PATH=%s/usr/bin/:%s/usr/sbin/:%s/usr/games/:%s/bin/:%s/sbin/:%s", appdir, appdir, appdir, appdir, appdir, old_env);
|
||||
SET_NEW_ENV(new_pythonhome, appdir_s, "PYTHONHOME=%s/usr/", appdir);
|
||||
@@ -172,7 +176,7 @@
|
||||
SET_NEW_ENV(new_path, appdir_s*5 + strlen(old_env), "PATH=%s/usr/bin/:%s/usr/sbin/:%s/usr/games/:%s/bin/:%s/sbin/:%s", appdir, appdir, appdir, appdir, appdir, old_env);
|
||||
|
||||
old_env = getenv("LD_LIBRARY_PATH") ?: "";
|
||||
- snprintf(new_env[2], length, "LD_LIBRARY_PATH=%s/usr/lib/:%s/usr/lib/i386-linux-gnu/:%s/usr/lib/x86_64-linux-gnu/:%s/usr/lib32/:%s/usr/lib64/:%s/lib/:%s/lib/i386-linux-gnu/:%s/lib/x86_64-linux-gnu/:%s/lib32/:%s/lib64/:%s", appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, old_env);
|
||||
+ snprintf(new_env[2], length, "LD_LIBRARY_PATH=%s%s/usr/lib/:%s/usr/lib/i386-linux-gnu/:%s/usr/lib/x86_64-linux-gnu/:%s/usr/lib32/:%s/usr/lib64/:%s/lib/:%s/lib/i386-linux-gnu/:%s/lib/x86_64-linux-gnu/:%s/lib32/:%s/lib64/:%s", optional, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, old_env);
|
||||
- SET_NEW_ENV(new_ld_library_path, appdir_s*10 + strlen(old_env), "LD_LIBRARY_PATH=%s/usr/lib/:%s/usr/lib/i386-linux-gnu/:%s/usr/lib/x86_64-linux-gnu/:%s/usr/lib32/:%s/usr/lib64/:%s/lib/:%s/lib/i386-linux-gnu/:%s/lib/x86_64-linux-gnu/:%s/lib32/:%s/lib64/:%s", appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, old_env);
|
||||
+ SET_NEW_ENV(new_ld_library_path, appdir_s*10 + strlen(old_env), "LD_LIBRARY_PATH=%s%s/usr/lib/:%s/usr/lib/i386-linux-gnu/:%s/usr/lib/x86_64-linux-gnu/:%s/usr/lib32/:%s/usr/lib64/:%s/lib/:%s/lib/i386-linux-gnu/:%s/lib/x86_64-linux-gnu/:%s/lib32/:%s/lib64/:%s", optional, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, old_env);
|
||||
|
||||
old_env = getenv("PYTHONPATH") ?: "";
|
||||
snprintf(new_env[3], length, "PYTHONPATH=%s/usr/share/pyshared/:%s", appdir, old_env);
|
||||
SET_NEW_ENV(new_pythonpath, appdir_s + strlen(old_env), "PYTHONPATH=%s/usr/share/pyshared/:%s", appdir, old_env);
|
||||
@@ -201,6 +205,9 @@
|
||||
if (ret == -1)
|
||||
die("Error executing '%s': %s\n", exe, strerror(error));
|
||||
|
||||
+ free(optional);
|
||||
+ if (optional_ld_preload)
|
||||
+ free(optional_ld_preload);
|
||||
free(line);
|
||||
free(desktop_file);
|
||||
free(usr_in_appdir);
|
||||
|
12
Makefile
12
Makefile
@ -1,16 +1,22 @@
|
||||
CFLAGS ?= -O2 -Wall -Wextra
|
||||
CFLAGS += -include checkrt.h
|
||||
LDFLAGS += -s
|
||||
BIN = AppRun_patched
|
||||
LIB = exec.so
|
||||
|
||||
|
||||
all: $(BIN)
|
||||
all: $(BIN) $(LIB)
|
||||
|
||||
clean:
|
||||
-rm -f $(BIN) *.o AppRun.c AppRun_patched.c
|
||||
-rm -f $(BIN) $(LIB) *.o AppRun.c AppRun_patched.c
|
||||
|
||||
$(BIN): AppRun_patched.o checkrt.o
|
||||
|
||||
$(LIB): exec.o
|
||||
$(CC) -shared $(LDFLAGS) -o $@ $^ -ldl
|
||||
|
||||
AppRun_patched.o checkrt.o: CFLAGS += -include checkrt.h
|
||||
exec.o: CFLAGS += -fPIC
|
||||
|
||||
AppRun_patched.c: AppRun.c
|
||||
patch -p1 --output $@ < AppRun.c.patch
|
||||
|
||||
|
@ -15,5 +15,5 @@ That's because both libraries are part of GCC.
|
||||
You would have to know the library version of the host system and decide whether to use a bundled library or not before the
|
||||
application is started. This is exactly what the patched AppRun binary does.
|
||||
It will search for `usr/optional/libstdc++/libstdc++.so.6` and `usr/optional/libgcc_s/libgcc_s.so.1` inside the AppImage or AppDir.
|
||||
If found it will compare their internal versions with the ones found on the system and prepend their paths to `LD_LIBRARY_PATH`
|
||||
if necessary.
|
||||
If found it will compare their internal versions with the ones found on the system and prepend their paths to `LD_LIBRARY_PATH` if necessary.
|
||||
You should also put `exec.so` into `usr/optional`.
|
||||
|
18
checkrt.c
18
checkrt.c
@ -41,10 +41,12 @@
|
||||
ret = fscanf(f, "%s", sym); (void)ret; \
|
||||
pclose(f);
|
||||
|
||||
#define CXXDIR "optional/libstdc++"
|
||||
#define GCCDIR "optional/libgcc"
|
||||
#define CXXDIR "optional/libstdc++"
|
||||
#define GCCDIR "optional/libgcc"
|
||||
#define EXEC_SO "optional/exec.so"
|
||||
|
||||
char optional[1024];
|
||||
char *optional = NULL;
|
||||
char *optional_ld_preload = NULL;
|
||||
|
||||
void checkrt(char *usr_in_appdir)
|
||||
{
|
||||
@ -92,6 +94,7 @@ void checkrt(char *usr_in_appdir)
|
||||
|
||||
int bundle_cxx = 0;
|
||||
int bundle_gcc = 0;
|
||||
size_t len = strlen(usr_in_appdir);
|
||||
|
||||
if (stdcxx_bundle_ver > stdcxx_sys_ver)
|
||||
bundle_cxx = 1;
|
||||
@ -99,13 +102,22 @@ void checkrt(char *usr_in_appdir)
|
||||
if (gcc_bundle_ver > gcc_sys_ver)
|
||||
bundle_gcc = 1;
|
||||
|
||||
if (bundle_cxx == 1 || bundle_gcc == 1) {
|
||||
optional_ld_preload = malloc(strlen("LD_PRELOAD=") + strlen(EXEC_SO) + 1 + len);
|
||||
sprintf(optional_ld_preload, "LD_PRELOAD=%s/" EXEC_SO, usr_in_appdir);
|
||||
}
|
||||
|
||||
if (bundle_cxx == 1 && bundle_gcc == 0) {
|
||||
optional = malloc(strlen(CXXDIR) + 2 + len);
|
||||
sprintf(optional, "%s/" CXXDIR ":", usr_in_appdir);
|
||||
} else if (bundle_cxx == 0 && bundle_gcc == 1) {
|
||||
optional = malloc(strlen(GCCDIR) + 2 + len);
|
||||
sprintf(optional, "%s/" GCCDIR ":", usr_in_appdir);
|
||||
} else if (bundle_cxx == 1 && bundle_gcc == 1) {
|
||||
optional = malloc(strlen(GCCDIR) + strlen(CXXDIR) + 4 + len*2);
|
||||
sprintf(optional, "%s/" GCCDIR ":%s/" CXXDIR ":", usr_in_appdir, usr_in_appdir);
|
||||
} else {
|
||||
optional = malloc(2);
|
||||
sprintf(optional, "%s", "");
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
extern char optional[1024];
|
||||
extern char *optional;
|
||||
extern char *optional_ld_preload;
|
||||
extern void checkrt(char *usr_in_appdir);
|
||||
|
315
exec.c
Normal file
315
exec.c
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Sven Brauch <mail@svenbrauch.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
This library is intended to be used together with the AppImage distribution mechanism.
|
||||
Place the library somewhere in your AppImage and point LD_PRELOAD to it
|
||||
before launching your application.
|
||||
|
||||
Whenever your application invokes a child process through execv() or execve(),
|
||||
this wrapper will intercept the call and see if the child process lies
|
||||
outside of the bundled appdir. If it does, the wrapper will attempt to undo
|
||||
any changes done to environment variables before launching the process,
|
||||
since you probably did not intend to launch it with e.g. the LD_LIBRARY_PATH
|
||||
you previously set for your application.
|
||||
|
||||
To perform this operation, you have to set the following environment variables:
|
||||
$APPDIR -- path of the AppDir you are launching your application from. If this
|
||||
is not present, the wrapper will do nothing.
|
||||
|
||||
For each environment variable you want restored, where {VAR} is the name of the environment
|
||||
variable (e.g. "PATH"):
|
||||
$APPIMAGE_ORIGINAL_{VAR} -- original value of the environment variable
|
||||
$APPIMAGE_STARTUP_{VAR} -- value of the variable when you were starting up
|
||||
your application
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef ssize_t (*execve_func_t)(const char* filename, char* const argv[], char* const envp[]);
|
||||
static execve_func_t old_execve = NULL;
|
||||
|
||||
typedef ssize_t (*execvp_func_t)(const char* filename, char* const argv[]);
|
||||
//static execvp_func_t old_execvp = NULL;
|
||||
|
||||
// TODO implement me: execl, execlp, execle; but it's annoying work and nothing seems to use them
|
||||
// typedef int (*execl_func_t)(const char *path, const char *arg);
|
||||
// static execl_func_t old_execl = NULL;
|
||||
//
|
||||
// typedef int (*execlp_func_t)(const char *file, const char *arg);
|
||||
// static execlp_func_t old_execlp = NULL;
|
||||
//
|
||||
// typedef int (*execle_func_t)(const char *path, const char *arg, char * const envp[]);
|
||||
// static execle_func_t old_execle = NULL;
|
||||
|
||||
typedef int (*execv_func_t)(const char *path, char *const argv[]);
|
||||
//static execv_func_t old_execv = NULL;
|
||||
|
||||
typedef int (*execvpe_func_t)(const char *file, char *const argv[], char *const envp[]);
|
||||
static execvpe_func_t old_execvpe = NULL;
|
||||
|
||||
char* APPIMAGE_ORIG_PREFIX = "APPIMAGE_ORIGINAL_";
|
||||
char* APPIMAGE_STARTUP_PREFIX = "APPIMAGE_STARTUP_";
|
||||
char* APPDIR = "APPDIR";
|
||||
|
||||
typedef struct {
|
||||
char** names;
|
||||
char** values;
|
||||
} environment;
|
||||
|
||||
environment environment_alloc(size_t envc) {
|
||||
environment env;
|
||||
env.names = calloc(envc+1, sizeof(char*));
|
||||
env.values = calloc(envc+1, sizeof(char*));
|
||||
return env;
|
||||
}
|
||||
|
||||
int arr_len(char* const x[]) {
|
||||
int len = 0;
|
||||
while ( x[len] != 0 ) {
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void stringlist_free(char* const envp[]) {
|
||||
if ( envp ) {
|
||||
for ( int i = 0; i < arr_len(envp); i++ ) {
|
||||
free(envp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char** stringlist_alloc(int size) {
|
||||
char** ret = calloc(size, sizeof(char*));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int environment_len(const environment env) {
|
||||
return arr_len(env.names);
|
||||
}
|
||||
|
||||
void environment_free(environment env) {
|
||||
stringlist_free(env.names);
|
||||
stringlist_free(env.values);
|
||||
}
|
||||
|
||||
void environment_append_item(environment env, char* name, int name_size, char* val, int val_size) {
|
||||
int count = environment_len(env);
|
||||
env.names[count] = calloc(name_size+1, sizeof(char));
|
||||
env.values[count] = calloc(val_size+1, sizeof(char));
|
||||
strncpy(env.names[count], name, name_size);
|
||||
strncpy(env.values[count], val, val_size);
|
||||
}
|
||||
|
||||
int environment_find_name(environment env, char* name, int name_size) {
|
||||
int count = environment_len(env);
|
||||
for ( int i = 0; i < count; i++ ) {
|
||||
if ( !strncmp(env.names[i], name, name_size) ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
char** environment_to_stringlist(environment env) {
|
||||
int len = environment_len(env);
|
||||
char** ret = stringlist_alloc(len+1);
|
||||
for ( int i = 0; i < len; i++ ) {
|
||||
char* name = env.names[i];
|
||||
char* value = env.values[i];
|
||||
int result_len = strlen(name) + strlen(value) + 1;
|
||||
ret[i] = calloc(result_len+1, sizeof(char));
|
||||
strcat(ret[i], name);
|
||||
strcat(ret[i], "=");
|
||||
strcat(ret[i], value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char** adjusted_environment(const char* filename, char* const envp[]) {
|
||||
if ( !envp ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int envc = arr_len(envp);
|
||||
|
||||
char* appdir = NULL;
|
||||
|
||||
environment orig = environment_alloc(envc);
|
||||
environment startup = environment_alloc(envc);
|
||||
int orig_prefix_len = strlen(APPIMAGE_ORIG_PREFIX);
|
||||
int startup_prefix_len = strlen(APPIMAGE_STARTUP_PREFIX);
|
||||
for ( int i = 0; i < envc; i++ ) {
|
||||
char* line = envp[i];
|
||||
int name_size = strchr(line, '=')-line;
|
||||
int val_size = strlen(line)-name_size-1;
|
||||
|
||||
if ( !strncmp(line, APPIMAGE_ORIG_PREFIX, orig_prefix_len) ) {
|
||||
environment_append_item(orig, line+orig_prefix_len, name_size-orig_prefix_len,
|
||||
line+name_size+1, val_size);
|
||||
}
|
||||
if ( !strncmp(line, APPIMAGE_STARTUP_PREFIX, startup_prefix_len) ) {
|
||||
environment_append_item(startup, line+startup_prefix_len, name_size-startup_prefix_len,
|
||||
line+name_size+1, val_size);
|
||||
}
|
||||
if ( !strncmp(line, APPDIR, strlen(APPDIR)) ) {
|
||||
appdir = calloc(val_size+1, sizeof(char));
|
||||
strncpy(appdir, line+name_size+1, val_size);
|
||||
}
|
||||
}
|
||||
|
||||
environment new_env = environment_alloc(envc);
|
||||
if ( appdir && strncmp(filename, appdir, strlen(appdir)) ) {
|
||||
// we have a value for $APPDIR and are leaving it -- perform replacement
|
||||
for ( int i = 0; i < envc; i++ ) {
|
||||
char* line = envp[i];
|
||||
if ( !strncmp(line, APPIMAGE_ORIG_PREFIX, strlen(APPIMAGE_ORIG_PREFIX)) ||
|
||||
!strncmp(line, APPIMAGE_STARTUP_PREFIX, strlen(APPIMAGE_STARTUP_PREFIX)) )
|
||||
{
|
||||
// we are not interested in the backup vars here, don't copy them over
|
||||
continue;
|
||||
}
|
||||
|
||||
int name_size = strchr(line, '=')-line;
|
||||
int val_size = strlen(line)-name_size-1;
|
||||
char* value = line+name_size+1;
|
||||
int value_len = strlen(value);
|
||||
|
||||
int at_startup = environment_find_name(startup, line, name_size);
|
||||
int at_original = environment_find_name(orig, line, name_size);
|
||||
if ( at_startup == -1 || at_original == -1 ) {
|
||||
// no information, just keep it
|
||||
environment_append_item(new_env, line, name_size, value, value_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
char* at_start = startup.values[at_startup];
|
||||
int at_start_len = strlen(at_start);
|
||||
char* at_orig = orig.values[at_original];
|
||||
int at_orig_len = strlen(at_orig);
|
||||
|
||||
// TODO HACK: do not copy over empty vars
|
||||
if ( strlen(at_orig) == 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !strncmp(line+name_size+1, startup.values[at_startup], val_size) ) {
|
||||
// nothing changed since startup, restore old value
|
||||
environment_append_item(new_env, line, name_size, at_orig, at_orig_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
int chars_added = value_len > at_start_len;
|
||||
char* use_value = NULL;
|
||||
if ( chars_added > 0 ) {
|
||||
// something was added to the current value
|
||||
// take _original_ value of the env var and append/prepend the same thing
|
||||
use_value = calloc(strlen(at_orig) + chars_added + 1, sizeof(char));
|
||||
if ( !strncmp(value, at_start, at_start_len) ) {
|
||||
// append case
|
||||
strcat(use_value, value);
|
||||
strcat(use_value, at_orig + strlen(value));
|
||||
}
|
||||
else if ( !strncmp(value+(value_len-at_start_len), at_start, at_start_len) ) {
|
||||
// prepend case
|
||||
strcat(use_value, at_orig + strlen(value));
|
||||
strcat(use_value, value);
|
||||
}
|
||||
else {
|
||||
// none of the above methods matched
|
||||
// assume the value changed completely and simply keep what the application set
|
||||
free(use_value);
|
||||
use_value = NULL;
|
||||
}
|
||||
}
|
||||
if ( !use_value ) {
|
||||
environment_append_item(new_env, line, name_size, value, value_len);
|
||||
}
|
||||
else {
|
||||
environment_append_item(new_env, line, name_size, use_value, strlen(use_value));
|
||||
free(use_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char** ret = NULL;
|
||||
if ( environment_len(new_env) > 0 ) {
|
||||
ret = environment_to_stringlist(new_env);
|
||||
}
|
||||
else {
|
||||
// nothing changed
|
||||
ret = stringlist_alloc(envc+1);
|
||||
for ( int i = 0; i < envc; i++ ) {
|
||||
int len = strlen(envp[i]);
|
||||
ret[i] = calloc(len+1, sizeof(char));
|
||||
strncpy(ret[i], envp[i], len);
|
||||
}
|
||||
}
|
||||
environment_free(orig);
|
||||
environment_free(startup);
|
||||
environment_free(new_env);
|
||||
free(appdir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int execve(const char* filename, char* const argv[], char* const envp[]) {
|
||||
char** new_envp = adjusted_environment(filename, envp);
|
||||
old_execve = dlsym(RTLD_NEXT, "execve");
|
||||
int ret = old_execve(filename, argv, new_envp);
|
||||
stringlist_free(new_envp);
|
||||
//printf(">>> custom execve()!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int execv(const char* filename, char* const argv[]) {
|
||||
char** new_envp = adjusted_environment(filename, environ);
|
||||
old_execve = dlsym(RTLD_NEXT, "execve");
|
||||
int ret = old_execve(filename, argv, new_envp);
|
||||
stringlist_free(new_envp);
|
||||
//printf(">>> custom execv()!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int execvpe(const char* filename, char* const argv[], char* const envp[]) {
|
||||
// TODO: might not be full path
|
||||
char** new_envp = adjusted_environment(filename, envp);
|
||||
old_execvpe = dlsym(RTLD_NEXT, "execvpe");
|
||||
int ret = old_execvpe(filename, argv, new_envp);
|
||||
stringlist_free(new_envp);
|
||||
//printf(">>> custom execvpe()!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int execvp(const char* filename, char* const argv[]) {
|
||||
// TODO: might not be full path
|
||||
char** new_envp = adjusted_environment(filename, environ);
|
||||
old_execvpe = dlsym(RTLD_NEXT, "execvpe");
|
||||
int ret = old_execvpe(filename, argv, new_envp);
|
||||
stringlist_free(new_envp);
|
||||
//printf(">>> custom execvp()!\n");
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue
Block a user