bin*_*rym 1 c linux debugging mocking abort
我需要编写单一测试来包装abort()系统调用.
这是一段代码:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
extern void __real_abort(void);
extern void * __real_malloc(int c);
extern void __real_free(void *);
void __wrap_abort(void)
{
printf("=== Abort called !=== \n");
}
void * __wrap_malloc(int s)
{
void *p = __real_malloc(s);
printf("allocated %d bytes @%p\n",s, (void *)p);
return p;
}
void __wrap_free(void *p)
{
printf("freeing @%p\n",(void *)p);
return __real_free((void *)p);
}
int main(int ac, char **av)
{
char *p = NULL;
printf("pre malloc: p=%p\n",p);
p = malloc(40);
printf("post malloc p=%p\n",p);
printf("pre abort\n");
//abort();
printf("post abort\n");
printf("pre free\n");
free(p);
printf("post free\n");
return -1;
}
Run Code Online (Sandbox Code Playgroud)
然后我使用以下命令行编译它:
gcc -Wl,--wrap=abort,--wrap=free,--wrap=malloc -ggdb -o test test.c
Run Code Online (Sandbox Code Playgroud)
运行它会给出以下输出:
$ ./test
pre malloc: p=(nil)
allocated 40 bytes @0xd06010
post malloc p=0xd06010
pre abort
post abort
pre free
freeing @0xd06010
post free
Run Code Online (Sandbox Code Playgroud)
一切都很好.现在让我们测试相同的代码,但使用abort()调用取消注释:
$ ./test
pre malloc: p=(nil)
allocated 40 bytes @0x1bf2010
post malloc p=0x1bf2010
pre abort
=== Abort called !===
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)
我真的不明白为什么在模拟abort()系统调用时出现分段错误...欢迎提出建议!
我在x86_64内核上运行Debian GNU/Linux 8.5.Machine是基于Core i7的笔记本电脑.
在glibc(这是libc Debian使用的)abort函数(它不是系统调用,它是一个普通的函数)声明如下:
extern void abort (void) __THROW __attribute__ ((__noreturn__));
Run Code Online (Sandbox Code Playgroud)
这一位:__attribute__ ((__noreturn__))是一个gcc扩展,告诉它函数不能返回.您的包装函数确实返回编译器没有预料到的.因为它会崩溃或做一些完全出乎意料的事情.
编译时的代码将使用stdlib.h调用的声明abort,你给链接器的标志不会改变它.
Noreturn函数的调用方式不同,编译器不必保留寄存器,它只能跳转到函数而不是正确调用,它甚至可能不会生成任何代码,因为根据定义,代码是不可访问的.
这是一个简单的例子:
extern void ret(void);
extern void noret(void) __attribute__((__noreturn__));
void
foo(void)
{
ret();
noret();
ret();
ret();
}
Run Code Online (Sandbox Code Playgroud)
编译成汇编程序(即使没有优化):
$ cc -S foo.c
$ cat foo.s
[...]
foo:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
call ret
call noret
.cfi_endproc
.LFE0:
.size foo, .-foo
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-4)"
.section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)
请注意,有一个调用noret,但此后没有任何代码.ret没有生成两个调用,也没有ret指令.功能刚刚结束.这意味着如果函数noret实际上由于一个bug(你的实现abort有)而返回,那么任何事情都可能发生.在这种情况下,我们将继续执行在我们之后的代码段中发生的任何事情.也许是另一个函数,或者一些字符串,或者只是零,或者我们很幸运,内存映射在此之后结束.
事实上,让我们做一些邪恶的事情.永远不要在实际代码中这样做.如果你认为这是一个好主意,你需要将钥匙交给你的电脑,然后慢慢离开键盘,同时保持双手:
$ cat foo.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void __wrap_abort(void)
{
printf("=== Abort called !=== \n");
}
int
main(int argc, char **argv)
{
abort();
return 0;
}
void
evil(void)
{
printf("evil\n");
_exit(17);
}
$ gcc -Wl,--wrap=abort -o foo foo.c && ./foo
=== Abort called !===
evil
$ echo $?
17
Run Code Online (Sandbox Code Playgroud)
正如我想的那样,代码只是继续发生之后发生的事情main,在这个简单的例子中,编译器认为重新组织函数不是一个好主意.
| 归档时间: |
|
| 查看次数: |
275 次 |
| 最近记录: |