kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

dwarf_loc.c (12447B)


      1 /* dwarf_loc.c — DWARF location-expression evaluator.
      2  *
      3  * Per doc/DWARF.md §4.4: small DWARF stack machine. Supports the ops the
      4  * producer emits: DW_OP_reg0..31, regx, fbreg, addr, call_frame_cfa, plus
      5  * arithmetic. DW_AT_frame_base = DW_OP_call_frame_cfa per §3.6 — the
      6  * caller passes the CFA in via frame->cfa.
      7  */
      8 
      9 #include <kit/arch.h>
     10 #include <kit/dwarf.h>
     11 #include <stdint.h>
     12 #include <string.h>
     13 
     14 #include "core/core.h"
     15 #include "core/heap.h"
     16 #include "debug/dwarf_internal.h"
     17 
     18 /* Tiny stack machine state. */
     19 typedef struct ExprMachine {
     20   i64 stack[64];
     21   int sp; /* points to next free slot; top is stack[sp-1] */
     22   int reg_result;
     23   u32 reg_num;     /* if reg_result, holds the register number */
     24   int stack_value; /* DW_OP_stack_value seen */
     25 } ExprMachine;
     26 
     27 static int push(ExprMachine* m, i64 v) {
     28   if (m->sp >= (int)(sizeof(m->stack) / sizeof(m->stack[0]))) return 0;
     29   m->stack[m->sp++] = v;
     30   return 1;
     31 }
     32 static int pop(ExprMachine* m, i64* v) {
     33   if (m->sp == 0) return 0;
     34   *v = m->stack[--m->sp];
     35   return 1;
     36 }
     37 
     38 /* Evaluate either DW_AT_frame_base (when we encounter DW_OP_fbreg) or
     39  * the inlined block; reuses the same machinery. Returns 0 on success. */
     40 static int eval_one(KitDebugInfo* d, const u8* expr, u32 len, const u8* fb_expr,
     41                     u32 fb_len, const KitUnwindFrame* frame, ExprMachine* m,
     42                     int allow_fbreg) {
     43   u32 off = 0;
     44   while (off < len) {
     45     u8 op = expr[off++];
     46     if (op >= DW_OP_lit0 && op <= DW_OP_lit0 + 31) {
     47       if (!push(m, op - DW_OP_lit0)) return 1;
     48     } else if (op >= DW_OP_reg0 && op <= DW_OP_reg0 + 31) {
     49       m->reg_result = 1;
     50       m->reg_num = op - DW_OP_reg0;
     51       return 0;
     52     } else if (op >= DW_OP_breg0 && op <= DW_OP_breg0 + 31) {
     53       i64 ofs = dw_sleb(expr, len, &off);
     54       u32 r = op - DW_OP_breg0;
     55       i64 v = (r < 32) ? (i64)frame->regs[r] : 0;
     56       if (!push(m, v + ofs)) return 1;
     57     } else {
     58       switch (op) {
     59         case DW_OP_addr:
     60           /* Address of a global. Address-size depends on CU; assume 8. */
     61           if (off + 8 > len) return 1;
     62           {
     63             u64 a = dw_u64(expr, len, &off);
     64             if (!push(m, (i64)a)) return 1;
     65           }
     66           break;
     67         case DW_OP_const1u:
     68           if (off + 1 > len) return 1;
     69           if (!push(m, expr[off++])) return 1;
     70           break;
     71         case DW_OP_const1s:
     72           if (off + 1 > len) return 1;
     73           if (!push(m, (i8)expr[off++])) return 1;
     74           break;
     75         case DW_OP_const2u: {
     76           if (!push(m, dw_u16(expr, len, &off))) return 1;
     77         } break;
     78         case DW_OP_const2s: {
     79           u16 v = dw_u16(expr, len, &off);
     80           if (!push(m, (i16)v)) return 1;
     81         } break;
     82         case DW_OP_const4u: {
     83           if (!push(m, dw_u32(expr, len, &off))) return 1;
     84         } break;
     85         case DW_OP_const4s: {
     86           u32 v = dw_u32(expr, len, &off);
     87           if (!push(m, (i32)v)) return 1;
     88         } break;
     89         case DW_OP_const8u:
     90         case DW_OP_const8s: {
     91           u64 v = dw_u64(expr, len, &off);
     92           if (!push(m, (i64)v)) return 1;
     93         } break;
     94         case DW_OP_constu: {
     95           u64 v = dw_uleb(expr, len, &off);
     96           if (!push(m, (i64)v)) return 1;
     97         } break;
     98         case DW_OP_consts: {
     99           i64 v = dw_sleb(expr, len, &off);
    100           if (!push(m, v)) return 1;
    101         } break;
    102         case DW_OP_dup: {
    103           i64 v;
    104           if (m->sp == 0) return 1;
    105           v = m->stack[m->sp - 1];
    106           if (!push(m, v)) return 1;
    107         } break;
    108         case DW_OP_drop: {
    109           i64 v;
    110           if (!pop(m, &v)) return 1;
    111         } break;
    112         case DW_OP_and: {
    113           i64 a, b;
    114           if (!pop(m, &b) || !pop(m, &a)) return 1;
    115           if (!push(m, a & b)) return 1;
    116         } break;
    117         case DW_OP_minus: {
    118           i64 a, b;
    119           if (!pop(m, &b) || !pop(m, &a)) return 1;
    120           if (!push(m, a - b)) return 1;
    121         } break;
    122         case DW_OP_mul: {
    123           i64 a, b;
    124           if (!pop(m, &b) || !pop(m, &a)) return 1;
    125           if (!push(m, a * b)) return 1;
    126         } break;
    127         case DW_OP_or: {
    128           i64 a, b;
    129           if (!pop(m, &b) || !pop(m, &a)) return 1;
    130           if (!push(m, a | b)) return 1;
    131         } break;
    132         case DW_OP_plus: {
    133           i64 a, b;
    134           if (!pop(m, &b) || !pop(m, &a)) return 1;
    135           if (!push(m, a + b)) return 1;
    136         } break;
    137         case DW_OP_plus_uconst: {
    138           u64 c = dw_uleb(expr, len, &off);
    139           i64 a;
    140           if (!pop(m, &a)) return 1;
    141           if (!push(m, a + (i64)c)) return 1;
    142         } break;
    143         case DW_OP_shl: {
    144           i64 a, b;
    145           if (!pop(m, &b) || !pop(m, &a)) return 1;
    146           if (!push(m, (i64)((u64)a << (b & 63)))) return 1;
    147         } break;
    148         case DW_OP_shr: {
    149           i64 a, b;
    150           if (!pop(m, &b) || !pop(m, &a)) return 1;
    151           if (!push(m, (i64)((u64)a >> (b & 63)))) return 1;
    152         } break;
    153         case DW_OP_shra: {
    154           i64 a, b;
    155           if (!pop(m, &b) || !pop(m, &a)) return 1;
    156           if (!push(m, a >> (b & 63))) return 1;
    157         } break;
    158         case DW_OP_xor: {
    159           i64 a, b;
    160           if (!pop(m, &b) || !pop(m, &a)) return 1;
    161           if (!push(m, a ^ b)) return 1;
    162         } break;
    163         case DW_OP_regx: {
    164           u64 r = dw_uleb(expr, len, &off);
    165           m->reg_result = 1;
    166           m->reg_num = (u32)r;
    167           return 0;
    168         }
    169         case DW_OP_bregx: {
    170           u64 r = dw_uleb(expr, len, &off);
    171           i64 ofs = dw_sleb(expr, len, &off);
    172           i64 v = (r < 32) ? (i64)frame->regs[r] : 0;
    173           if (!push(m, v + ofs)) return 1;
    174         } break;
    175         case DW_OP_fbreg: {
    176           i64 ofs = dw_sleb(expr, len, &off);
    177           if (!allow_fbreg) return 1;
    178           /* Evaluate frame_base expression to get the CFA-equivalent base. */
    179           {
    180             ExprMachine fbm;
    181             i64 base = 0;
    182             int rc;
    183             memset(&fbm, 0, sizeof(fbm));
    184             if (fb_expr && fb_len > 0) {
    185               rc = eval_one(d, fb_expr, fb_len, NULL, 0, frame, &fbm, 0);
    186               if (rc != 0) return rc;
    187               if (fbm.sp > 0)
    188                 base = fbm.stack[fbm.sp - 1];
    189               else if (fbm.reg_result) {
    190                 /* Frame base lives in a register — value is reg contents. */
    191                 base = (fbm.reg_num < 32) ? (i64)frame->regs[fbm.reg_num] : 0;
    192               }
    193             } else {
    194               base = (i64)frame->cfa;
    195             }
    196             if (!push(m, base + ofs)) return 1;
    197           }
    198         } break;
    199         case DW_OP_call_frame_cfa: {
    200           if (!push(m, (i64)frame->cfa)) return 1;
    201         } break;
    202         case DW_OP_stack_value:
    203           m->stack_value = 1;
    204           return 0;
    205         default:
    206           /* Unsupported op — give up. */
    207           return 1;
    208       }
    209     }
    210   }
    211   return 0;
    212 }
    213 
    214 /* Resolve a loclistx index to the active entry for `pc`.
    215  *
    216  * Per DWARF 5: DW_AT_loclists_base on the CU points at the offset_entries
    217  * array within .debug_loclists. offset_entries[idx] is a 4-byte value (in
    218  * 32-bit DWARF) giving the byte offset (relative to loclists_base) of the
    219  * matching location list. Each list is a sequence of LLE entries
    220  * terminated by DW_LLE_end_of_list. We recognize at minimum:
    221  *   DW_LLE_offset_pair (relative to base address)
    222  *   DW_LLE_start_length (absolute)
    223  *   DW_LLE_start_end (absolute)
    224  *   DW_LLE_default_location
    225  *   DW_LLE_base_address (sets the base for offset_pair)
    226  *   DW_LLE_base_addressx / DW_LLE_startx_* — degraded (skipped; need
    227  *   .debug_addr resolution we don't yet model).
    228  */
    229 int dw_loclist_resolve(KitDebugInfo* d, const DwCu* cu, u64 idx, u64 pc,
    230                        const u8** bytes_out, u32* len_out) {
    231   u32 base;
    232   u32 entry_off;
    233   u32 list_off;
    234   u64 base_addr = 0;
    235   if (!d || !cu) return 0;
    236   if (d->loclists.sec_idx == UINT32_MAX || d->loclists.size == 0) return 0;
    237   base = cu->loclists_base;
    238   /* DW_AT_loclists_base points to the start of the offset_entries table
    239    * for the CU (i.e. just past the header). offset_entries[i] is a
    240    * 4-byte (32-bit DWARF) value, the byte offset (relative to base) of
    241    * the matching location list. */
    242   entry_off = base + (u32)idx * 4u;
    243   if (entry_off + 4 > d->loclists.size) return 0;
    244   {
    245     u32 t = entry_off;
    246     list_off = dw_u32(d->loclists.data, d->loclists.size, &t);
    247   }
    248   /* The entry value is an offset relative to `base`. */
    249   list_off += base;
    250   if (list_off >= d->loclists.size) return 0;
    251   /* Walk the list. */
    252   {
    253     u32 off = list_off;
    254     while (off < d->loclists.size) {
    255       u8 lle = dw_u8(d->loclists.data, d->loclists.size, &off);
    256       switch (lle) {
    257         case DW_LLE_end_of_list:
    258           return 0;
    259         case DW_LLE_base_address: {
    260           if (cu->address_size == 8)
    261             base_addr = dw_u64(d->loclists.data, d->loclists.size, &off);
    262           else
    263             base_addr = dw_u32(d->loclists.data, d->loclists.size, &off);
    264         } break;
    265         case DW_LLE_offset_pair: {
    266           u64 lo = dw_uleb(d->loclists.data, d->loclists.size, &off);
    267           u64 hi = dw_uleb(d->loclists.data, d->loclists.size, &off);
    268           u32 elen = (u32)dw_uleb(d->loclists.data, d->loclists.size, &off);
    269           const u8* eb = d->loclists.data + off;
    270           off += elen;
    271           if (pc >= base_addr + lo && pc < base_addr + hi) {
    272             *bytes_out = eb;
    273             *len_out = elen;
    274             return 1;
    275           }
    276         } break;
    277         case DW_LLE_start_end: {
    278           u64 lo, hi;
    279           u32 elen;
    280           const u8* eb;
    281           if (cu->address_size == 8) {
    282             lo = dw_u64(d->loclists.data, d->loclists.size, &off);
    283             hi = dw_u64(d->loclists.data, d->loclists.size, &off);
    284           } else {
    285             lo = dw_u32(d->loclists.data, d->loclists.size, &off);
    286             hi = dw_u32(d->loclists.data, d->loclists.size, &off);
    287           }
    288           elen = (u32)dw_uleb(d->loclists.data, d->loclists.size, &off);
    289           eb = d->loclists.data + off;
    290           off += elen;
    291           if (pc >= lo && pc < hi) {
    292             *bytes_out = eb;
    293             *len_out = elen;
    294             return 1;
    295           }
    296         } break;
    297         case DW_LLE_start_length: {
    298           u64 lo, length;
    299           u32 elen;
    300           const u8* eb;
    301           if (cu->address_size == 8)
    302             lo = dw_u64(d->loclists.data, d->loclists.size, &off);
    303           else
    304             lo = dw_u32(d->loclists.data, d->loclists.size, &off);
    305           length = dw_uleb(d->loclists.data, d->loclists.size, &off);
    306           elen = (u32)dw_uleb(d->loclists.data, d->loclists.size, &off);
    307           eb = d->loclists.data + off;
    308           off += elen;
    309           if (pc >= lo && pc < lo + length) {
    310             *bytes_out = eb;
    311             *len_out = elen;
    312             return 1;
    313           }
    314         } break;
    315         case DW_LLE_default_location: {
    316           u32 elen = (u32)dw_uleb(d->loclists.data, d->loclists.size, &off);
    317           const u8* eb = d->loclists.data + off;
    318           off += elen;
    319           *bytes_out = eb;
    320           *len_out = elen;
    321           return 1;
    322         }
    323         case DW_LLE_base_addressx: {
    324           (void)dw_uleb(d->loclists.data, d->loclists.size, &off);
    325           /* unsupported: needs .debug_addr indirection */
    326         } break;
    327         case DW_LLE_startx_endx:
    328         case DW_LLE_startx_length: {
    329           (void)dw_uleb(d->loclists.data, d->loclists.size, &off);
    330           (void)dw_uleb(d->loclists.data, d->loclists.size, &off);
    331           {
    332             u32 elen = (u32)dw_uleb(d->loclists.data, d->loclists.size, &off);
    333             off += elen;
    334           }
    335         } break;
    336         default:
    337           /* Unknown LLE — stop. */
    338           return 0;
    339       }
    340     }
    341   }
    342   return 0;
    343 }
    344 
    345 int dw_eval_expr(KitDebugInfo* d, const u8* expr, u32 len, const u8* fb_expr,
    346                  u32 fb_len, const KitUnwindFrame* frame, DwExprResult* out) {
    347   ExprMachine m;
    348   int rc;
    349   memset(&m, 0, sizeof(m));
    350   out->kind = 3;
    351   out->value = 0;
    352   if (!expr || len == 0 || !frame) return 1;
    353   rc = eval_one(d, expr, len, fb_expr, fb_len, frame, &m, 1);
    354   if (rc != 0) return rc;
    355   if (m.reg_result) {
    356     out->kind = 2;
    357     out->value = m.reg_num;
    358     return 0;
    359   }
    360   if (m.sp == 0) return 1;
    361   if (m.stack_value) {
    362     out->kind = 1;
    363     out->value = (u64)m.stack[m.sp - 1];
    364     return 0;
    365   }
    366   out->kind = 0;
    367   out->value = (u64)m.stack[m.sp - 1];
    368   return 0;
    369 }