执行init和fini [英] executing init and fini

查看:176
本文介绍了执行init和fini的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚阅读了 init和fini部分在ELF文件中,并给它一个尝试:

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

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

$ / code>

如果我做 gcc -Wl,-init, init -Wl,-fini,fini foo.c 并运行结果,init部分不会被打印:

  $ ./a.out 
main
fini

init部分没有运行,还是无法打印?



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



man ld 说:

  -init = name 
创建一个ELF可执行文件或共享对象时,在加载可执行文件或共享对象时,调用
NAME,
将DT_INIT设置为该函数的地址。通过
的默认值,链接器使用_init作为函数来调用。

不应该这样说,只需要命名init函数 _init ? (如果我做gcc抱怨多重定义。)

解决方案

不要这样做;让你的编译器和链接器填写他们认为合适的部分。



相反,用适当的函数属性,以便编译器和链接器将它们放入正确的部分。



例如,

  static void before_main(void)__attribute __((constructor) ); 
static void after_main(void)__attribute __((destructor));

static void before_main(void)
{
/ *在main()* /
}之前运行

static void after_main (void)
{
/ *在main()返回(或者调用exit()被调用)之后运行* /
}
pre>

您也可以分配一个优先级(比如 __ 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); (p 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; $(b)



$ b void before_main(void)__attribute __((constructor(101)));
void before_main(void)
{
int saved_errno = errno;
$ b $ / *这是在main()* /
outfd = dup(STDERR_FILENO)之前运行的;
wrout(main()\\\
之前);

errno = saved_errno;


static void after_main(void)__attribute __((destructor(65535)));
static void after_main(void)
{
int saved_errno = errno;
$ b $ *这是在main()返回(或者exit()被调用)之后运行的* /
wrout(main()\\\
);

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 不变,尽管在实践中应该没有关系,并且使用低-level write() syscall将消息输出到标准错误。最初的标准错误被复制到一个新的描述符中,因为在许多情况下,标准错误本身在上一个全局析构函数 - 我们的析构函数 - 之前被关闭 - 运行。



< (一些偏执的二进制文件,通常是安全敏感的二进制文件,关闭所有他们不知道的描述符,所以你可能看不到 After main()在所有情况下的消息。)


I just read about init and fini sections in ELF files and gave it a try:

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

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

If I do gcc -Wl,-init,init -Wl,-fini,fini foo.c and run the result the "init" part is not printed:

$ ./a.out
main
fini

Did the init part not run, or was it not able to print somehow?

Is there a any "official" documentation about the init/fini stuff?

man ld says:

 -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.

Shouldn't that mean, that it would be enough to name the init function _init? (If I do gcc complains about multiple definition.)

解决方案

Don't do that; let your compiler and linker fill in the sections as they see fit.

Instead, mark your functions with the appropriate function attributes, so that the compiler and linker will put them in the correct sections.

For example,

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) */
}

You can also assign a priority (say, __attribute__((constructor (300)))), an integer between 101 and 65535, inclusive, with functions having a smaller priority number run first.

Note that for illustration, I marked the functions static. That is, the functions won't be visible outside the file scope. The functions do not need to be exported symbols to be automatically called.


For testing, I suggest saving the following in a separate file, say 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;
}

so you can compile and link it as part of any program or library. To compile it as a shared library, use e.g.

gcc -Wall -Wextra -fPIC -shared tructor.c -Wl,-soname,libtructor.so -o libtructor.so

and you can interpose it into any dynamically linked command or binary using

LD_PRELOAD=./libtructor.so some-command-or-binary

The functions keep errno unchanged, although it should not matter in practice, and use the low-level write() syscall to output the messages to standard error. The initial standard error is duplicated to a new descriptor, because in many instances, the standard error itself gets closed before the last global destructor -- our destructor here -- gets run.

(Some paranoid binaries, typically security sensitive ones, close all descriptors they don't know about, so you might not see the After main() message in all cases.)

这篇关于执行init和fini的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