libunwind-devel
[Top][All Lists]
Advanced

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

[Libunwind-devel] Toward support for DW_CFA_val_expression


From: Richard Henderson
Subject: [Libunwind-devel] Toward support for DW_CFA_val_expression
Date: Fri, 26 Sep 2008 09:40:28 -0700
User-agent: Thunderbird 2.0.0.16 (X11/20080723)

I recently added a new DW_OP_ opcode, for use in certain section changing inline asms present in the kernel and glibc. A minimal patch for libunwind for this new opcode is attached.

However, it doesn't actually work yet, because the base Dwarf 3 CFA opcode that gets used, DW_CFA_val_expression, isn't supported. And the changes to add that support won't be trivial. The basic problem at the moment is that the dwarf_cursor only uses addresses of values, and with three of the new Dwarf 3 opcodes we deal with computed values and not addresses at all.

So I'm wondering what people think is better: (1) fiddle dwarf_loc_t for
each target to handle the case of an immedate value, or (2) rearrange the entire dwarf unwinding process to deal solely with register values
instead of register save locations.  I'm not quite sure why we save the
copies to the machine state for the very last thing during unw_resume.

Thoughts?


r~
diff --git a/include/dwarf.h b/include/dwarf.h
index c7c757e..77510f4 100644
--- a/include/dwarf.h
+++ b/include/dwarf.h
@@ -121,7 +121,14 @@ typedef enum
     DW_OP_call2                        = 0x98,
     DW_OP_call4                        = 0x99,
     DW_OP_call_ref             = 0x9a,
+    DW_OP_form_tls_address     = 0x9b,
+    DW_OP_call_frame_cfa       = 0x9c,
+    DW_OP_bit_piece            = 0x9d,
+
     DW_OP_lo_user              = 0xe0,
+    DW_OP_GNU_push_tls_address = 0xe0,
+    DW_OP_GNU_uninit           = 0xf0,
+    DW_OP_GNU_encoded_addr     = 0xf1,
     DW_OP_hi_user              = 0xff
   }
 dwarf_expr_op_t;
@@ -156,6 +163,10 @@ typedef enum
     DW_CFA_offset_extended_sf  = 0x11,
     DW_CFA_def_cfa_sf          = 0x12,
     DW_CFA_def_cfa_offset_sf   = 0x13,
+    DW_CFA_val_offset          = 0x14,
+    DW_CFA_val_offset_sf       = 0x15,
+    DW_CFA_val_expression      = 0x16,
+
     DW_CFA_lo_user             = 0x1c,
     DW_CFA_MIPS_advance_loc8   = 0x1d,
     DW_CFA_GNU_window_save     = 0x2d,
@@ -197,12 +208,14 @@ dwarf_cfa_t;
 /* Pointer-encoding application: */
 #define DW_EH_PE_absptr                0x00    /* absolute value */
 #define DW_EH_PE_pcrel         0x10    /* rel. to addr. of encoded value */
-#define DW_EH_PE_textrel       0x20    /* text-relative (GCC-specific???) */
-#define DW_EH_PE_datarel       0x30    /* data-relative */
-/* The following are not documented by LSB v1.3, yet they are used by
-   GCC, presumably they aren't documented by LSB since they aren't
-   used on Linux:  */
-#define DW_EH_PE_funcrel       0x40    /* start-of-procedure-relative */
+/* The base address for both the textrel and datarel encodings is target
+   specific.  These would be used in preference to PCREL on systems in
+   which the text and data segments do not have a fixed displacement, e.g.
+   HPUX.  It is intended that it be something that is easy to compute with
+   relocations on the target.  */
+#define DW_EH_PE_textrel       0x20    /* text segment relative */
+#define DW_EH_PE_datarel       0x30    /* data segment relative */
+#define DW_EH_PE_funcrel       0x40    /* start-of-FDE-relative */
 #define DW_EH_PE_aligned       0x50    /* aligned pointer */
 
 extern struct mempool dwarf_reg_state_pool;
@@ -214,7 +227,9 @@ typedef enum
     DWARF_WHERE_SAME,          /* register has same value as in prev. frame */
     DWARF_WHERE_CFAREL,                /* register saved at CFA-relative 
address */
     DWARF_WHERE_REG,           /* register saved in another register */
-    DWARF_WHERE_EXPR,          /* register saved */
+    DWARF_WHERE_EXPRA,         /* register saved at computed address */
+    DWARF_WHERE_EXPRV,         /* register value = computed expression */
+    DWARF_WHERE_CFAOFF         /* register value = cfa+offset */
   }
 dwarf_where_t;
 
@@ -230,9 +245,9 @@ dwarf_save_loc_t;
    the CFA can be expressed as a (REGISTER,OFFSET) pair.  To handle
    this, we use two dwarf_save_loc structures to describe the CFA.
    The first one (CFA_REG_COLUMN), tells us where the CFA is saved.
