This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[x86-64] An unreliable backtrace().


Hi,

In glibc-2.4 the x86-64/ia64 backtrace_helper() uses _Unwind_GetIP() to fill
the backtrace array. In fact _Unwind_GetIP retrieves the return address which
isn't a caller address. A backtrace is a summary of how your program got where
it is. Currently glibc-2.4 and gdb-6.5 (at least for x86-64) produces
an unwind path instead of true backtrace.

Please consider attached testcase.

$ gcc -Wall -ggdb -rdynamic --save-temps -O1  backTrace.c -o backTrace

Let's see a quick comparision.

(gdb) bt
#0  libcBackTrace () at backTrace.c:39
#1  0x0000000000400a7f in zoo () at backTrace.c:52
#2  0x00000000004009ce in bar (f=0x400a71 <zoo>) at backTrace.c:58
#3  0x00000000004009e1 in foo () at backTrace.c:63
#4  0x00000000004009f4 in main () at backTrace.c:68

(gdb) c
libc backtrace:
ip = 0x400a11
ip = 0x400a7f
ip = 0x4009ce
ip = 0x4009e1
ip = 0x4009f4
ip = 0x2b58886f1134
ip = 0x4008f9

(gdb) set listsize 1
(gdb) l *0x400a11
0x400a11 is in libcBackTrace (backTrace.c:41).
41              int n = backtrace( array, 64 );   <= wrong, 0x400a11 != call
(gdb) l *0x400a7f
0x400a7f is in zoo (backTrace.c:53).
53              myBackTrace();                    <= wrong, 0x400a7f != call
(gdb) l *0x4009ce
0x4009ce is in bar (backTrace.c:59).
59      }                                         <= wrong, 0x4009ce != ret
(gdb) l *0x4009e1
0x4009e1 is in foo (backTrace.c:64).
64      }                                         <= wrong, 0x4009e1 != ret
(gdb) l *0x4009f4
0x4009f4 is in main (backTrace.c:70).
70      }                                         <= wrong, 0x4009f4 != ret

