我目前正在使用gcc 4.1.2,我有一个用例,我想使用链接器选项来包装exit方法。让我们立即抛开所有典型的回复/评论,"为什么你甚至想要这样做?"
我想知道的是,甚至可以做到这一点。我之前使用链接器包装了其他方法,没有任何问题。以下是我使用的__wrap_exit函数的快速示例:
void __wrap_exit(int _status)
{
return;
}
当调用exit时,确实调用了这个包装器。但是,在__wrap_exit返回后,程序会立即遇到分段错误。
答案 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
它开始时相同,但只是跳过addl
,leave
和ret
指令,func1
告诉CPU如何返回执行任何称为func1
的函数。
当你偷偷地替换exit
并违反这个承诺时,指令指针可能会出现没有合理代码的地方,或者堆栈结构可能无效等等。在示例函数func2
中,函数程序集实际上在调用exit
时结束,所以当你返回并且指令指针返回时,它将指向程序映像中接下来的任何数据,甚至根本不是可执行代码