On 14.08.2012 14:18, Andrew Haley wrote:
You've already had an answer from Richard Henderson, who is probably
the best-placed person to answer you.
My question was: why I get wrong (from my pov) CFA value from GCC unwinder.
I rewritten my small test.
$ 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 uintptr_t _Unwind_GetIP(_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);
char *ip = (char *) _Unwind_GetIP(ctx);
uint64_t v = *(uint64_t *)(cfa - 16);
printf("cfa = %p\nra = %p\nip = %p\nv = %llx\n*cfa = %llx\n", cfa,
*(void **)(cfa - 8), ip, v, *(uint64_t *)cfa);
return 8; // _URC_CONTINUE_UNWIND
}
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:
mov $0x1020304050,%rax
push %rax
call throw
pop %rax
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 17
.uleb128 0
.byte 0xe
.uleb128 16
.align 8
.LEFDE2:
As one can see here in foo() I placed constant 0x1020304050 right after
return address to main(), e.g. *(CFA - 16) and in personality routine I
try to get it from there.
First I use standard Solaris unwinder from libc:
$ /opt/csw/bin/gas -64 foo.s
$ cc -m64 main.c foo.o
$ ./a.out
&main = 400bc0, &foo = 400c00
cfa = fffffd7fffdffb10
ra = 400bf1
ip = 400c10
v = 1020304050
*cfa = 1
And got expected value (v = 1020304050).
Then I use unwinder from libgcc_s:
$ /opt/csw/bin/gcc -m64 main.c foo.s
$ ./a.out
&main = 40186a, &foo = 401894
cfa = fffffd7fffdffb10
ra = 4018a4
ip = 4018a4
v = fffffd7fffdffb20
*cfa = 1020304050
And got something totally wrong.
In foo_personality() I expect to have the following stack layout for
foo() frame (width is 8 bytes):
: : ^
| main() frame | |
+----------------+ <--- CFA for foo() |
increasing addresses
| return address | <--- points to somewhere in main() |
+----------------+ |
| 0x1020304050 | <--- some context value stored by foo() |
+----------------+
: :
And this is what I have when using Solaris unwinder. But libgcc returned
me another CFA:
: : ^
| main() frame | |
+----------------+ | increasing addresses
| return address | <--- points to somewhere in main() |
+----------------+ |
| 0x1020304050 | <--- some context value stored by foo() |
+----------------+ <--- CFA for foo() returned by libgcc_s
: :
So the question is who is right here?
From my understanding of definition of CFA given in x86_64 psABI
Solaris unwinder is right and libgcc_s one is wrong.
See also
http://stackoverflow.com/questions/7534420/gas-explanation-of-cfi-def-cfa-offset