ELF http://www.sco.com/developers/devspecs/gabi41.pdf可执行文件不可重定位,并且它们通常编译为在相同的起始地址(x86_64 为 0x400000)处启动,这意味着从技术上讲,不可能将其中两个加载到同一地址空间中。
你可以做的是:
编译你想要的可执行文件dlopen()
作为可执行共享库(-pie
)。从技术上讲,这个文件是一个 ELF 共享对象,但可以执行。您可以检查该程序是否是 ELF 可执行文件或 ELF 共享对象readelf -h my_program
or file my_program
。 (作为奖励,通过将程序编译为共享对象,您将能够从 ASLR 中受益)。
通过将主程序编译为共享对象(以便将其加载到虚拟地址空间中的另一个位置),您应该能够动态链接其他可执行文件。 GNU 动态链接器不想dlopen
一个可执行文件,因此您必须自己进行动态链接(您可能不想这样做)。
或者,您可以使用链接描述文件链接其中一个可执行文件以使用另一个基地址。和以前一样,您必须自己完成动态链接器的工作。
解决方案 1:将动态加载的可执行文件编译为 PIE
被调用的可执行文件:
// hello.c
#include <string.h>
#include <stdio.h>
void hello()
{
printf("Hello world\n");
}
int main()
{
hello();
return 0;
}
调用者可执行文件:
// caller.c
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char** argv)
{
void* handle = dlopen(argv[1], RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
return 1;
}
void (*hello)() = dlsym(handle, "hello");
if (!hello) {
fprintf(stderr, "%s\n", dlerror());
return 1;
}
hello();
return 0;
}
试图让它发挥作用:
$ gcc -fpie -pie hello.c -o hello
$ gcc caller.c -o caller
$ ./caller ./hello
./hello: undefined symbol: hello
原因是当你将hello编译为PIE时,动态链接器不会将hell符号添加到动态符号表中(.dynsym
):
$ readelf -s
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000200 0 SECTION LOCAL DEFAULT 1
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
6: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
7: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
8: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)
9: 0000000000200bd0 0 NOTYPE GLOBAL DEFAULT 24 _edata
10: 0000000000200bd8 0 NOTYPE GLOBAL DEFAULT 25 _end
11: 0000000000200bd0 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
Symbol table '.symtab' contains 67 entries:
Num: Value Size Type Bind Vis Ndx Name
[...]
52: 0000000000000760 18 FUNC GLOBAL DEFAULT 13 hello
[...]
为了解决这个问题,您需要通过-E
标记为ld
(参见@AlexKey的回答):
$ gcc -fpie -pie hello.c -Wl,-E hello.c -o hello
$ gcc caller.c -o caller
$ ./caller ./hello
Hello world
$ ./hello
Hello world
$ readelf -s ./hello
Symbol table '.dynsym' contains 22 entries:
Num: Value Size Type Bind Vis Ndx Name
[...]
21: 00000000000008d0 18 FUNC GLOBAL DEFAULT 13 hello
[...]
一些参考资料
了解更多信息,4. 动态加载(DL)库 http://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html来自程序库指南是开始阅读的好地方。