-   In the case of DWARF_WHERE_EXPR, the CFA is defined by a DWARF
-   location expression whose address is given by member "val".  In the
-   case of DWARF_WHERE_REG, member "val" gives the number of the
+   In the case of DWARF_WHERE_EXPR_{ADDR,VALUE}, the CFA is defined by
+   a DWARF location expression whose address is given by member "val".
+   In the case of DWARF_WHERE_REG, member "val" gives the number of the
    base-register and the "val" member of DWARF_CFA_OFF_COLUMN gives
    the offset value.  */
 #define DWARF_CFA_REG_COLUMN   DWARF_NUM_PRESERVED_REGS
diff --git a/src/dwarf/Gexpr.c b/src/dwarf/Gexpr.c
index 4a01215..abea721 100644
--- a/src/dwarf/Gexpr.c
+++ b/src/dwarf/Gexpr.c
@@ -104,7 +104,8 @@ static uint8_t operands[256] =
     [DW_OP_xderef_size] =      OPND1 (VAL8),
     [DW_OP_call2] =            OPND1 (VAL16),
     [DW_OP_call4] =            OPND1 (VAL32),
-    [DW_OP_call_ref] =         OPND1 (OFFSET)
+    [DW_OP_call_ref] =         OPND1 (OFFSET),
+    [DW_OP_GNU_encoded_addr] = OPND1 (VAL8)
   };
 
 static inline unw_sword_t
@@ -628,6 +629,16 @@ do {                                               \
          Debug (15, "OP_nop\n");
          break;
 
+        case DW_OP_GNU_encoded_addr:
+         ret = dwarf_read_encoded_pointer (as, a, addr, operand1,
+                                           &c->pi, &operand2, arg);
+         if (ret < 0)
+           return ret;
+         Debug (15, "OP_GNU_encoded_addr(0x%02x,0x%lx)\n",
+                (int)operand1, (unsigned long)operand2);
+         push (operand2);
+         break;
+
        case DW_OP_call2:
        case DW_OP_call4:
        case DW_OP_call_ref:
diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c
index aa87d61..d8fa47c 100644
--- a/src/dwarf/Gparser.c
+++ b/src/dwarf/Gparser.c
@@ -300,7 +300,7 @@ run_cfi_program (struct dwarf_cursor *c, 
dwarf_state_record_t *sr,
 
        case DW_CFA_def_cfa_expression:
          /* Save the address of the DW_FORM_block for later evaluation. */
-         set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPR, *addr);
+         set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPRA, *addr);
 
          if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
            goto fail;
@@ -315,13 +315,46 @@ run_cfi_program (struct dwarf_cursor *c, 
dwarf_state_record_t *sr,
            goto fail;
 
          /* Save the address of the DW_FORM_block for later evaluation. */
-         set_reg (sr, regnum, DWARF_WHERE_EXPR, *addr);
+         set_reg (sr, regnum, DWARF_WHERE_EXPRA, *addr);
 
          if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
            goto fail;
 
          Debug (15, "CFA_expression r%lu @ 0x%lx [%lu bytes]\n",
-                (long) regnum, (long) addr, (long) len);
+                (long) regnum, (long) *addr, (long) len);
+         *addr += len;
+         break;
+
+       case DW_CFA_val_offset:
+         if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
+             || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
+           goto fail;
+         set_reg (sr, regnum, DWARF_WHERE_CFAOFF, val * dci->data_align);
+         Debug (15, "CFA_val_offset r%lu = cf+0x%lx\n",
+                (long) regnum, (long)(val * dci->data_align));
+         break;
+
+       case DW_CFA_val_offset_sf:
+         if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
+             || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
+           goto fail;
+         set_reg (sr, regnum, DWARF_WHERE_CFAOFF, val * dci->data_align);
+         Debug (15, "CFA_val_offset_sf r%lu = cf+0x%lx\n",
+                (long) regnum, (long)(val * dci->data_align));
+         break;
+
+       case DW_CFA_val_expression:
+         if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
+           goto fail;
+
+         /* Save the address of the DW_FORM_block for later evaluation.  */
+         set_reg (sr, regnum, DWARF_WHERE_EXPRV, *addr);
+
+         if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
+           goto fail;
+
+         Debug (15, "CFA_val_expression r%lu = 0x%lx [%lu bytes]\n",
+                (long) regnum, (long) *addr, (long) len);
          *addr += len;
          break;
 
@@ -725,7 +758,7 @@ apply_reg_state (struct dwarf_cursor *c, struct 
dwarf_reg_state *rs)
     {
       /* CFA is equal to EXPR: */
 
-      assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPR);
+      assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPRA);
 
       addr = rs->reg[DWARF_CFA_REG_COLUMN].val;
       if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0)
@@ -755,11 +788,16 @@ apply_reg_state (struct dwarf_cursor *c, struct 
dwarf_reg_state *rs)
          c->loc[i] = DWARF_REG_LOC (c, dwarf_to_unw_regnum (rs->reg[i].val));
          break;
 
-       case DWARF_WHERE_EXPR:
+       case DWARF_WHERE_EXPRA:
          addr = rs->reg[i].val;
          if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) , 0)
            return ret;
          break;
+
+       case DWARF_WHERE_CFAOFF:
+       case DWARF_WHERE_EXPRV:
+       default:
+         abort ();
        }
     }
   c->cfa = cfa;

reply via email to

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