Imported Upstream version 0.165
[platform/upstream/elfutils.git] / libdwfl / frame_unwind.c
index 16cebd0..0e470b9 100644 (file)
@@ -101,6 +101,49 @@ bra_compar (const void *key_voidp, const void *elem_voidp)
   return (offset > op->offset) - (offset < op->offset);
 }
 
+struct eval_stack {
+  Dwarf_Addr *addrs;
+  size_t used;
+  size_t allocated;
+};
+
+static bool
+do_push (struct eval_stack *stack, Dwarf_Addr val)
+{
+  if (stack->used >= DWARF_EXPR_STACK_MAX)
+    {
+      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+      return false;
+    }
+  if (stack->used == stack->allocated)
+    {
+      stack->allocated = MAX (stack->allocated * 2, 32);
+      Dwarf_Addr *new_addrs;
+      new_addrs = realloc (stack->addrs,
+                          stack->allocated * sizeof (*stack->addrs));
+      if (new_addrs == NULL)
+        {
+          __libdwfl_seterrno (DWFL_E_NOMEM);
+          return false;
+        }
+      stack->addrs = new_addrs;
+    }
+  stack->addrs[stack->used++] = val;
+  return true;
+}
+
+static bool
+do_pop (struct eval_stack *stack, Dwarf_Addr *val)
+{
+  if (stack->used == 0)
+    {
+      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+      return false;
+    }
+  *val = stack->addrs[--stack->used];
+  return true;
+}
+
 /* If FRAME is NULL is are computing CFI frame base.  In such case another
    DW_OP_call_frame_cfa is no longer permitted.  */
 
@@ -114,43 +157,15 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
       __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
       return false;
     }
-  Dwarf_Addr *stack = NULL;
-  size_t stack_used = 0, stack_allocated = 0;
+  struct eval_stack stack =
+    {
+      .addrs = NULL,
+      .used = 0,
+      .allocated = 0
+    };
 
-  bool
-  push (Dwarf_Addr val)
-  {
-    if (stack_used >= DWARF_EXPR_STACK_MAX)
-      {
-       __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
-       return false;
-      }
-    if (stack_used == stack_allocated)
-      {
-       stack_allocated = MAX (stack_allocated * 2, 32);
-       Dwarf_Addr *stack_new = realloc (stack, stack_allocated * sizeof (*stack));
-       if (stack_new == NULL)
-         {
-           __libdwfl_seterrno (DWFL_E_NOMEM);
-           return false;
-         }
-       stack = stack_new;
-      }
-    stack[stack_used++] = val;
-    return true;
-  }
-
-  bool
-  pop (Dwarf_Addr *val)
-  {
-    if (stack_used == 0)
-      {
-       __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
-       return false;
-      }
-    *val = stack[--stack_used];
-    return true;
-  }
+#define pop(x) do_pop(&stack, x)
+#define push(x) do_push(&stack, x)
 
   Dwarf_Addr val1, val2;
   bool is_location = false;
@@ -168,14 +183,14 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        case DW_OP_lit0 ... DW_OP_lit31:
          if (! push (op->atom - DW_OP_lit0))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
        case DW_OP_addr:
          if (! push (op->number + bias))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
@@ -195,7 +210,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        case DW_OP_consts:
          if (! push (op->number))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
