注意到了 gcc -shared
创建一个可执行文件,我只是有一个奇怪的想法,检查当我尝试运行它时会发生什么...好结果是一个 段错误 为了我自己的lib。因此,对此感到好奇,我试图“运行”glibc(/lib/x86_64-linux-gnu/libc.so.6
在我的系统上)。果然,它没有崩溃,但为我提供了一些输出:
GNU C Library (Debian GLIBC 2.19-18) stable release version 2.19, by Roland McGrath et al.
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.8.4.
Compiled on a Linux 3.16.7 system on 2015-04-14.
Available extensions:
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.
所以我的问题是:这背后的魔力是什么?我不能只定义一个 main
图书馆里的符号 - 或者我可以吗?
我写了 一篇博文 在这个问题上,我更深入,因为我发现它很有趣。您可以在下面找到我的原始答案。
您可以使用指定链接器的自定义入口点 -Wl,-e,entry_point
gcc的选项,在哪里 entry_point
是库的“主要”功能的名称。
void entry_point()
{
printf("Hello, world!\n");
}
链接器不期望链接的东西 -shared
要作为可执行文件运行,并且必须为程序提供更多信息才能运行。如果您现在尝试运行库,则会遇到分段错误。
.interp部分是操作系统运行应用程序所需的结果二进制文件的一部分。它是由链接器自动设置的 -shared
未使用。如果要构建要自行执行的共享库,则必须在C代码中手动设置此部分。看到 这个问题。
解释器的工作是查找并加载程序所需的共享库,准备程序运行,然后运行它。对于Linux上的ELF格式(普遍适用于现代* nix), ld-linux.so
使用程序。看到了 手册页 了解更多信息。
下面的行使用.interp部分中的字符串 GCC属性。将它放在库的全局范围内,以明确告诉链接器您希望在二进制文件中包含动态链接器路径。
const char interp_section[] __attribute__((section(".interp"))) = "/path/to/ld-linux";
找到路径的最简单方法 ld-linux.so
是跑 ldd
在任何正常的应用程序我系统的示例输出:
jacwah@jacob-mint17 ~ $ ldd $(which gcc)
linux-vdso.so.1 => (0x00007fff259fe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faec5939000)
/lib64/ld-linux-x86-64.so.2 (0x00007faec5d23000)
一旦你指定了解释器,你的库应该是可执行的!只有一个轻微的缺陷:它会在何时发生 entry_point
回报。
使用时编译程序 main
,它不是执行它时调用的第一个函数。 main
实际上是被另一个叫做的函数调用 _start
。此功能负责设置 argv
和 argc
和其他初始化。然后它打电话 main
。什么时候 main
回报, _start
电话 exit
具有返回值 main
。
堆栈中没有返回地址 _start
因为它是第一个被调用的函数。如果它尝试返回,则发生无效读取(最终导致分段错误)。这正是我们的入口点函数中发生的事情。添加电话 exit
作为您的输入功能的最后一行,以正确清理而不是崩溃。
example.c
#include <stdio.h>
#include <stdlib.h>
const char interp_section[] __attribute__((section(".interp"))) = "/path/to/ld-linux";
void entry_point()
{
printf("Hello, world!\n");
exit(0);
}
编译 gcc example.c -shared -fPIC -Wl,-e,entry_point
。