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 }