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!