我正在尝试这样做:我在/chroot/debian6.0/
中定义了一个环境
我绑定了一些目录并创建了其他目录。一个是libs/
,其中包含库libOne.so
及其依赖项
所以:
/chroot/debian6.0/
\--- libs/
\--- libOne.so
\--- other dependencies (*.so)
这个库已经在chroot环境中编译了,我想用包含环境的进程运行它。
这是代码:
#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int res = 0;
void* handle;
/*chdir to first argument(path1)*/
res = chdir(argv[1]);
if (res == 0) {
printf("\nchdir: %s", argv[1]);
} else {
printf("\nError chdir %s\n", argv[1]);
return 1;
}
/*chroot to path in first argument*/
res = chroot(argv[1]);
if (res == 0) {
printf("\nchroot: %s", argv[1]);
} else {
printf("\nError chroot %s\n", argv[1]);
return 1;
}
/*Define path for dependencies*/
putenv("LD_LIBRARY_PATH=/libs/");
/*Opens library*/
handle = dlopen(argv[2], RTLD_NOW);
if (handle == NULL) {
printf("\nError opening %s\n", argv[2]);
return 1;
} else {
printf("\ndlopen: library %s opened\n", argv[2]);
}
return 0;
}
我使用以下命令执行:
./ remote“/chroot/debian6.0/Debian-6.0-chroot/”“/ libs / libOne.so”
尝试dlopen库时结果是错误。 最后一行:
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0P#\0\000"..., 1024) = 1024
fstat64(3, {st_mode=S_IFREG|0644, st_size=116600, ...}) = 0
old_mmap(NULL, 119656, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0xb7644000
mprotect(0xb7661000, 872, PROT_NONE) = 0
old_mmap(0xb7661000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x1c000) = 0xb7661000
close(3) = 0
munmap(0xb7f01000, 6291) = 0
munmap(0xb7770000, 6496996) = 0
munmap(0xb7757000, 98784) = 0
munmap(0xb7662000, 1000332) = 0
munmap(0xb7644000, 119656) = 0
write(1, "chroot: /chroot/debian6.0/Deb"..., 48chroot: /chroot/debian6.0/Debian-6.0-chroot/
) = 48
write(1, "Error opening /libs/libD"..., 50Error opening /libs/libOne.so
) = 50
munmap(0xb7f03000, 4096) = 0
_exit(1) = ?
该库似乎具有所有依赖关系 - 我可以在chroot内ldd libOne.so
得到
linux-gate.so.1 =&gt; (0xb7f16000)libpthread.so.0 =&gt; /lib/libpthread.so.0(0xb78c6000)libstdc ++。so.6 =&gt; /usr/lib/libstdc++.so.6(0xb77d1000)libm.so.6 =&gt; /lib/libm.so.6 (0xb77aa000)libc.so.6 =&gt; /lib/libc.so.6(0xb7665000)
libgcc_s.so.1 =&gt; /lib/libgcc_s.so.1(0xb7647000)/lib/ld-linux.so.2 (0xb7f17000)
知道dlopen
失败的原因吗?或者如何让它发挥作用?
添加了perror和dlerror,我得到了:
Error opening library /lib/libc.so.6: version `GLIBC_2.4' not found (required by /libs/libOne.so)) = 117
更新:
虽然我已经复制了用于编译程序的libc版本,但是我的包含环境和chroot'环境(都在/ lib中),我仍然在执行时得到相同的消息:
打开库/lib/libc.so.6时出错:找不到版本“GLIBC_2.4” (/libs/libOne.so要求))= 117
所以我去了每个lib文件夹,这就是我所看到的:
包含环境:
ll /lib
libc-2.2.4.so
libc.so.6 -> libc-2.2.4.so
ldd libc.so.6
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb7dcc000)
Chroot的环境:
ll /lib
libc.so.6 -> libc-2.11.3.so
libc-2.11.3.so
ldd libc.so.6
/lib/ld-linux.so.2 (0xb7fb4000)
linux-gate.so.1 => (0xb7fb3000)
在我的编译环境中:
ll /lib
libc.so.6 -> libc-2.11.3.so
libc-2.11.3.so
ldd libc.so.6
/lib/ld-linux.so.2 (0xf770f000)
linux-gate.so.1 => (0xf770c000)
所以似乎我的libc.so.6的编译和执行版本是相同的,并且链接到第二个是相同的,即libc-2.11.3.so
所以,我不明白为什么我会收到 GLIBC_2.4消息
上次更新:
现在,在Petesh的指导和使用dlmopen
之后,我的strace会抛出下一条消息(最后):
writev(2, [{"./remote", 5}, {": ", 2}, {"/lib/libdl.so.2", 15}, {": ", 2}, {"version \`GLIBC_2.3.4\' not found "..., 51}, {"\n", 1}], 6./remote: /lib/libdl.so.2: version `GLIBC_2.3.4' not found (required by ./remote) ) = 76
答案 0 :(得分:0)
与GLIBC_2.4
未找到相关的错误很可能是因为dlopen
的调用发生在已经映射到libc副本但尚未公开的过程中版本
因此,我们需要有点创意,并使用辅助例程dlmopen
而不是简单的旧dlopen
。
dlmopen接受一个参数lm_id
,它是一个命名空间,它加载所有相关的库 - 如果使用默认值(LM_ID_BASE
),它将从程序运行的命名空间开始;例如,它将包含在启动应用程序时已加载的libc
。
因此,我们不是仅仅调用handle = dlopen(argv[2], RTLD_NOW);
,而是通过调用:
handle = dlmopen(LM_ID_NEWLM, argv[2], RTLD_NOW | RTLD_LOCAL);
这会将argv[2]
所需的所有库的新副本加载到该句柄使用的链接地址空间中。
然后,您可以使用以下方法引用库中的符号:
symbol = dlsym(handle, "symname");
它应该有用。
我稍微改变了你的例子来使用它:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int res = 0;
void* handle;
/*chdir to first argument(path1)*/
res = chdir(argv[1]);
if (res == 0) {
printf("\nchdir: %s", argv[1]);
} else {
printf("\nError chdir %s\n", argv[1]);
return 1;
}
/*chroot to path in first argument*/
res = chroot(argv[1]);
if (res == 0) {
printf("\nchroot: %s", argv[1]);
} else {
printf("\nError chroot %s\n", argv[1]);
return 1;
}
/*Define path for dependencies*/
putenv("LD_LIBRARY_PATH=/libs/");
/*Opens library*/
handle = dlmopen(LM_ID_NEWLM, argv[2], RTLD_NOW); // needs _GNU_SOURCE
if (handle == NULL) {
printf("\nError opening %s: %s\n", argv[2], dlerror());
return 1;
} else {
printf("\ndlopen: library %s opened\n", argv[2]);
}
int (*foo)(void);
foo = (int (*)(void))dlsym(handle, argv[3]);
if (foo == NULL) {
printf("\nError referencing %s: %s\n", argv[3], dlerror());
return 1;
}
printf("%d\n", foo());
return 0;
}
现在,putenv("LD_LIBRARY_PATH=/libs/");
暂时不需要 - 您无法在程序运行时更改LD_LIBRARY_PATH
;根本无法检测到变化。因为你的程序非常简单,我们可以在主要的开头做一些事情,如:
if (0 == getenv("RE_EXEC")) {
putenv("RE_EXEC=1");
putenv("LD_LIBRARY_PATH=/libs/");
execvp(argv[0], argv);
perror("execvp failed");
exit(1);
}
在LD_LIBRARY_PATH
设置为/libs
时重新执行,因此会在那里搜索。
还有一些其他可能的解决方案 - 如果路径设置不适合re-exec
您可以确保libOne.so
在二进制文件的rpath中有$ORIGIN
。您可以在编译/链接时使用以下命令执行此操作:
-Wl,-rpath,'$ORIGIN'
引用是为了防止$ORIGIN
被shell转义。如果这是makefile
,那么您需要使用$$
语法进行目标操作。
您可以使用patchelf
之类的工具对库进行临时补丁(在我的系统上我做了一个简单的sudo apt-get install patchelf
):
patchelf --set-rpath '$ORIGIN' libOne.so
这当然依赖于$ORIGIN
中对ld.so
的支持(我不记得它何时被添加到ld.so
;但它已经过了几年);如果您的ld.so
不支持$ORIGIN
,那么您可以随时将其替换为/libs/
,如果您找不到patchelf
发行版,那么您可以随时build it yourself