标签:应该 std 编译 代码 函数地址 阅读 lib exec section
程序启动时,链接器会优先LD_PRELOAD指定的库中的符号。如果fork()创建子进程后,LD_PRELOAD还能生效么?
main.c
#include <unistd.h>
#include <stdio.h>
extern char** environ;
void foo();
int main()
{
for(char **current = environ; *current; current++) {
puts(*current);
}
if(0 == fork()){
foo();
}else{
sleep(1);
}
return 0;
}
foo.c
#include <stdio.h>
void foo()
{
printf("real foo\n");
}
wfoo.c
#include <stdio.h>
void foo()
{
printf("wrap foo\n");
}
exec.c
将以上代码编译成执行文件和动态库
gcc -fPIC -shared foo.c -o libfoo.so
gcc -fPIC -shared wfoo.c -o libwfoo.so
gcc main.c -L./ -lfoo
执行LD_PRELOAD=./libwfoo.so ./a.out
,其输出如下
[root@localhost ~]# LD_LIBRARY_PATH=./ LD_PRELOAD=./libwfoo.so ./a.out
LD_PRELOAD=./libwfoo.so
....
其他环境变量
...
wrap foo
也就是说fork()后的子进程其函数地址是和父进程一样的。
先执行了
export LD_LIBRARY_PATH=./
仔细想了一想,其实这和LD_PRELOAD并没有关系。fork()创建了子进程,用的COW,代码段是只读的,父子进程用的应该是同一份。
如果是创建子进程后执行exec簇函数,此时新程序镜像还会收到LD_PRELOAD的影响么?
#include <unistd.h>
int main()
{
if(0 == fork()){
execl("./a.out", "./a.out", NULL);
}
return 0;
}
gcc exec.c -o exec
[root@localhost ~]# LD_PRELOAD=./libwfoo.so ./exec
LD_PRELOAD=./libwfoo.so
....
其他环境变量
...
wrap foo
如果将excel()
替换为excele()
,则情况发生变化
[root@localhost ~]# ./exec
./a.out: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
这意味着LD_LIBRARY_PATH环境变量失效了。
仔细阅读man 3 exec
后,恍然大悟。exec函数簇可分为两类,一类将当前进程的char** environ
指向的环境变量传递给新程序镜像,如execl()
,另一类则只使用函数参数指定的环境变量,如execle()
。
综上,当使用execle()
这类函数时,如果没有手动将当前进程的环境变量通过参数传递下去,那么就会使得LD_PRELOAD失效,无法实现函数拦截的功能了。
标签:应该 std 编译 代码 函数地址 阅读 lib exec section
原文地址:https://www.cnblogs.com/yizui/p/11515119.html