longjmp question

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

Over the last few days I had some quality time trying to track down 
the segfault in one of Ruby tests, involving continuations (which are 
implemented on top of setjmp/longjmp):

http://redmine.ruby-lang.org/issues/5244

After lots of experiments I came to a conclusion that something is 
wrong with libc (eglibc in this case) implementation of longjmp. In 
particular, this code from sysdeps/sparc/sparc32/__longjmp.S is used 
to perform the longjmp in the Ruby test:

[...]
#define RW_FP [%fp + 0x48]

ENTRY(__longjmp)
        /* Store our arguments in global registers so we can still
           use them while unwinding frames and their register windows.  */

        ld ENV(o0,JB_FP), %g3   /* Cache target FP in register %g3.  */
#ifdef PTR_DEMANGLE
        PTR_DEMANGLE (%g3, %g3, %g4)
#endif
[...]
LOC(thread):
        /*
         * Do a "flush register windows trap".  The trap handler in the
         * kernel writes all the register windows to their stack slots, and
         * marks them all as invalid (needing to be sucked up from the
         * stack when used).  This ensures that all information needed to
         * unwind to these callers is in memory, not in the register
         * windows.
         */
        ta      ST_FLUSH_WINDOWS
#ifdef PTR_DEMANGLE
        ld      ENV(g1,JB_PC), %g5 /* Set return PC. */
        ld      ENV(g1,JB_SP), %g1 /* Set saved SP on restore below. */
        PTR_DEMANGLE2 (%o7, %g5, %g4)
        PTR_DEMANGLE2 (%fp, %g1, %g4)
#else
        ld      ENV(g1,JB_PC), %o7 /* Set return PC. */
        ld      ENV(g1,JB_SP), %fp /* Set saved SP on restore below. */
#endif
        sub     %fp, 64, %sp    /* Allocate a register frame. */
        st      %g3, RW_FP      /* Set saved FP on restore below. */
        retl
         restore %g2, 0, %o0    /* Restore values from above register frame. */

At the end of the procedure %g3 contains the target (post-jump) frame 
pointer address, which we would like to end up in %fp as a result of 
restore instruction in the retl delay slot. To that end we write it to 
a location determined by RW_FP, which is [%fp + 0x48]. However, 
according to http://www.sics.se/~psm/sparcstack.html, which nicely 
describes the layout of registers in memory and the effect of 
save/restore operations, the post-restore frame pointer value should 
be located at [%fp + 0x38], as we are only saving 16 word-sized 
registers, and %fp is 15-th out of them (see Figure 4), so offset 
should be 14 * 4 == 56 == 0x38. I'm not sure whether if that's just an 
off-by-0x10, or I don't understand how this things work - anyone has 
any ideas?
 
There are a lot of things here I don't understand. For example, Ruby 
test only fails if the code is compiled with -O2, but works with -O0,
and with -O0 we get the correct value of target frame pointer at
[%fp + 0x38], so it does not matter whether we write another one at
the "wrong" location. With -O2 though the memory layout is different, 
and we end up with broken value at [%fp + 0x38], and this is what gets 
restored into %fp, eventually causing a segfault. Also, it's probably 
not the whole story, because after changing the offset to 0x38 I still 
get a test failure, but it now segfaults in a different place. I could 
not, however, explain the 0x48 vs 0x38 discrepancy, so taking things 
one step at a time here :-).

Best regards,
-- 
Jurij Smakov                                           jurij@xxxxxxxxx
Key: http://www.wooyd.org/pgpkey/                      KeyID: C99E03CC
--
To unsubscribe from this list: send the line "unsubscribe sparclinux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Kernel Development]     [DCCP]     [Linux ARM Development]     [Linux]     [Photo]     [Yosemite Help]     [Linux ARM Kernel]     [Linux SCSI]     [Linux x86_64]     [Linux Hams]

  Powered by Linux