是否可以成功包装退出方法?

时间:2014-07-01 21:10:37

标签: c++ c gcc linker exit

我目前正在使用gcc 4.1.2,我有一个用例,我想使用链接器选项来包装exit方法。让我们立即抛开所有典型的回复/评论,"为什么你甚至想要这样做?"

我想知道的是,甚至可以做到这一点。我之前使用链接器包装了其他方法,没有任何问题。以下是我使用的__wrap_exit函数的快速示例:

void __wrap_exit(int _status)
{
   return;
}

当调用exit时,确实调用了这个包装器。但是,在__wrap_exit返回后,程序会立即遇到分段错误。

  • gcc是否会在幕后做一些事情,比如在退出时发出信号 即使没有调用真正的退出函数,也会调用它 包装器版本是?
  • 可以了解背后发生的事情 还提供场景?

1 个答案:

答案 0 :(得分:4)

gcc有一个名为__noreturn__的语言扩展属性。在<stdlib.h>中,您可能会声明如下内容:

extern void exit (int __status) __THROW __attribute__ ((__noreturn__));

此属性表示函数永远不会以正常方式返回的承诺。 (也许它结束了这个过程,或者它可能是longjmp,或者它可能抛出一个C ++异常,或者它可能有一个无休止的循环...)

因此,在编译调用exit的代码时,gcc可以进行一些优化,例如可能不需要设置和/或清理堆栈指针以使其可以返回到调用函数。

例如,这是一个调用库函数的简单函数,然后是x86上的汇编,没有指定-O标志。

void func1(void) {
    srand(1);
}

.globl func1
        .type   func1,@function
func1:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $12, %esp
        pushl   $1
        call    srand
        addl    $16, %esp
        leave
        ret

一个几乎相同的函数,而不是调用exit

void func2(void) {
    exit(0);
}

.globl func2
        .type   func2,@function
func2:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $12, %esp
        pushl   $0
        call    exit

它开始时相同,但只是跳过addlleaveret指令,func1告诉CPU如何返回执行任何称为func1的函数。

当你偷偷地替换exit并违反这个承诺时,指令指针可能会出现没有合理代码的地方,或者堆栈结构可能无效等等。在示例函数func2中,函数程序集实际上在调用exit时结束,所以当你返回并且指令指针返回时,它将指向程序映像中接下来的任何数据,甚至根本不是可执行代码