IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Linux 不使用 chroot 临时替换C运行时库

    hev发表于 2015-07-08 03:32:36
    love 0

    有些时候我们需要在老旧的 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!



沪ICP备19023445号-2号
友情链接