libunwind-devel
[Top][All Lists]
Advanced

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

[Libunwind-devel] libunwind-setjmp and alloca


From: Taahir Ahmed
Subject: [Libunwind-devel] libunwind-setjmp and alloca
Date: Sun, 22 Feb 2015 23:09:11 -0600
User-agent: KMail/4.14.3 (Linux/3.16.5-gentoo; KDE/4.14.3; x86_64; ; )

I'm currently preparing a setjmp/longjmp replacement based on libunwind
(specifically, I'm trying make GNU guile's exceptions play nicely with
c++).

My approach is heavily based on libunwind's bundled set/longjmp
replacement, but I've run into an edge case (offending program below the 
break) involving alloca that I'm having a little trouble working around.

Basically, if the function that calls setjmp later calls alloca(), its
stack pointer is modified, and the replacement longjmp no longer finds
an exact match for its target.

I have a couple of ideas for working around this:

  1) Match based on frame pointer -- unfortunately, at least on x86_64,
     guile (and most other programs) is compiled with
     -fomit-frame-pointer.

  2) Match based on a range -- alloca() can't make the stack frame
     smaller , so, when walking with libunwind, test if the target stack
     pointer is between the current stack pointer and the previous stack
     pointer.  This seems like it should work, but it seems rather
     hackish.

  3) Rewrite the setjmp() caller to not use alloca.  This is doable
     (it's only about five sites in the Guile codebase), but introduces
     a magic rule that WILL cause pain later when someone accidentally
     adds an offending alloca back in.

Is there some fiendishly clever trick that I'm overlooking, or should I
go with option 2?

Thanks,

Taahir Ahmed

----------%<--------------------
/* test-exception-jump.c */

#define UNW_LOCAL_ONLY
#include <libunwind.h>

#include <alloca.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
  unw_word_t sp;
  unw_word_t rp;
} eh_jmp_buf;

int eh_setjmp_impl(eh_jmp_buf *env) __attribute__((noinline));
void eh_longjmp(eh_jmp_buf env, int val) __attribute__((noinline));

#define eh_setjmp(env) (eh_setjmp_impl(&env))

int eh_setjmp_impl(eh_jmp_buf *env)
{
  unw_context_t context;
  unw_cursor_t cursor;

  if(unw_getcontext(&context) < 0 || unw_init_local(&cursor, &context) < 0)
    abort();

  if(unw_step(&cursor) <= 0)
    abort();

  if(unw_get_reg(&cursor, UNW_REG_SP, &(env->sp)) < 0)
    abort();

  env->rp = (unw_word_t) 
__builtin_extract_return_addr(__builtin_return_address(0));
  return 0;
}

void eh_longjmp(eh_jmp_buf env, int val)
{
  extern int eh_longjmp_landing;
  unw_context_t context;
  unw_cursor_t cursor;
  unw_word_t cur_sp;

  if(unw_getcontext(&context) < 0 || unw_init_local(&cursor, &context) < 0)
  {
    abort();
  }

  do
    {
      if(unw_get_reg(&cursor, UNW_REG_SP, &cur_sp))
        {
          abort();
        }

      if(cur_sp != env.sp)
        {
          fprintf(stderr, "%p (cur) %p (tar)\n", (void*)cur_sp,(void*)env.sp);

          /* TODO: Invoke personality routine. */
          continue;
        }

      /* This is true on all currently-supported libunwind platforms. */
      assert (UNW_NUM_EH_REGS >= 2);

      if(unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)&eh_longjmp_landing) < 0
         || unw_set_reg (&cursor, UNW_REG_EH + 0, env.rp) < 0
         || unw_set_reg (&cursor, UNW_REG_EH + 1, val) < 0)
        {
          abort();
        }

      unw_resume(&cursor);

      abort();
    }
  while(unw_step(&cursor) > 0);

  fprintf(stderr, "Ran out of stack.\n");
  abort();
}

#ifdef __amd64__

__asm__ (
         ".globl eh_longjmp_landing                      \n\t"
         ".type eh_longjmp_landing, @function            \n\t"
         "eh_longjmp_landing:                            \n\t"
         "push %rax                                      \n\t"
         "mov %rdx, %rax                                 \n\t"
         "retq                                           \n\t"
         ".size eh_longjmp_landing, .-eh_longjmp_landing \n\t"
);

#else
#error "You need to implement eh_longjmp_landing for your architecture."
#endif

int make_some_stack(eh_jmp_buf *env, int counter)
{
  if(counter > 0)
    {
      make_some_stack(env, counter - 1);
    }
  else
    {
      eh_longjmp(*env, 1);
    }
}

int main(int argc, char **argv)
{
  eh_jmp_buf env;
  if(eh_setjmp(env))
    {
      fprintf(stderr, "Jump succeeded.\n");
      return 0;
    }
  else
    {
      volatile void* a = alloca(183);
      fprintf(stderr, "eh_setjmp returned 0. %p \n", a);
      return make_some_stack(&env, 5);
    }

  return 1;
}

Attachment: signature.asc
Description: This is a digitally signed message part.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]