├── .gitignore ├── env.h ├── checkrt.h ├── debug.h ├── .travis.yml ├── Makefile ├── AppRun.sh ├── AppRun.c.patch ├── README.md ├── env.c ├── exec.c └── checkrt.c /.gitignore: -------------------------------------------------------------------------------- 1 | /AppRun.c 2 | /AppRun_patched* 3 | *.o 4 | -------------------------------------------------------------------------------- /env.h: -------------------------------------------------------------------------------- 1 | #ifndef ENV_H 2 | #define END_H 3 | 4 | #include 5 | 6 | char* const* read_parent_env(); 7 | void env_free(char* const *env); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /checkrt.h: -------------------------------------------------------------------------------- 1 | #ifndef CHEKRT_H 2 | #define CHECKRT_H 3 | 4 | extern char *optional_ld_library_path; 5 | extern char *optional_ld_preload; 6 | extern void checkrt(char *usr_in_appdir); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | #include 5 | #include 6 | 7 | #define DEBUG(...) do { \ 8 | if (getenv("APPIMAGE_CHECKRT_DEBUG")) \ 9 | printf("APPIMAGE_CHECKRT>> " __VA_ARGS__); \ 10 | } while (0) 11 | 12 | #endif // DEBUG_H 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: gcc 3 | 4 | services: 5 | - docker 6 | 7 | env: 8 | - ARCH=i686 DOCKER_IMAGE=toopher/centos-i386:centos6 9 | - ARCH=x86_64 DOCKER_IMAGE=library/centos:6.8 10 | 11 | script: 12 | - mkdir -p ./out/ 13 | - docker run -i -v ${PWD}/out:/out -v "${PWD}:/AppImageKit-checkrt" "$DOCKER_IMAGE" /bin/bash -c "yum -y install gcc wget make binutils glibc-devel patch ; make -C /AppImageKit-checkrt ; cp /AppImageKit-checkrt/AppRun_patched out/AppRun-patched-${ARCH} ; cp /AppImageKit-checkrt/exec.so out/exec-${ARCH}.so" 14 | - ls -lh out/* 15 | - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh 16 | - bash ./upload.sh out/* 17 | 18 | branches: 19 | except: 20 | - # Do not build tags that we create when we upload to GitHub Releases 21 | - /^(?i:continuous)$/ 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -O2 -Wall -Wextra 2 | LDFLAGS += -s 3 | BIN = AppRun_patched 4 | LIB = exec.so 5 | EXEC_TEST = exec_test 6 | ENV_TEST = env_test 7 | 8 | checkrt: $(BIN) $(LIB) 9 | 10 | test: $(EXEC_TEST) $(ENV_TEST) 11 | 12 | all: checkrt test 13 | 14 | clean: 15 | -rm -f $(BIN) $(LIB) $(EXEC_TEST) $(ENV_TEST) *.o AppRun.c AppRun_patched.c 16 | 17 | $(BIN): AppRun_patched.o checkrt.o env.o 18 | 19 | $(LIB): exec.o env.o 20 | $(CC) -shared $(LDFLAGS) -o $@ $^ -ldl 21 | 22 | AppRun_patched.o checkrt.o: CFLAGS += -include checkrt.h 23 | exec.o env.o: CFLAGS += -fPIC 24 | 25 | $(EXEC_TEST): CFLAGS += -DEXEC_TEST 26 | $(EXEC_TEST): exec.c env.c 27 | $(CC) -o $@ $(CFLAGS) $^ -ldl 28 | 29 | $(ENV_TEST): CFLAGS += -DENV_TEST 30 | $(ENV_TEST): env.c 31 | $(CC) -o $@ $(CFLAGS) $^ 32 | 33 | run_tests: $(EXEC_TEST) $(ENV_TEST) 34 | ./$(ENV_TEST) 35 | ./$(EXEC_TEST) 36 | 37 | AppRun_patched.c: AppRun.c 38 | patch -p1 --output $@ < AppRun.c.patch 39 | 40 | AppRun.c: 41 | wget -c "https://raw.githubusercontent.com/AppImage/AppImageKit/appimagetool/master/src/AppRun.c" 42 | 43 | .PHONY: checkrt test run_tests all clean 44 | -------------------------------------------------------------------------------- /AppRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # A shell script that does the same as the binaries in the release section. 4 | # It's only here as an example on how to achieve it with a Shell script. 5 | 6 | cd "$(dirname "$0")" 7 | 8 | cxxpre="" 9 | gccpre="" 10 | execpre="" 11 | libc6arch="libc6,x86-64" 12 | exec="./bin/$(sed -n 's|^Exec=||p' $(ls -1 *.desktop))" 13 | 14 | if [ -n "$APPIMAGE" ] && [ "$(file -b "$APPIMAGE" | cut -d, -f2)" != " x86-64" ]; then 15 | libc6arch="libc6" 16 | fi 17 | 18 | cd "usr" 19 | 20 | if [ -e "./optional/libstdc++/libstdc++.so.6" ]; then 21 | lib="$(PATH="/sbin:$PATH" ldconfig -p | grep "libstdc++\.so\.6 ($libc6arch)" | awk 'NR==1{print $NF}')" 22 | sym_sys=$(tr '\0' '\n' < "$lib" | grep -e '^GLIBCXX_3\.4' | tail -n1) 23 | sym_app=$(tr '\0' '\n' < "./optional/libstdc++/libstdc++.so.6" | grep -e '^GLIBCXX_3\.4' | tail -n1) 24 | if [ "$(printf "${sym_sys}\n${sym_app}"| sort -V | tail -1)" != "$sym_sys" ]; then 25 | cxxpath="./optional/libstdc++:" 26 | fi 27 | fi 28 | 29 | if [ -e "./optional/libgcc/libgcc_s.so.1" ]; then 30 | lib="$(PATH="/sbin:$PATH" ldconfig -p | grep "libgcc_s\.so\.1 ($libc6arch)" | awk 'NR==1{print $NF}')" 31 | sym_sys=$(tr '\0' '\n' < "$lib" | grep -e '^GCC_[0-9]\\.[0-9]' | tail -n1) 32 | sym_app=$(tr '\0' '\n' < "./optional/libgcc/libgcc_s.so.1" | grep -e '^GCC_[0-9]\\.[0-9]' | tail -n1) 33 | if [ "$(printf "${sym_sys}\n${sym_app}"| sort -V | tail -1)" != "$sym_sys" ]; then 34 | gccpath="./optional/libgcc:" 35 | fi 36 | fi 37 | 38 | if [ -n "$cxxpath" ] || [ -n "$gccpath" ]; then 39 | if [ -e "./optional/exec.so" ]; then 40 | execpre="" 41 | export LD_PRELOAD="./optional/exec.so:${LD_PRELOAD}" 42 | fi 43 | export LD_LIBRARY_PATH="${cxxpath}${gccpath}${LD_LIBRARY_PATH}" 44 | fi 45 | 46 | #echo ">>>>> $LD_LIBRARY_PATH" 47 | #echo ">>>>> $LD_PRELOAD" 48 | 49 | $exec "$*" 50 | exit $? 51 | 52 | -------------------------------------------------------------------------------- /AppRun.c.patch: -------------------------------------------------------------------------------- 1 | --- a/AppRun.c 2 | +++ b/AppRun.c 3 | @@ -164,6 +164,10 @@ 4 | char *old_env; 5 | size_t length; 6 | const char *format; 7 | + checkrt(usr_in_appdir); 8 | + 9 | + if (optional_ld_preload) 10 | + putenv(optional_ld_preload); 11 | 12 | /* https://docs.python.org/2/using/cmdline.html#envvar-PYTHONHOME */ 13 | SET_NEW_ENV(new_pythonhome, appdir_s, "PYTHONHOME=%s/usr/", appdir); 14 | @@ -172,7 +176,7 @@ 15 | 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); 16 | 17 | old_env = getenv("LD_LIBRARY_PATH") ?: ""; 18 | - 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); 19 | + SET_NEW_ENV(new_ld_library_path, strlen(optional_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_ld_library_path, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, appdir, old_env); 20 | 21 | old_env = getenv("PYTHONPATH") ?: ""; 22 | SET_NEW_ENV(new_pythonpath, appdir_s + strlen(old_env), "PYTHONPATH=%s/usr/share/pyshared/:%s", appdir, old_env); 23 | @@ -201,6 +205,9 @@ 24 | if (ret == -1) 25 | die("Error executing '%s': %s\n", exe, strerror(error)); 26 | 27 | + free(optional_ld_library_path); 28 | + if (optional_ld_preload) 29 | + free(optional_ld_preload); 30 | free(line); 31 | free(desktop_file); 32 | free(usr_in_appdir); 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The problem 2 | 3 | Some projects require newer C++ standards to build them. To keep the glibc dependency low you can 4 | build a newer GCC version on an older distro and use it to compile the project. 5 | This project however will now require a newer version of the `libstdc++.so.6` library than available on that distro. 6 | Bundling `libstdc++.so.6` however will in most cases break compatibility with distros that have a newer library 7 | version installed into their system than the bundled one. So blindly bundling the library is not reliable. 8 | 9 | By the way, while this is primarily an issue with `libstdc++.so.6` in some rare cases this might also occur with `libgcc_s.so.1`. 10 | That's because both libraries are part of GCC. 11 | 12 | 13 | # The solution 14 | 15 | You would have to know the library version of the host system and decide whether to use a bundled library or not before the 16 | application is started. This is exactly what the patched AppRun binary does. 17 | It will search for `usr/optional/libstdc++/libstdc++.so.6` and `usr/optional/libgcc_s/libgcc_s.so.1` inside the AppImage or AppDir. 18 | 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. 19 | 20 | You should also put `exec.so` into `usr/optional`. This exec.so library is intended to restore the environment of the AppImage to its parent. 21 | This is done to avoid library clashing of bundled libraries with external processes. e.g when running the web browser 22 | 23 | The intended usage is as follows: 24 | 25 | 1. This library is injected to the dynamic loader through LD_PRELOAD 26 | automatically in AppRun **only** if `usr/optional/exec.so` exists: 27 | e.g `LD_PRELOAD=$APPDIR/usr/optional/exec.so My.AppImage` 28 | 29 | 2. This library will intercept calls to new processes and will detect whether 30 | those calls are for binaries within the AppImage bundle or external ones. 31 | 32 | 3. In case it's an internal process, it will not change anything. 33 | In case it's an external process, it will restore the environment of 34 | the AppImage parent by reading `/proc/[pid]/environ`. 35 | This is the conservative approach taken. 36 | -------------------------------------------------------------------------------- /env.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018 Pablo Marcos Oltra 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | #define _GNU_SOURCE 23 | 24 | #include "env.h" 25 | #include "debug.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | static char** env_allocate(size_t size) { 34 | return calloc(size + 1, sizeof(char*)); 35 | } 36 | 37 | void env_free(char* const *env) { 38 | size_t len = 0; 39 | while (env[len] != 0) { 40 | free(env[len]); 41 | len++; 42 | } 43 | free((char**)env); 44 | } 45 | 46 | static size_t get_number_of_variables(FILE *file, char **buffer, size_t *len) { 47 | size_t number = 0; 48 | 49 | if (getline(buffer, len, file) < 0) 50 | return -1; 51 | 52 | char *ptr = *buffer; 53 | while (ptr < *buffer + *len) { 54 | size_t var_len = strlen(ptr); 55 | ptr += var_len + 1; 56 | if (var_len == 0) 57 | break; 58 | number++; 59 | } 60 | 61 | return number != 0 ? (ssize_t)number : -1; 62 | } 63 | 64 | static char* const* env_from_buffer(FILE *file) { 65 | char *buffer = NULL; 66 | size_t len = 0; 67 | size_t num_vars = get_number_of_variables(file, &buffer, &len); 68 | char** env = env_allocate(num_vars); 69 | 70 | size_t n = 0; 71 | char *ptr = buffer; 72 | while (ptr < buffer + len && n < num_vars) { 73 | size_t var_len = strlen(ptr); 74 | if (var_len == 0) 75 | break; 76 | 77 | env[n] = calloc(sizeof(char*), var_len + 1); 78 | strncpy(env[n], ptr, var_len + 1); 79 | DEBUG("\tenv var copied: %s\n", env[n]); 80 | ptr += var_len + 1; 81 | n++; 82 | } 83 | free(buffer); 84 | 85 | return env; 86 | } 87 | 88 | static char* const* read_env_from_process(pid_t pid) { 89 | char buffer[256] = {0}; 90 | 91 | snprintf(buffer, sizeof(buffer), "/proc/%d/environ", pid); 92 | DEBUG("Reading env from parent process: %s\n", buffer); 93 | FILE *env_file = fopen(buffer, "r"); 94 | if (!env_file) { 95 | DEBUG("Error reading file: %s (%s)\n", buffer, strerror(errno)); 96 | return NULL; 97 | } 98 | 99 | char* const* env = env_from_buffer(env_file); 100 | fclose(env_file); 101 | 102 | return env; 103 | } 104 | 105 | char* const* read_parent_env() { 106 | pid_t ppid = getppid(); 107 | return read_env_from_process(ppid); 108 | } 109 | 110 | #ifdef ENV_TEST 111 | int main() { 112 | putenv("APPIMAGE_CHECKRT_DEBUG=1"); 113 | DEBUG("ENV TEST\n"); 114 | char **env = NULL; 115 | read_parent_env(&env); 116 | 117 | return 0; 118 | } 119 | #endif 120 | 121 | -------------------------------------------------------------------------------- /exec.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018 Pablo Marcos Oltra 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | /* 23 | * This exec.so library is intended to restore the environment of the AppImage's 24 | * parent process. This is done to avoid library clashing of bundled libraries 25 | * with external processes. e.g when running the web browser 26 | * 27 | * The intended usage is as follows: 28 | * 29 | * 1. This library is injected to the dynamic loader through LD_PRELOAD 30 | * automatically in AppRun **only** if `usr/optional/exec.so` exists: 31 | * e.g `LD_PRELOAD=$APPDIR/usr/optional/exec.so My.AppImage` 32 | * 33 | * 2. This library will intercept calls to new processes and will detect whether 34 | * those calls are for binaries within the AppImage bundle or external ones. 35 | * 36 | * 3. In case it's an internal process, it will not change anything. 37 | * In case it's an external process, it will restore the environment of 38 | * the AppImage parent by reading `/proc/[pid]/environ`. 39 | * This is the conservative approach taken. 40 | */ 41 | 42 | #define _GNU_SOURCE 43 | 44 | #include "env.h" 45 | #include "debug.h" 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | typedef int (*execve_func_t)(const char *filename, char *const argv[], char *const envp[]); 54 | 55 | #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) 56 | 57 | static const char* get_fullpath(const char *filename) { 58 | // Try to get the canonical path in case it's a relative path or symbolic 59 | // link. Otherwise, use which to get the fullpath of the binary 60 | char *fullpath = canonicalize_file_name(filename); 61 | DEBUG("filename %s, canonical path %s\n", filename, fullpath); 62 | if (fullpath) 63 | return fullpath; 64 | 65 | return filename; 66 | } 67 | 68 | static int is_external_process(const char *filename) { 69 | const char *appdir = getenv("APPDIR"); 70 | if (!appdir) 71 | return 0; 72 | DEBUG("APPDIR = %s\n", appdir); 73 | 74 | return strncmp(filename, appdir, MIN(strlen(filename), strlen(appdir))); 75 | } 76 | 77 | static int exec_common(execve_func_t function, const char *filename, char* const argv[], char* const envp[]) { 78 | const char *fullpath = get_fullpath(filename); 79 | DEBUG("filename %s, fullpath %s\n", filename, fullpath); 80 | char* const *env = envp; 81 | if (is_external_process(fullpath)) { 82 | DEBUG("External process detected. Restoring env vars from parent %d\n", getppid()); 83 | env = read_parent_env(); 84 | if (!env) 85 | env = envp; 86 | else 87 | DEBUG("Error restoring env vars from parent\n"); 88 | } 89 | int ret = function(filename, argv, env); 90 | 91 | if (fullpath != filename) 92 | free((char*)fullpath); 93 | if (env != envp) 94 | env_free(env); 95 | 96 | return ret; 97 | } 98 | 99 | int execve(const char *filename, char *const argv[], char *const envp[]) { 100 | DEBUG("execve call hijacked: %s\n", filename); 101 | execve_func_t execve_orig = dlsym(RTLD_NEXT, "execve"); 102 | if (!execve_orig) 103 | DEBUG("Error getting execve original symbol: %s\n", strerror(errno)); 104 | 105 | return exec_common(execve_orig, filename, argv, envp); 106 | } 107 | 108 | int execv(const char *filename, char *const argv[]) { 109 | DEBUG("execv call hijacked: %s\n", filename); 110 | return execve(filename, argv, environ); 111 | } 112 | 113 | int execvpe(const char *filename, char *const argv[], char *const envp[]) { 114 | DEBUG("execvpe call hijacked: %s\n", filename); 115 | execve_func_t execve_orig = dlsym(RTLD_NEXT, "execvpe"); 116 | if (!execve_orig) 117 | DEBUG("Error getting execvpe original symbol: %s\n", strerror(errno)); 118 | 119 | return exec_common(execve_orig, filename, argv, envp); 120 | } 121 | 122 | int execvp(const char *filename, char *const argv[]) { 123 | DEBUG("execvp call hijacked: %s\n", filename); 124 | return execvpe(filename, argv, environ); 125 | } 126 | 127 | #ifdef EXEC_TEST 128 | int main(int argc, char *argv[]) { 129 | putenv("APPIMAGE_CHECKRT_DEBUG=1"); 130 | DEBUG("EXEC TEST\n"); 131 | execv("./env_test", argv); 132 | 133 | return 0; 134 | } 135 | #endif 136 | -------------------------------------------------------------------------------- /checkrt.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2018 djcj 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | #include "debug.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #ifndef LIBC6_ARCH 30 | # if (__WORDSIZE == 64) 31 | # define LIBC6_ARCH "libc6,x86-64" 32 | # else 33 | # define LIBC6_ARCH "libc6" 34 | # endif 35 | #endif 36 | 37 | #define LINE_SIZE 255 38 | 39 | #define SCANLIB(lib,sym,regex) \ 40 | sprintf(command, format, lib, regex); \ 41 | f = popen(command, "r"); \ 42 | ret = fscanf(f, "%s", sym); (void)ret; \ 43 | pclose(f); 44 | 45 | #define CXXDIR "optional/libstdc++" 46 | #define GCCDIR "optional/libgcc" 47 | #define EXEC_SO "optional/exec.so" 48 | 49 | char *optional_ld_library_path = NULL; 50 | char *optional_ld_preload = NULL; 51 | 52 | void checkrt(char *usr_in_appdir) 53 | { 54 | int ret; 55 | FILE *f; 56 | char command[LINE_SIZE]; 57 | char stdcxx_sys_lib[LINE_SIZE], gcc_sys_lib[LINE_SIZE]; 58 | char stdcxx_sys_sym[LINE_SIZE], gcc_sys_sym[LINE_SIZE]; 59 | char stdcxx_bundle_sym[LINE_SIZE], gcc_bundle_sym[LINE_SIZE]; 60 | int stdcxx_sys_ver=1, stdcxx_bundle_ver=0, gcc_sys_ver=1, gcc_bundle_ver=0; 61 | 62 | char *stdcxx_bundle_lib = "./" CXXDIR "/libstdc++.so.6"; 63 | char *gcc_bundle_lib = "./" GCCDIR "/libgcc_s.so.1"; 64 | const char *format = "tr '\\0' '\\n' < '%s' | grep -e '%s' | tail -n1"; 65 | 66 | if (access(stdcxx_bundle_lib, F_OK) == 0) { 67 | f = popen("PATH=\"/sbin:$PATH\" ldconfig -p | grep 'libstdc++.so.6 (" LIBC6_ARCH ")' | awk 'NR==1{print $NF}'", "r"); 68 | ret = fscanf(f, "%s", stdcxx_sys_lib); (void)ret; 69 | pclose(f); 70 | 71 | if (access(stdcxx_sys_lib, F_OK) == 0) { 72 | SCANLIB(stdcxx_sys_lib, stdcxx_sys_sym, "^GLIBCXX_3\\.4"); 73 | SCANLIB(stdcxx_bundle_lib, stdcxx_bundle_sym, "^GLIBCXX_3\\.4"); 74 | stdcxx_sys_ver = atoi(stdcxx_sys_sym+12); 75 | stdcxx_bundle_ver = atoi(stdcxx_bundle_sym+12); 76 | DEBUG("%s ==> %s (%d)\n", stdcxx_sys_lib, stdcxx_sys_sym, stdcxx_sys_ver); 77 | DEBUG("%s ==> %s (%d)\n\n", stdcxx_bundle_lib, stdcxx_bundle_sym, stdcxx_bundle_ver); 78 | } 79 | } 80 | 81 | if (access(gcc_bundle_lib, F_OK) == 0) { 82 | f = popen("PATH=\"/sbin:$PATH\" ldconfig -p | grep 'libgcc_s.so.1 (" LIBC6_ARCH ")' | awk 'NR==1{print $NF}'", "r"); 83 | ret = fscanf(f, "%s", gcc_sys_lib); (void)ret; 84 | pclose(f); 85 | 86 | if (access(gcc_sys_lib, F_OK) == 0) { 87 | SCANLIB(gcc_sys_lib, gcc_sys_sym, "^GCC_[0-9]\\.[0-9]"); 88 | SCANLIB(gcc_bundle_lib, gcc_bundle_sym, "^GCC_[0-9]\\.[0-9]"); 89 | gcc_sys_ver = atoi(gcc_sys_sym+4) * 100 + atoi(gcc_sys_sym+6) * 10 + atoi(gcc_sys_sym+8); 90 | gcc_bundle_ver = atoi(gcc_bundle_sym+4) * 100 + atoi(gcc_bundle_sym+6) * 10 + atoi(gcc_bundle_sym+8); 91 | DEBUG("%s ==> %s (%d)\n", gcc_sys_lib, gcc_sys_sym, gcc_sys_ver); 92 | DEBUG("%s ==> %s (%d)\n\n", gcc_bundle_lib, gcc_bundle_sym, gcc_bundle_ver); 93 | } 94 | } 95 | 96 | int bundle_cxx = 0; 97 | int bundle_gcc = 0; 98 | size_t len = strlen(usr_in_appdir); 99 | 100 | if (stdcxx_bundle_ver > stdcxx_sys_ver) 101 | bundle_cxx = 1; 102 | 103 | if (gcc_bundle_ver > gcc_sys_ver) 104 | bundle_gcc = 1; 105 | 106 | char *exec_file = malloc(strlen(usr_in_appdir) + 1 + strlen(EXEC_SO) + 1); 107 | sprintf(exec_file, "%s/%s", usr_in_appdir, EXEC_SO); 108 | f = fopen(exec_file, "r"); 109 | if (f) { 110 | char *old_ld_preload = getenv("LD_PRELOAD"); 111 | optional_ld_preload = malloc(strlen(EXEC_SO) + (old_ld_preload ? 1+strlen(old_ld_preload) : 0) + 13 + len); 112 | sprintf(optional_ld_preload, "LD_PRELOAD=%s%s%s", exec_file, 113 | old_ld_preload ? ":" : "", old_ld_preload ? old_ld_preload : ""); 114 | DEBUG("optional_ld_preload: %s\n", optional_ld_preload); 115 | fclose(f); 116 | } 117 | free(exec_file); 118 | 119 | if (bundle_cxx == 1 && bundle_gcc == 0) { 120 | optional_ld_library_path = malloc(strlen(CXXDIR) + 3 + len); 121 | sprintf(optional_ld_library_path, "%s/" CXXDIR ":", usr_in_appdir); 122 | } else if (bundle_cxx == 0 && bundle_gcc == 1) { 123 | optional_ld_library_path = malloc(strlen(GCCDIR) + 3 + len); 124 | sprintf(optional_ld_library_path, "%s/" GCCDIR ":", usr_in_appdir); 125 | } else if (bundle_cxx == 1 && bundle_gcc == 1) { 126 | optional_ld_library_path = malloc(strlen(GCCDIR) + strlen(CXXDIR) + 5 + len*2); 127 | sprintf(optional_ld_library_path, "%s/" GCCDIR ":%s/" CXXDIR ":", usr_in_appdir, usr_in_appdir); 128 | } else { 129 | optional_ld_library_path = malloc(2); 130 | sprintf(optional_ld_library_path, "%s", ""); 131 | } 132 | 133 | DEBUG("optional_ld_library_path: %s\n", optional_ld_library_path); 134 | } 135 | 136 | --------------------------------------------------------------------------------