gpt4 book ai didi

c - 执行 init 和 fini

转载 作者:IT王子 更新时间:2023-10-29 00:18:33 25 4
gpt4 key购买 nike

我刚刚读到 init and fini sections在 ELF 文件中尝试一下:

#include <stdio.h>
int main(){
puts("main");
return 0;
}

void init(){
puts("init");
}
void fini(){
puts("fini");
}

如果我执行 gcc -Wl,-init,init -Wl,-fini,fini foo.c 并运行结果,则不会打印“init”部分:

$ ./a.out
main
fini

是初始化部分没有运行,还是无法打印?

是否有任何关于 init/fini 的“官方”文档?

man ld 说:

 -init=name
When creating an ELF executable or shared object, call
NAME when the executable or shared object is loaded, by
setting DT_INIT to the address of the function. By
default, the linker uses "_init" as the function to call.

这不应该意味着将初始化函数命名为 _init 就足够了吗? (如果我这样做,gcc 会提示多重定义。)

最佳答案

不要那样做;让您的编译器和链接器按照他们认为合适的方式填充这些部分。

相反,使用适当的 function attributes 标记您的函数, 以便编译器和链接器将它们放入正确的部分。

例如,

static void before_main(void) __attribute__((constructor));
static void after_main(void) __attribute__((destructor));

static void before_main(void)
{
/* This is run before main() */
}

static void after_main(void)
{
/* This is run after main() returns (or exit() is called) */
}

您还可以分配一个优先级(例如,__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;

/* This is run before main() */
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;

/* This is run after main() returns (or exit() is called) */
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() 消息例。)

关于c - 执行 init 和 fini,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32700494/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com