I need, to write unitary tests, to wrap the abort() system call.
Here is a snippet of code:
#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;
}
Then i compile this using the following command line :
gcc -Wl,--wrap=abort,--wrap=free,--wrap=malloc -ggdb -o test test.c
Running it give the following output:
$ ./test
pre malloc: p=(nil)
allocated 40 bytes @0xd06010
post malloc p=0xd06010
pre abort
post abort
pre free
freeing @0xd06010
post free
So everything is fine. Now let's test the same code but with abort() call uncommented:
$ ./test
pre malloc: p=(nil)
allocated 40 bytes @0x1bf2010
post malloc p=0x1bf2010
pre abort
=== Abort called !===
Segmentation fault (core dumped)
I don't really understand why i get a segmentation fault while mocking abort() syscall... Every advice is welcome !
I run Debian GNU/Linux 8.5 on an x86_64 kernel. Machine is a Core i7 based laptop.
This is a continuation of the discussion under Art's answer, and is meant purely as an experiment.
Do not do this in real code!
The problem can be averted using longjmp to restore the environment, before calling the real abort.
The following program does not display undefined behavior:
Output:
In glibc (which is the libc Debian uses) the
abort
function (it's not a system call, it's a normal function) is declared like this:This bit:
__attribute__ ((__noreturn__))
is a gcc extension that tells it that the function can't return. Your wrapper function does return which the compiler didn't expect. Because of that it will crash or do something completely unexpected.Your code when compiled will be using the declarations from
stdlib.h
for the call toabort
, the flags you gave to the linker won't change that.Noreturn functions are called differently, the compiler doesn't have to preserve registers, it can just jump to the function instead of doing a proper call, it might even just not generate any code after it because that code is by definition not reachable.
Here's a simple example:
Compiled into assembler (even without optimizations):
Notice that there is a call to
noret
, but there isn't any code after this. The two calls toret
were not generated and there is noret
instruction. The function just ends. This means that if the functionnoret
actually returns because of a bug (which your implementation ofabort
has), anything can happen. In this case we'll just continue executing whatever happens to be in the code segment after us. Maybe another function, or some strings, or just zeroes, or maybe we're lucky and the memory mapping ends just after this.In fact, let's do something evil. Never do this in real code. If you ever think that this is a good idea you'll need to hand over the keys to your computer and slowly step away from the keyboard while keeping your hands up:
As I thought, the code just keeps going after whatever happened to be placed after
main
and in this simple example the compiler didn't think it would be a good idea to reorganize the functions.Nice answer, above, with the assembly output. I had the same problem, again, while creating unit tests and stubbing the abort() call - the compiler sees the __noreturn__characteristic in stdlib.h, knows it CAN stop generating code after the call to a __noreturn__ function, but GCC and other compilers DO stop generating code, even with optimization suppressed. Returns after the call to the stubbed abort() just fell through to the next function, declared data, etc. I tried the --wrap approach, above, but the calling function is just missing code after the __wrap_abort() returns.
One way I found to override this behavior is to catch the abort() declaration at the preprocessor level - keep your stubbed abort() in a separate source file, and add to the CFLAGS for the file that's calling abort()
-D__noreturn__="/* __noreturn__ */"
This modifies the effect of the declaration found in stdlib.h. Check your preprocessor output via gcc -E and verify this worked. You can also check your compiler's output via objdump of the .o file.
This whole approach will have the added side effect of generating code for source that follows other abort() calls, exit() calls, and anything else that appears in stdlib.h with the __noreturn__ characteristic, but most of us don't have code that follows an exit(), and most of us just want to clean the stack and return from the abort() caller.
You can keep the linker --wrap logic in order to invoke your __wrap_abort() call, or, since you won't be calling __real_abort(), you can do something similar to the above to get to your stubbed abort():
-Dabort=my_stubbed_abort
Hope this helps.