关于linux:加载可执行文件或执行库
Loading executable or executing a library
关于如何执行库或动态加载可执行文件有大量关于 SO 的问题。据我所知,所有答案都归结为:将您的可执行文件编译为与位置无关的代码并使用 dlopen 加载它。这很好用——并且在 macOS 上仍然很好用——直到最近 glibc 发生了变化,它明确禁用了 dlopening PIE。例如,此更改现在在 ArchLinux 上的当前 glibc (2.30) 版本中,并且尝试 dlopen 位置无关的可执行文件会给出错误:"无法动态加载与位置无关的可执行文件"。
很难猜测是什么促成了如此彻底的改变,破坏了如此多的代码和有用的用例。 (关于 Patchwork 和 Bugzilla 的解释对我来说没有多大意义。)但是现在有一个问题:如果您想创建一个也是动态库的可执行文件,或者反之亦然,该怎么办?
其中一条评论链接了一个解决方案。在这里复制它以供后代使用:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <stdio.h>
#include <unistd.h> const char service_interp[] __attribute__((section(".interp"))) ="/lib/ld-linux-x86-64.so.2"; extern"C" { void lib_entry(void) } |
用 g++ -shared test-no-pie.cpp -o test-no-pie -Wl,-e,lib_entry 编译会生成一个共享对象(动态库),它也可以在 Linux 上执行。
我有两个问题:
相关讨论
- 如何更改解释器路径并将命令行参数传递给 Linux 上的"可执行"共享库的可能重复项?
请看这个答案:
https://stackoverflow.com/a/68339111/14760867
argc, argv 问题在那里没有得到解答,但是当我发现我需要一个时,我一起破解了一些东西以在运行时解析 /proc/self/cmdline 以供 pam_cap.so 使用。
It's difficult to guess what prompted such a radical change
并非如此:它从未正常工作过。
that breaks so much code
该代码已经以微妙的方式被破坏了。现在您可以清楚地看到它不起作用。
Are there other alternatives?
不要那样做吗?
dlopen执行一个可执行文件解决了什么问题?
如果这是一个真正的问题,请打开 GLIBC bugzilla 功能请求,解释该问题并请求支持的机制以达到预期的结果。
更新:
at least say why"it never worked correctly". Is it some triviality like potentially clashing globals between the executables, or something real?
线程局部变量是一个不能正常工作的例子。你是否认为它们是"真实的"我不知道。
代码如下:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// foo.c
#include <stdio.h> __thread int var; __attribute__((constructor)) int bar() { int main() |
|
1
2 3 4 |
gcc -g foo.c -o foo -Wl,-E -fpie -pie && ./foo
foo.c init: 42 0x7fb5dfd7d4fc foo.c bar: 42 0x7fb5dfd7d4fc foo.c main: 42 0x7fb5dfd7d4fc bar()=42 |
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
// main.c
// Error checking omitted for brevity #include <dlfcn.h> #include <stdio.h> int main() printf("main.c: %d |
|
1
2 3 4 |
gcc -g main.c -ldl && ./a.out
foo.c init: 42 0x7fb7305da73c foo.c bar: 0 0x7fb7305da73c <<< what? main.c: 0 <<< what? |
这是使用 GNU C Library (Debian GLIBC 2.28-10) stable release version 2.28.
底线:这从未被设计为有效,而且您只是碰巧没有踩到许多地雷,所以您认为它有效,而实际上您正在执行未定义的行为。
相关讨论
- 谢谢你的例子。所以全局变量是问题所在。似乎仍然像把婴儿和洗澡水一起扔出去。另外,如果我将 foo.c 编译为共享库,这是什么原因?即,这两种情况有何不同?
- 我同意...我们使用可执行共享库是为了方便共享库,该共享库可以通过与 GDB 独立运行来进行调试。 (而不是编译另一个可执行文件 dlopen 并运行 main)但是,我们看到了许多微妙的问题,包括全局变量......
- 如果示例代码是用 -fPIC 编译的(如本问题所示),则示例代码将正常工作。因此,它不能证明任何事情,并且在拿出更多证据之前,此答案中的所有内容都应仅视为意见和毫无根据的主张。
- @foxcub 全局变量不是问题。