x86_64 unwinder in libgcc_s

Dmitri Shubin sbn@tbricks.com
Mon Aug 6 15:24:00 GMT 2012


On 02.08.2012 20:05, Dmitri Shubin wrote:
> Hello!
>
> I got strange problem in LuaJIT [1] stack unwinding on Solaris x64.

I wrote minimal test that reproduces the problem:

$ cat main.c
#include <stdio.h>
#include <stdint.h>

typedef struct _Unwind_Exception
{
     uint64_t exclass;
     void (*excleanup)(int, struct _Unwind_Exception);
     uintptr_t p1, p2;
} __attribute__((__aligned__)) _Unwind_Exception;

typedef struct _Unwind_Context _Unwind_Context;

extern uintptr_t _Unwind_GetCFA(_Unwind_Context *);
extern int _Unwind_RaiseException(_Unwind_Exception *);

int
foo_personality(int version, int actions, uint64_t uexclass, 
_Unwind_Exception *uex, _Unwind_Context *ctx)
{
     char *cfa = (char *) _Unwind_GetCFA(ctx);
     printf("cfa = %p\nra = %p\n", cfa, *(void **)(cfa - 8));
}

void
throw()
{
     static _Unwind_Exception static_uex;
     static_uex.exclass = 0x0102030405060708ULL;
     static_uex.excleanup = NULL;
     _Unwind_RaiseException(&static_uex);
}

extern void foo(void);

int
main()
{
     printf("&main = %p, &foo = %p\n", &main, &foo);
     foo();
     return 0;
}
$ cat foo.s
         .file   "foo.s"
         .text
.globl foo
         .type   foo, @function
foo:
         call    throw
         ret
         .size   foo, .-foo

         .section .eh_frame,"a",@unwind
.Lframe1:
         .long .LECIE1-.LSCIE1
.LSCIE1:
         .long 0
         .byte 0x1
         .string "zPR"
         .uleb128 0x1
         .sleb128 -8
         .byte 0x10
         .uleb128 6
         .byte 0x1b
         .long foo_personality-.
         .byte 0x1b
         .byte 0xc
         .uleb128 0x7
         .uleb128 8
         .byte 0x80+0x10
         .uleb128 0x1
         .align 8
.LECIE1:

.LSFDE2:
         .long .LEFDE2-.LASFDE2
.LASFDE2:
         .long .LASFDE2-.Lframe1
         .long foo-.
         .long 6
         .uleb128 0
         .align 8
.LEFDE2:

First I use GCC (4.7.1 from OpenCSW) to compile both files:

$ /opt/csw/bin/gcc --version | head -1
gcc (GCC) 4.7.1
$ /opt/csw/bin/gcc -m64 main.c foo.s
$ ./a.out
&main = 401834, &foo = 401860
cfa = fffffd7fffdffaf8
ra = 401865
$ gobjdump -dr a.out
...
0000000000401860 <foo>:
   401860:       e8 a0 ff ff ff          callq  401805 <throw>
   401865:       c3                      retq
         ...

AFAIU here GCC supplied stack unwinder from statically linked libgcc_s 
is used and return address for foo context points to somewhere inside foo()

Second I use GCC (gas in fact) to compile foo.s and SunStudio C to 
compile main.c and link:
$ cc -V 2>&1 | head -1
cc: Sun C 5.11 SunOS_i386 145355-03 2011/02/11
$ /opt/csw/bin/gcc -m64 -c foo.s
$ cc -m64 main.c foo.o
$ ./a.out
&main = 400b30, &foo = 400b70
cfa = fffffd7fffdffaf0
ra = 400b61
$ gobjdump -dr a.out
...
0000000000400b30 <main>:
   400b30:       55                      push   %rbp
   400b31:       48 8b ec                mov    %rsp,%rbp
   400b34:       48 83 ec 10             sub    $0x10,%rsp
   400b38:       48 c7 c2 70 0b 40 00    mov    $0x400b70,%rdx
   400b3f:       48 c7 c6 30 0b 40 00    mov    $0x400b30,%rsi
   400b46:       48 c7 c7 e0 0b 40 00    mov    $0x400be0,%rdi
   400b4d:       b8 00 00 00 00          mov    $0x0,%eax
   400b52:       e8 49 fd ff ff          callq  4008a0 <printf@plt>
   400b57:       b8 00 00 00 00          mov    $0x0,%eax
   400b5c:       e8 0f 00 00 00          callq  400b70 <foo>
   400b61:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
   400b68:       8b 45 fc                mov    -0x4(%rbp),%eax
   400b6b:       c9                      leaveq
   400b6c:       c3                      retq
   400b6d:       00 00                   add    %al,(%rax)
         ...

In this case standard Solaris unwinder from libc is used and return 
address points to main()

 From my understanding of CFA definition given in x86-64 psABI ("the 
value of %rsp at the call site in the previous frame." 6.2.5 Context 
Management) it looks like value returned by GCC unwinder is wrong.
Am I missing something here?

BTW on Linux (w/ GCC) RA points to foo() as in case 1 so it's 
GCC-specific rather than Solaris-specific behavior.

Thanks!



More information about the Gcc mailing list