有些时候我们需要在老旧的 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
*/
#include
#include
#define __USE_GNU
#include
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
#include
#include
#include
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
gcc -O3 -o wrapper wrapper.c
Over!