x86_64 unwinder in libgcc_s

Dmitri Shubin sbn@tbricks.com
Tue Aug 14 13:14:00 GMT 2012


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



More information about the Gcc mailing list