@@ -203,67 +218,67 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
          if (! state_get_reg (state, op->atom - DW_OP_reg0, &val1)
              || ! push (val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
        case DW_OP_regx:
          if (! state_get_reg (state, op->number, &val1) || ! push (val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
        case DW_OP_breg0 ... DW_OP_breg31:
          if (! state_get_reg (state, op->atom - DW_OP_breg0, &val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          val1 += op->number;
          if (! push (val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
        case DW_OP_bregx:
          if (! state_get_reg (state, op->number, &val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          val1 += op->number2;
          if (! push (val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
        case DW_OP_dup:
          if (! pop (&val1) || ! push (val1) || ! push (val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
        case DW_OP_drop:
          if (! pop (&val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
        case DW_OP_pick:
-         if (stack_used <= op->number)
+         if (stack.used <= op->number)
            {
-             free (stack);
+             free (stack.addrs);
              __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
              return false;
            }
-         if (! push (stack[stack_used - 1 - op->number]))
+         if (! push (stack.addrs[stack.used - 1 - op->number]))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
@@ -271,14 +286,14 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
          if (! pop (&val1) || ! pop (&val2)
              || ! push (val2) || ! push (val1) || ! push (val2))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
        case DW_OP_swap:
          if (! pop (&val1) || ! pop (&val2) || ! push (val1) || ! push (val2))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
@@ -288,7 +303,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
            if (! pop (&val1) || ! pop (&val2) || ! pop (&val3)
                || ! push (val1) || ! push (val3) || ! push (val2))
              {
-               free (stack);
+               free (stack.addrs);
                return false;
              }
          }
@@ -297,7 +312,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        case DW_OP_deref_size:
          if (process->callbacks->memory_read == NULL)
            {
-             free (stack);
+             free (stack.addrs);
              __libdwfl_seterrno (DWFL_E_INVALID_ARGUMENT);
              return false;
            }
@@ -305,7 +320,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
              || ! process->callbacks->memory_read (process->dwfl, val1, &val1,
                                                    process->callbacks_arg))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          if (op->atom == DW_OP_deref_size)
@@ -314,7 +329,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
              const unsigned addr_bytes = elfclass == ELFCLASS32 ? 4 : 8;
              if (op->number > addr_bytes)
                {
-                 free (stack);
+                 free (stack.addrs);
                  __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
                  return false;
                }
@@ -330,7 +345,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
            }
          if (! push (val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
@@ -338,18 +353,18 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        case atom:                                                      \
          if (! pop (&val1) || ! push (expr))                           \
            {                                                           \
-             free (stack);                                             \
+             free (stack.addrs);                                       \
              return false;                                             \
            }                                                           \
          break;
-       UNOP (DW_OP_abs, abs ((int64_t) val1))
+       UNOP (DW_OP_abs, llabs ((int64_t) val1))
        UNOP (DW_OP_neg, -(int64_t) val1)
        UNOP (DW_OP_not, ~val1)
 #undef UNOP
        case DW_OP_plus_uconst:
          if (! pop (&val1) || ! push (val1 + op->number))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
@@ -357,7 +372,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        case atom:                                                      \
          if (! pop (&val2) || ! pop (&val1) || ! push (val1 op val2))  \
            {                                                           \
-             free (stack);                                             \
+             free (stack.addrs);                                       \
              return false;                                             \
            }                                                           \
          break;
@@ -366,7 +381,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
          if (! pop (&val2) || ! pop (&val1)                            \
              || ! push ((int64_t) val1 op (int64_t) val2))             \
            {                                                           \
-             free (stack);                                             \
+             free (stack.addrs);                                       \
              return false;                                             \
            }                                                           \
          break;
@@ -374,18 +389,18 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        case DW_OP_div:
          if (! pop (&val2) || ! pop (&val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          if (val2 == 0)
            {
-             free (stack);
+             free (stack.addrs);
              __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
              return false;
            }
          if (! push ((int64_t) val1 / (int64_t) val2))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
@@ -393,18 +408,18 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        case DW_OP_mod:
          if (! pop (&val2) || ! pop (&val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          if (val2 == 0)
            {
-             free (stack);
+             free (stack.addrs);
              __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
              return false;
            }
          if (! push (val1 % val2))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          break;
@@ -426,7 +441,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        case DW_OP_bra:
          if (! pop (&val1))
            {
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          if (val1 == 0)
@@ -438,7 +453,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
                                           sizeof (*ops), bra_compar);
          if (found == NULL)
            {
-             free (stack);
+             free (stack.addrs);
              /* PPC32 vDSO has such invalid operations.  */
              __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
              return false;
@@ -460,7 +475,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
              || ! push (cfa))
            {
              __libdwfl_seterrno (DWFL_E_LIBDW);
-             free (stack);
+             free (stack.addrs);
              return false;
            }
          is_location = true;
@@ -476,10 +491,10 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
     }
   if (! pop (result))
     {
-      free (stack);
+      free (stack.addrs);
       return false;
     }
-  free (stack);
+  free (stack.addrs);
   if (is_location)
     {
       if (process->callbacks->memory_read == NULL)
@@ -492,6 +507,8 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
        return false;
     }
   return true;
+#undef push
+#undef pop
 }
 
 static void
@@ -620,7 +637,14 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
       if (unwound->pc == 0)
        unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
       else
-       unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+        {
+          unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+          /* In SPARC the return address register actually contains
+             the address of the call instruction instead of the return
+             address.  Therefore we add here an offset defined by the
+             backend.  Most likely 0.  */
+          unwound->pc += ebl_ra_offset (ebl);
+        }
     }
   free (frame);
 }