不要这样做;让你的编译器和链接器根据需要填充部分。
相反,用适当的函数属性标记你的函数,以便编译器和链接器将它们放到正确的部分中。
例如,
static void before_main(void) __attribute__((constructor));
static void after_main(void) __attribute__((destructor));
static void before_main(void)
{
}
static void after_main(void)
{
}
您还可以为函数指定优先级(例如:__attribute__((constructor (300)))
),取值范围为101到65535之间的整数,其中优先级较低的函数将先执行。
请注意,我在示例中将函数标记为static
。也就是说,在文件作用域之外不会看到这些函数。这些函数不需要导出符号即可自动调用。
为了测试,建议将以下内容保存在单独的文件中,例如tructor.c
:
#include <unistd.h>
#include <string.h>
#include <errno.h>
static int outfd = -1;
static void wrout(const char *const string)
{
if (string && *string && outfd != -1) {
const char *p = string;
const char *const q = string + strlen(string);
while (p < q) {
ssize_t n = write(outfd, p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1 || errno != EINTR)
break;
}
}
}
void before_main(void) __attribute__((constructor (101)));
void before_main(void)
{
int saved_errno = errno;
outfd = dup(STDERR_FILENO);
wrout("Before main()\n");
errno = saved_errno;
}
static void after_main(void) __attribute__((destructor (65535)));
static void after_main(void)
{
int saved_errno = errno;
wrout("After main()\n");
errno = saved_errno;
}
因此,您可以将其编译并链接为任何程序或库的一部分。要将其编译为共享库,请使用例如
gcc -Wall -Wextra -fPIC -shared tructor.c -Wl,-soname,libtructor.so -o libtructor.so
你可以使用它将其插入到任何动态链接的命令或二进制文件中
LD_PRELOAD=./libtructor.so some-command-or-binary
这些函数保持 errno
不变,尽管在实践中这不重要,并使用低级别的 write()
系统调用将消息输出到标准错误。初始标准错误被复制到一个新描述符,因为在许多情况下,在最后一个全局析构函数——也就是我们的析构函数运行之前——标准错误本身会被关闭。
(一些偏执的二进制文件,通常是安全敏感的,会关闭它们不知道的所有描述符,所以在某些情况下可能看不到 After main()
消息。)