We can see that libc and gdb print bogus backtrace :(

00000000004009c3 <bar>:
  4009c3:       48 83 ec 08             sub    $0x8,%rsp
  4009c7:       b8 00 00 00 00          mov    $0x0,%eax
  4009cc:       ff d7                   callq  *%rdi
  4009ce:       48 83 c4 08             add    $0x8,%rsp
  4009d2:       c3                      retq

00000000004009d3 <foo>:
  4009d3:       48 83 ec 08             sub    $0x8,%rsp
  4009d7:       bf 71 0a 40 00          mov    $0x400a71,%edi
  4009dc:       e8 e2 ff ff ff          callq  4009c3 <bar>
  4009e1:       48 83 c4 08             add    $0x8,%rsp
  4009e5:       c3                      retq

00000000004009e6 <main>:
  4009e6:       48 83 ec 08             sub    $0x8,%rsp
  4009ea:       b8 00 00 00 00          mov    $0x0,%eax
  4009ef:       e8 df ff ff ff          callq  4009d3 <foo>
  4009f4:       b8 00 00 00 00          mov    $0x0,%eax
  4009f9:       48 83 c4 08             add    $0x8,%rsp
  4009fd:       c3                      retq

00000000004009fe <libcBackTrace>:
  4009fe:       41 54                   push   %r12
  400a00:       55                      push   %rbp
  400a01:       53                      push   %rbx
  400a02:       be 40 00 00 00          mov    $0x40,%esi
  400a07:       bf 60 12 60 00          mov    $0x601260,%edi
  400a0c:       e8 67 fe ff ff          callq  400878 <backtrace@plt>
  400a11:       41 89 c4                mov    %eax,%r12d
  400a14:       bf ac 0b 40 00          mov    $0x400bac,%edi
  400a19:       e8 6a fe ff ff          callq  400888 <puts@plt>
  400a1e:       45 85 e4                test   %r12d,%r12d
  400a21:       7e 27                   jle    400a4a <libcBackTrace+0x4c>
  400a23:       bd 00 00 00 00          mov    $0x0,%ebp
  400a28:       bb 60 12 60 00          mov    $0x601260,%ebx
  400a2d:       48 8b 33                mov    (%rbx),%rsi
  400a30:       bf bc 0b 40 00          mov    $0x400bbc,%edi
  400a35:       b8 00 00 00 00          mov    $0x0,%eax
  400a3a:       e8 29 fe ff ff          callq  400868 <printf@plt>
  400a3f:       ff c5                   inc    %ebp
  400a41:       48 83 c3 08             add    $0x8,%rbx
  400a45:       41 39 ec                cmp    %ebp,%r12d
  400a48:       75 e3                   jne    400a2d <libcBackTrace+0x2f>
  400a4a:       5b                      pop    %rbx
  400a4b:       5d                      pop    %rbp
  400a4c:       41 5c                   pop    %r12
  400a4e:       c3                      retq

0000000000400a4f <myBackTrace>:
  400a4f:       48 83 ec 08             sub    $0x8,%rsp
  400a53:       bf c5 0b 40 00          mov    $0x400bc5,%edi
  400a58:       e8 2b fe ff ff          callq  400888 <puts@plt>
  400a5d:       be 00 00 00 00          mov    $0x0,%esi
  400a62:       bf 8e 0a 40 00          mov    $0x400a8e,%edi
  400a67:       e8 4c fe ff ff          callq  4008b8 <_Unwind_Backtrace@plt>
  400a6c:       48 83 c4 08             add    $0x8,%rsp
  400a70:       c3                      retq

0000000000400a71 <zoo>:
  400a71:       48 83 ec 08             sub    $0x8,%rsp
  400a75:       b8 00 00 00 00          mov    $0x0,%eax
  400a7a:       e8 7f ff ff ff          callq  4009fe <libcBackTrace>
  400a7f:       b8 00 00 00 00          mov    $0x0,%eax
  400a84:       e8 c6 ff ff ff          callq  400a4f <myBackTrace>
  400a89:       48 83 c4 08             add    $0x8,%rsp
  400a8d:       c3                      retq

With a little hack in backtrace helper we can extract the true caller
address from return point address.

(gdb) c
my backtrace:
ip = 0x400a67
ip = 0x400a84
ip = 0x4009cc
ip = 0x4009dc
ip = 0x4009ef
ip = 0x2b58886f1134
ip = 0x4008f4

(gdb) l *0x400a67
0x400a67 is in myBackTrace (backTrace.c:35).
35              _Unwind_Backtrace( backTraceHelper, 0 );
(gdb) l *0x400a84
0x400a84 is in zoo (backTrace.c:53).
53              myBackTrace();
(gdb) l *0x4009cc
0x4009cc is in bar (backTrace.c:58).
58              f();
(gdb) l *0x4009dc
0x4009dc is in foo (backTrace.c:63).
63              bar( &zoo );
(gdb) l *0x4009ef
0x4009ef is in main (backTrace.c:68).
68              foo();

Yup, It looks better now.

BR,
PaweÅ.
CXX		:= gcc
CXXFLAGS	:= -Wall -ggdb -rdynamic --save-temps -O1 

all: backTrace.c
	$(CXX) $(CXXFLAGS) backTrace.c -o backTrace

clean:
	rm -f *.s *.i* *.o backTrace
#include <execinfo.h>
#include <stdio.h>
#include <string.h>
#include <unwind.h>

long callerAddress( long ip )
{
#if defined( __x86_64__ )
	unsigned char const* code = (unsigned char const*)ip;
// call, near, relative
	if ( code[ -5 ] == 0xe8 )
		return ( ip - 5 );
// call, near, absolute indirect
	if ( code[ -2 ] == 0xff )
	{
		if ( ( code[ -1 ] & 0xf8 ) == 0xd0 ) // call *%reg
			return ( ip - 2 );
		if ( ( code[ -1 ] & 0xf8 ) == 0x10 ) // call *(%reg)
			return ( ip - 2 );
	}
#endif
	return ip;
}

_Unwind_Reason_Code backTraceHelper( struct _Unwind_Context *ctx, void *traceArgs )
{
	_Unwind_Ptr ip = _Unwind_GetIP( ctx );
	printf( "ip = 0x%lx\n", callerAddress( (long)ip ) );
	return _URC_NO_REASON;
}

void myBackTrace()
{
	puts( "my backtrace:" );
	_Unwind_Backtrace( backTraceHelper, 0 );
}

void libcBackTrace()
{
	static void* array[ 64 ];
	int n = backtrace( array, 64 );
	int i = 0;
	puts( "libc backtrace:" );
	for ( ; i < n; i++ )
		printf( "ip = %p\n", array[ i ] );
}

typedef void ( *fn )();

void zoo()
{
	libcBackTrace();
	myBackTrace();
}

void bar( fn f )
{
	f();
}

void foo()
{
	bar( &zoo );
}

int main()
{
	foo();
	return 0;
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]