有些时候我们需要在老旧的 Linux 系统上运行一些依赖于较新版本 C 库的应用程序或库,应用程序会因为系统中安装的C库缺少符号还启动失败。解决方法之一就是临时替换使用非系统安装的C运行时库。使用临时C库需要做些什么配置及会带来哪些问题呢?
配置步骤
1. 下载与目标应用程序版本相匹配的临时C库,解压缩到临时位置 A。
2. 需要设置 LD_LIBRARY_PATH 环境变量指向目标临时C库的存储位置 A。
3. 需要通过与临时C库匹配的 ld.so 启用应用程序。因为应用程序默认是链接了一个绝对路径的 ld.so,如 x86_64 是 /lib64/ld-linux-x86-64.so.2
衍生问题
使用临时C库的 ld.so 启动的应用程序执行系统标准命令的子进程出错,原因是因为环境变量 LD_LIBRARY_PATH 被子进程继承,从而导致子进程在执行系统C库的ld.so中加载了版本不匹配的临时C库。
解决办法
在合适的时机清除环境变量 LD_LIBRARY_PATH,最合适的时机应用就是执行目标应用程序 main 函数之前啦。这里又要用到了之前写过的方法 => Linux 平台一种进程代码注入方法
/* fakemain.c * Heiher <admin@heiher.info> */ #include <stdio.h> #include <stdlib.h> #define __USE_GNU #include <dlfcn.h> int __libc_start_main(int (*main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void (*stack_end)) { int (*__libc_start_main_real)(int (*main) (int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void (*stack_end)); unsetenv ("LD_PRELOAD"); unsetenv ("LD_LIBRARY_PATH"); __libc_start_main_real = dlsym(RTLD_NEXT, "__libc_start_main"); return __libc_start_main_real(main, argc, ubp_av, init, fini, rtld_fini, stack_end); } |
gcc -fPIC -O3 -shared -o libfakemain.so fakemain.c -ldl |
设置环境变量 LD_PRELOAD=/xxx/libfakemain.so,运行目标应用程序在执行 main 之前即会清除 LD_PRELOAD 和 LD_LIBRARY_PATH 变量。
为了方便使用我还写了个 wrapper,使用方法是将真实的目标应用程序 xxx 重命令为 xxx.bin,然后创建个符号链接 xxx 指向 wrapper,执行时直接执行 xxx,wrapper 会自动设置所需要的环境变量。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main (int argc, char *argv[]) { int i; char buf[1024], path[1024]; char *str, *root, *args[512]; /* get FAKE_ROOT */ root = getenv ("FAKE_ROOT"); if (!root) { fprintf (stderr, "Please set environment FAKE_ROOT!\n"); return -1; } /* export PATH */ str = getenv ("PATH"); if (!str) { fprintf (stderr, "Get environment PATH failed!\n"); return -2; } if (NULL == strstr (str, root)) { strcpy (buf, root); strcat (buf, "/bin:"); strcat (buf, str); if (0 != setenv ("PATH", buf, 1)) { fprintf (stderr, "Set environment PATH failed!\n"); return -3; } } /* export LD_PRELOAD */ strcpy (buf, root); strcat (buf, "/lib64/libfakemain.so"); if (0 != setenv ("LD_PRELOAD", buf, 1)) { fprintf (stderr, "Set environment LD_PRELOAD failed!\n"); return -4; } /* export LD_LIBRARY_PATH */ strcpy (buf, root); strcat (buf, "/lib64"); if (0 != setenv ("LD_LIBRARY_PATH", buf, 1)) { fprintf (stderr, "Set environment LD_LIBRARY_PATH failed!\n"); return -5; } /* set new path */ strcpy (path, root); strcat (path, "/lib64/ld-2.20.so"); args[0] = path; /* set real program path */ strcpy (buf, root); strcat (buf, "/bin/"); strcat (buf, argv[0]); strcat (buf, ".bin"); args[1] = buf; /* copy arguments */ for (i=1; i<argc; i++) args[i+1] = argv[i]; args[i+1] = NULL; /* run real program */ return execv (path, args);; } |
gcc -O3 -o wrapper wrapper.c |
Over!