Inline assembly with gcc on linux
Sometimes, it's useful to test/debug at assembly level. On linux gcc provides inline assembly to directly embed assembly code into your source code. I was chasing a wierd bug in a multi thread application, the program crashed with SIGSEGV with the following information:
(gdb) bt
#0 0x00d5f3dc in pthread_join () from /lib/libpthread.so.0
#1 0x0804b51b in main (argc=140055452, argv=0x3e8) at main.cpp:214
(gdb) x/20i 0x00d5f3c0
0xd5f3c0: push %ebp
0xd5f3c1: mov %esp,%ebp
0xd5f3c3: push %edi
0xd5f3c4: push %esi
0xd5f3c5: push %ebx
0xd5f3c6: sub $0x28,%esp
0xd5f3c9: call 0xd5d4fa <__i686.get_pc_thunk.bx>
0xd5f3ce: add $0xac26,%ebx
0xd5f3d4: mov $0x3,%edi
0xd5f3d9: mov 0x8(%ebp),%eax
0xd5f3dc: mov 0x48(%eax),%ecx <------------- SIGSEGV
0xd5f3df: test %ecx,%ecx
0xd5f3e1: js 0xd5f4aa
0xd5f3e7: mov $0x16,%di
0xd5f3eb: cmp 0x200(%eax),%eax
0xd5f3f1: je 0xd5f4aa
0xd5f3f7: mov %gs:0x8,%edi
0xd5f3fe: add $0x200,%eax
0xd5f403: mov %eax,0x8(%esp)
0xd5f407: lea 0xffff53b8(%ebx),%eax
The prototype of pthread_join is this: int pthread_join(pthread_t thread, void **value_ptr); Now I couldn't remember how the parameters were passed into a subroutine on the stack, exactly what's in 0x08(%ebp)? Is it thread or value_ptr. Inline assembly to the rescue:
#include < stdio.h>
int foo(int x, int y){
unsigned long int ebp;
asm("movl %%ebp, %[ebp]" : [ebp] "=m" (ebp));
printf("$ebp=%x $x=%x $y=%x\n", ebp, &x, &y);
return x+y;
}
int main(){
int x = 3, y = 4;
foo(x, y);
}
$ ./addr_t
$ebp=bfb799c8 $x=bfb799d0 $y=bfb799d4
clearly, gcc on linux passes the paramter following the C argument passing convention, that is the arguments are pushed onto stack from right to left. The stack looks like this (top is higher memory address):
$y
$x
$ret_addr
And the prelog of pthread_join creates a stack like this:
$y
$x
$ret_addr
$ebp <--------- $ebp = $esp
It's clear now that 0x08(%ebp) is the address of argment 'thread', and apparently something went wrong and 'thread' contains an invalid memory address in the crashed application.
(gdb) bt
#0 0x00d5f3dc in pthread_join () from /lib/libpthread.so.0
#1 0x0804b51b in main (argc=140055452, argv=0x3e8) at main.cpp:214
(gdb) x/20i 0x00d5f3c0
0xd5f3c0
0xd5f3c1
0xd5f3c3
0xd5f3c4
0xd5f3c5
0xd5f3c6
0xd5f3c9
0xd5f3ce
0xd5f3d4
0xd5f3d9
0xd5f3dc
0xd5f3df
0xd5f3e1
0xd5f3e7
0xd5f3eb
0xd5f3f1
0xd5f3f7
0xd5f3fe
0xd5f403
0xd5f407
The prototype of pthread_join is this: int pthread_join(pthread_t thread, void **value_ptr); Now I couldn't remember how the parameters were passed into a subroutine on the stack, exactly what's in 0x08(%ebp)? Is it thread or value_ptr. Inline assembly to the rescue:
#include < stdio.h>
int foo(int x, int y){
unsigned long int ebp;
asm("movl %%ebp, %[ebp]" : [ebp] "=m" (ebp));
printf("$ebp=%x $x=%x $y=%x\n", ebp, &x, &y);
return x+y;
}
int main(){
int x = 3, y = 4;
foo(x, y);
}
$ ./addr_t
$ebp=bfb799c8 $x=bfb799d0 $y=bfb799d4
clearly, gcc on linux passes the paramter following the C argument passing convention, that is the arguments are pushed onto stack from right to left. The stack looks like this (top is higher memory address):
$y
$x
$ret_addr
And the prelog of pthread_join creates a stack like this:
$y
$x
$ret_addr
$ebp <--------- $ebp = $esp
It's clear now that 0x08(%ebp) is the address of argment 'thread', and apparently something went wrong and 'thread' contains an invalid memory address in the crashed application.
<< Home