// Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //- analyzers #if 0 internal DW_SimpleLoc dw_expr__analyze_fast(void *base, Rng1U64 range, U64 text_section_base) { DW_SimpleLoc result = {DW_SimpleLocKind_Empty}; String8 expr_data = str8((U8*)data+range.min, (U8*)data+range.max); U8 op = 0; if (str8_deserial_read_struct(expr_data, 0, &op)) { // step params U64 size_param = 0; B32 is_signed = 0; // step U64 step_cursor = 1; switch (op) { //// literal encodings //// case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8: case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20: case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: { U64 x = op - DW_ExprOp_Lit0; result.kind = DW_SimpleLocKind_Address; result.addr = x; } break; case DW_ExprOp_Const1U:size_param = 1; goto const_n; case DW_ExprOp_Const2U:size_param = 2; goto const_n; case DW_ExprOp_Const4U:size_param = 4; goto const_n; case DW_ExprOp_Const8U:size_param = 8; goto const_n; case DW_ExprOp_Const1S:size_param = 1; is_signed = 1; goto const_n; case DW_ExprOp_Const2S:size_param = 2; is_signed = 1; goto const_n; case DW_ExprOp_Const4S:size_param = 4; is_signed = 1; goto const_n; case DW_ExprOp_Const8S:size_param = 8; is_signed = 1; goto const_n; const_n: { U64 x = 0; step_cursor += dw_based_range_read(base, range, step_cursor, size_param, &x); if (is_signed) { x = extend_sign64(x, size_param); } result.kind = DW_SimpleLocKind_Address; result.addr = x; } break; case DW_ExprOp_Addr: { U64 offset = 0; step_cursor += dw_based_range_read(base, range, step_cursor, 8, &offset); U64 x = text_section_base + offset; result.kind = DW_SimpleLocKind_Address; result.addr = x; } break; case DW_ExprOp_ConstU: { U64 x = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &x); result.kind = DW_SimpleLocKind_Address; result.addr = x; } break; case DW_ExprOp_ConstS: { U64 x = 0; step_cursor += dw_based_range_read_sleb128(base, range, step_cursor, (S64*)&x); result.kind = DW_SimpleLocKind_Address; result.addr = x; } break; //// register location descriptions //// case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8: case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20: case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: { U64 reg_idx = op - DW_ExprOp_Reg0; result.kind = DW_SimpleLocKind_Register; result.reg_idx = reg_idx; } break; case DW_ExprOp_RegX: { U64 reg_idx = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, ®_idx); result.kind = DW_SimpleLocKind_Register; result.reg_idx = reg_idx; } break; //// implicit location descriptions //// case DW_ExprOp_ImplicitValue: { U64 size = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &size); if (step_cursor + size <= range.max) { result.kind = DW_SimpleLocKind_ValueLong; result.val_long.str = (U8*)base + range.min + step_cursor; result.val_long.size = size; } step_cursor += size; } break; case DW_ExprOp_StackValue: { // this op pops from the value stack, so if it comes first the dwarf expression is bad. result.kind = DW_SimpleLocKind_Fail; result.fail_kind = DW_LocFailKind_BadData; } break; //// composite location descriptions //// // if the first and only op is a piece, the expression is empty case DW_ExprOp_Piece: { U64 size = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &size); result.kind = DW_SimpleLocKind_Empty; } break; case DW_ExprOp_BitPiece: { U64 bit_size = 0, bit_off = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &bit_size); step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &bit_off); result.kind = DW_SimpleLocKind_Empty; } break; //// final fallback //// default: { result.kind = DW_SimpleLocKind_Fail; result.fail_kind = DW_LocFailKind_TooComplicated; } break; } // check this was the whole expression if (range.min + step_cursor < range.max) { result.kind = DW_SimpleLocKind_Fail; result.fail_kind = DW_LocFailKind_TooComplicated; } } return result; } internal DW_ExprAnalysis dw_expr__analyze_details(void *in_base, Rng1U64 in_range, DW_ExprMachineCallConfig *call_config) { Temp scratch = scratch_begin(0, 0); DW_ExprAnalysis result = {0}; // are we resolving calls? B32 has_call_func = (call_config != 0 && call_config->func != 0); // tasks DW_ExprAnalysisTask *unfinished_tasks = 0; DW_ExprAnalysisTask *finished_tasks = 0; // convert range input to string String8 in_data = str8((U8*)in_base + in_range.min, in_range.max - in_range.min); // put input task onto the list { DW_ExprAnalysisTask *new_task = push_array(scratch.arena, DW_ExprAnalysisTask, 1); new_task->p = max_U64; new_task->data = in_data; SLLStackPush(unfinished_tasks, new_task); } // state for checking implicit locations B32 last_was_implicit_loc = 0; // task loop for (;;) { // get next task to handle DW_ExprAnalysisTask *task = unfinished_tasks; if (task == 0) { break; } String8 task_data = task->data; U8 *task_base = task_data.str; Rng1U64 task_range = rng_1u64(0, task_data.size); // move the task to finished now SLLStackPop(unfinished_tasks); SLLStackPush(finished_tasks, task); // analysis loop for (U64 cursor = 0;;) { // decode op U64 op_offset = cursor; U8 op = 0; if (dw_based_range_read(task_base, task_range, op_offset, 1, &op)) { U64 after_op_off = cursor + 1; // require piece op after 'implicit' location descriptions if (last_was_implicit_loc) { if (op != DW_ExprOp_Piece && op != DW_ExprOp_BitPiece) { result.flags |= DW_ExprFlag_BadData; goto finish; } } // step params U64 size_param = 0; B32 is_signed = 0; // step U64 step_cursor = after_op_off; switch (op) { //// literal encodings //// case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8: case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20: case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: break; case DW_ExprOp_Const1U:size_param = 1; goto const_n; case DW_ExprOp_Const2U:size_param = 2; goto const_n; case DW_ExprOp_Const4U:size_param = 4; goto const_n; case DW_ExprOp_Const8U:size_param = 8; goto const_n; case DW_ExprOp_Const1S:size_param = 1; is_signed = 1; goto const_n; case DW_ExprOp_Const2S:size_param = 2; is_signed = 1; goto const_n; case DW_ExprOp_Const4S:size_param = 4; is_signed = 1; goto const_n; case DW_ExprOp_Const8S:size_param = 8; is_signed = 1; goto const_n; const_n: { U64 x = 0; step_cursor += dw_based_range_read(task_base, task_range, step_cursor, size_param, &x); } break; case DW_ExprOp_Addr: { U64 offset = 0; step_cursor += dw_based_range_read(task_base, task_range, step_cursor, 8, &offset); result.flags |= DW_ExprFlag_UsesTextBase; } break; case DW_ExprOp_ConstU: { U64 x = 0; step_cursor += dw_based_range_read_uleb128(task_base, task_range, step_cursor, &x); } break; case DW_ExprOp_ConstS: { U64 x = 0; step_cursor += dw_based_range_read_sleb128(task_base, task_range, step_cursor, (S64*)&x); } break; //// register based addressing //// case DW_ExprOp_FBReg: { S64 offset = 0; step_cursor += dw_based_range_read_sleb128(task_base, task_range, step_cursor, &offset); result.flags |= DW_ExprFlag_UsesFrameBase; } break; case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8: case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11: case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20: case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23: case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: case DW_ExprOp_BReg30: case DW_ExprOp_BReg31: { S64 offset = 0; step_cursor += dw_based_range_read_sleb128(task_base, task_range, step_cursor, &offset); result.flags |= DW_ExprFlag_UsesRegisters; } break; case DW_ExprOp_BRegX: { U64 reg_idx = 0; S64 offset = 0; step_cursor += dw_based_range_read_uleb128(task_base, task_range, step_cursor, ®_idx); step_cursor += dw_based_range_read_sleb128(task_base, task_range, step_cursor, &offset); result.flags |= DW_ExprFlag_UsesRegisters; } break; //// stack operations //// case DW_ExprOp_Dup: case DW_ExprOp_Drop: break; case DW_ExprOp_Pick: { U64 idx = 0; step_cursor += dw_based_range_read(task_base, task_range, step_cursor, 1, &idx); } break; case DW_ExprOp_Over: case DW_ExprOp_Swap: case DW_ExprOp_Rot: break; case DW_ExprOp_Deref: { result.flags |= DW_ExprFlag_UsesMemory; } break; case DW_ExprOp_DerefSize: { U64 size = 0; step_cursor += dw_based_range_read(task_base, task_range, step_cursor, 1, &size); result.flags |= DW_ExprFlag_UsesMemory; } break; case DW_ExprOp_XDeref: case DW_ExprOp_XDerefSize: { result.flags |= DW_ExprFlag_NotSupported; } goto finish; case DW_ExprOp_PushObjectAddress: { result.flags |= DW_ExprFlag_UsesObjectAddress; } break; case DW_ExprOp_GNU_PushTlsAddress: case DW_ExprOp_FormTlsAddress: { result.flags |= DW_ExprFlag_UsesTLSAddress; } break; case DW_ExprOp_CallFrameCfa: { result.flags |= DW_ExprFlag_UsesCFA; } break; //// arithmetic and logical operations //// case DW_ExprOp_Abs: case DW_ExprOp_And: case DW_ExprOp_Div: case DW_ExprOp_Minus: case DW_ExprOp_Mod: case DW_ExprOp_Mul: case DW_ExprOp_Neg: case DW_ExprOp_Not: case DW_ExprOp_Or: case DW_ExprOp_Plus: break; case DW_ExprOp_PlusUConst: { U64 y = 0; step_cursor += dw_based_range_read_uleb128(task_base, task_range, step_cursor, &y); } break; case DW_ExprOp_Shl: case DW_ExprOp_Shr: case DW_ExprOp_Shra: case DW_ExprOp_Xor: break; //// control flow operations //// case DW_ExprOp_Le: case DW_ExprOp_Ge: case DW_ExprOp_Eq: case DW_ExprOp_Lt: case DW_ExprOp_Gt: case DW_ExprOp_Ne: break; case DW_ExprOp_Skip: case DW_ExprOp_Bra: { S16 d = 0; step_cursor += dw_based_range_read(task_base, task_range, step_cursor, 2, &d); result.flags |= DW_ExprFlag_NonLinearFlow; } break; case DW_ExprOp_Call2:size_param = 2; goto callN; case DW_ExprOp_Call4:size_param = 4; goto callN; callN: { U64 p = 0; step_cursor += dw_based_range_read(task_base, task_range, step_cursor, size_param, &p); result.flags |= DW_ExprFlag_UsesCallResolution|DW_ExprFlag_NonLinearFlow; // add to task list if (has_call_func) { DW_ExprAnalysisTask *existing = dw_expr__analysis_task_from_p(unfinished_tasks, p); if (existing == 0) { existing = dw_expr__analysis_task_from_p(finished_tasks, p);; } if (existing == 0) { DW_ExprAnalysisTask *new_task = push_array(scratch.arena, DW_ExprAnalysisTask, 1); new_task->p = p; new_task->data = call_config->func(call_config->user_ptr, p); SLLStackPush(unfinished_tasks, new_task); } } } break; case DW_ExprOp_CallRef: { result.flags |= DW_ExprFlag_NotSupported; } goto finish; //// special operations //// case DW_ExprOp_Nop:break; //// register location descriptions //// case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8: case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20: case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: { last_was_implicit_loc = 1; } break; case DW_ExprOp_RegX: { U64 reg_idx = 0; step_cursor += dw_based_range_read(task_base, task_range, step_cursor, size_param, ®_idx); last_was_implicit_loc = 1; } break; //// implicit location descriptions //// case DW_ExprOp_ImplicitValue: { U64 size = 0; step_cursor += dw_based_range_read(task_base, task_range, step_cursor, size_param, &size); if (step_cursor + size > task_range.max) { result.flags |= DW_ExprFlag_BadData; goto finish; } step_cursor += size; last_was_implicit_loc = 1; } break; case DW_ExprOp_StackValue: { last_was_implicit_loc = 1; } break; //// composite location descriptions //// case DW_ExprOp_Piece: { U64 size = 0; step_cursor += dw_based_range_read_uleb128(task_base, task_range, step_cursor, &size); result.flags |= DW_ExprFlag_UsesComposite; last_was_implicit_loc = 0; } break; case DW_ExprOp_BitPiece: { U64 bit_size = 0; U64 bit_off = 0; step_cursor += dw_based_range_read_uleb128(task_base, task_range, step_cursor, &bit_size); step_cursor += dw_based_range_read_uleb128(task_base, task_range, step_cursor, &bit_off); result.flags |= DW_ExprFlag_UsesComposite; last_was_implicit_loc = 0; } break; //// final fallback //// default: { result.flags |= DW_ExprFlag_NotSupported; } goto finish; } // increment cursor cursor = step_cursor; } // check for end of task if (cursor < task_data.size) { goto finish_task; } } finish_task:; } finish:; scratch_end(scratch); return result; } #endif //- full eval internal DW_Location dw_expr__eval(Arena *arena_optional, void *expr_base, Rng1U64 expr_range, DW_ExprMachineConfig *config) { #if 0 Temp scratch = scratch_begin(&arena_optional, 1); DW_Location result = {0}; // setup stack DW_ExprStack stack = dw_expr__stack_make(scratch.arena); // adjust expr range void *expr_ptr = (U8*)expr_base + expr_range.min; U64 expr_size = expr_range.max - expr_range.min; // setup call stack DW_ExprCallStack call_stack = {0}; dw_expr__call_push(scratch.arena, &call_stack, expr_ptr, expr_size); // state variables DW_SimpleLoc stashed_loc = {DW_SimpleLocKind_Address}; // run loop U64 max_step_count = config->max_step_count; U64 step_counter = 0; for (;;) { // check top of stack DW_ExprCall *call = dw_expr__call_top(&call_stack); if (call == 0) { goto finish; } // grab top of stack details void *base = call->ptr; Rng1U64 range = rng_1u64(0, call->size); U64 cursor = call->cursor; // decode op U64 op_offset = cursor; U8 op = 0; if (dw_based_range_read(base, range, op_offset, 1, &op)) { U64 after_op_off = cursor + 1; // require piece op after 'implicit' location descriptions if (stashed_loc.kind != DW_SimpleLocKind_Address) { if (op != DW_ExprOp_Piece && op != DW_ExprOp_BitPiece) { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_BadData; goto finish; } } // step params U64 size_param = 0; B32 is_signed = 0; // step U64 step_cursor = after_op_off; switch (op) { //// literal encodings //// case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8: case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20: case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: { U64 x = op - DW_ExprOp_Lit0; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Const1U:size_param = 1; goto const_n; case DW_ExprOp_Const2U:size_param = 2; goto const_n; case DW_ExprOp_Const4U:size_param = 4; goto const_n; case DW_ExprOp_Const8U:size_param = 8; goto const_n; case DW_ExprOp_Const1S:size_param = 1; is_signed = 1; goto const_n; case DW_ExprOp_Const2S:size_param = 2; is_signed = 1; goto const_n; case DW_ExprOp_Const4S:size_param = 4; is_signed = 1; goto const_n; case DW_ExprOp_Const8S:size_param = 8; is_signed = 1; goto const_n; const_n: { U64 x = 0; step_cursor += dw_based_range_read(base, range, step_cursor, size_param, &x); if (is_signed) { x = extend_sign64(x, size_param); } dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Addr: { U64 offset = 0; step_cursor += dw_based_range_read(base, range, step_cursor, 8, &offset); // earlier versions of GCC emit TLS offset with DW_ExprOp_Addr. B32 is_text_relative; { U8 next_op = 0; dw_based_range_read_struct(base, range, step_cursor, &next_op); is_text_relative = (next_op != DW_ExprOp_GNU_PushTlsAddress); } U64 addr = offset; if (is_text_relative) { if (config->text_section_base != 0) { addr += *config->text_section_base; } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingTextBase; goto finish; } } dw_expr__stack_push(scratch.arena, &stack, addr); } break; case DW_ExprOp_ConstU: { U64 x = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &x); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_ConstS: { U64 x = 0; step_cursor += dw_based_range_read_sleb128(base, range, step_cursor, (S64*)&x); dw_expr__stack_push(scratch.arena, &stack, x); } break; //// register based addressing //// case DW_ExprOp_FBReg: { S64 offset = 0; step_cursor += dw_based_range_read_sleb128(base, range, step_cursor, &offset); if (config->frame_base != 0) { U64 x = *config->frame_base + offset; dw_expr__stack_push(scratch.arena, &stack, x); } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingFrameBase; goto finish; } } break; case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8: case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11: case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20: case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23: case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: case DW_ExprOp_BReg30: case DW_ExprOp_BReg31: { S64 offset = 0; step_cursor += dw_based_range_read_sleb128(base, range, step_cursor, &offset); U64 reg_idx = op - DW_ExprOp_BReg0; DW_RegsX64 *regs = config->regs; if (regs != 0) { if (reg_idx < ArrayCount(regs->r)) { U64 x = regs->r[reg_idx] + offset; dw_expr__stack_push(scratch.arena, &stack, x); } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_BadData; stashed_loc.fail_data = op_offset; goto finish; } } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingRegisters; goto finish; } } break; case DW_ExprOp_BRegX: { U64 reg_idx = 0; S64 offset = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, ®_idx); step_cursor += dw_based_range_read_sleb128(base, range, step_cursor, &offset); DW_RegsX64 *regs = config->regs; if (regs != 0) { if (reg_idx < ArrayCount(regs->r)) { U64 x = regs->r[reg_idx] + offset; dw_expr__stack_push(scratch.arena, &stack, x); } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_BadData; stashed_loc.fail_data = op_offset; goto finish; } } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingRegisters; goto finish; } } break; //// stack operations //// case DW_ExprOp_Dup: { U64 x = dw_expr__stack_pick(&stack, 0); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Drop: { dw_expr__stack_pop(&stack); } break; case DW_ExprOp_Pick: { U64 idx = 0; step_cursor += dw_based_range_read(base, range, step_cursor, 1, &idx); U64 x = dw_expr__stack_pick(&stack, idx); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Over: { U64 x = dw_expr__stack_pick(&stack, 1); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Swap: { U64 a = dw_expr__stack_pop(&stack); U64 b = dw_expr__stack_pop(&stack); dw_expr__stack_push(scratch.arena, &stack, b); dw_expr__stack_push(scratch.arena, &stack, a); } break; case DW_ExprOp_Rot: { U64 a = dw_expr__stack_pop(&stack); U64 b = dw_expr__stack_pop(&stack); U64 c = dw_expr__stack_pop(&stack); dw_expr__stack_push(scratch.arena, &stack, a); dw_expr__stack_push(scratch.arena, &stack, c); dw_expr__stack_push(scratch.arena, &stack, b); } break; case DW_ExprOp_Deref: { U64 addr = dw_expr__stack_pop(&stack); B32 read_success = 0; if (config->read_memory) { U64 x = 0; if (config->read_memory(addr, sizeof(x), &x, config->read_memory_ud) == sizeof(x)) { dw_expr__stack_push(scratch.arena, &stack, x); read_success = 1; } } if (!read_success) { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingMemory; stashed_loc.fail_data = addr; goto finish; } } break; case DW_ExprOp_DerefSize: { U64 raw_size = 0; step_cursor += dw_based_range_read(base, range, step_cursor, 1, &raw_size); U64 size = ClampTop(raw_size, 8); U64 addr = dw_expr__stack_pop(&stack); B32 read_success = 0; if (config->read_memory) { U64 x = 0; if (config->read_memory(addr, size, &x, config->read_memory_ud) == sizeof(x)) { dw_expr__stack_push(scratch.arena, &stack, x); read_success = 1; } } if (!read_success) { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingMemory; stashed_loc.fail_data = addr; goto finish; } } break; case DW_ExprOp_XDeref: case DW_ExprOp_XDerefSize: { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_NotSupported; goto finish; } break; case DW_ExprOp_PushObjectAddress: { if (config->object_address != 0) { U64 x = *config->object_address; dw_expr__stack_push(scratch.arena, &stack, x); } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingObjectAddress; goto finish; } } break; // NOTE: pop offset from stack, convert it to TLS address, then push it back. case DW_ExprOp_GNU_PushTlsAddress: case DW_ExprOp_FormTlsAddress: { S64 s = (S64)dw_expr__stack_pop(&stack); if (config->tls_address != 0) { U64 x = *config->tls_address + s; dw_expr__stack_push(scratch.arena, &stack, x); } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingTLSAddress; goto finish; } } break; case DW_ExprOp_CallFrameCfa: { if (config->cfa != 0) { U64 x = *config->cfa; dw_expr__stack_push(scratch.arena, &stack, x); } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingCFA; goto finish; } } break; //// arithmetic and logical operations //// case DW_ExprOp_Abs: { S64 s = (S64)dw_expr__stack_pop(&stack); S64 x = abs_s64(s); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_And: { U64 x = dw_expr__stack_pop(&stack); U64 y = dw_expr__stack_pop(&stack); dw_expr__stack_push(scratch.arena, &stack, x&y); } break; case DW_ExprOp_Div: { S64 d = (S64)dw_expr__stack_pop(&stack); S64 n = (S64)dw_expr__stack_pop(&stack); S64 x = (d == 0)?0:n/d; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Minus: { U64 b = dw_expr__stack_pop(&stack); U64 a = dw_expr__stack_pop(&stack); U64 x = a - b; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Mod: { S64 d = (S64)dw_expr__stack_pop(&stack); S64 n = (S64)dw_expr__stack_pop(&stack); S64 x = (d == 0)?0:n%d; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Mul: { U64 b = dw_expr__stack_pop(&stack); U64 a = dw_expr__stack_pop(&stack); U64 x = a*b; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Neg: { S64 s = (S64)dw_expr__stack_pop(&stack); S64 x = -s; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Not: { U64 y = dw_expr__stack_pop(&stack); U64 x = ~y; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Or: { U64 y = dw_expr__stack_pop(&stack); U64 z = dw_expr__stack_pop(&stack); U64 x = y | z; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Plus: { U64 y = dw_expr__stack_pop(&stack); U64 z = dw_expr__stack_pop(&stack); U64 x = y + z; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_PlusUConst: { U64 y = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &y); U64 z = dw_expr__stack_pop(&stack); U64 x = y + z; dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Shl: { U64 y = dw_expr__stack_pop(&stack); U64 z = dw_expr__stack_pop(&stack); U64 x = 0; if (y < 64) { x = z << y; } dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Shr: { U64 y = dw_expr__stack_pop(&stack); U64 z = dw_expr__stack_pop(&stack); U64 x = 0; if (y < 64) { x = z >> y; } dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Shra: { U64 y = dw_expr__stack_pop(&stack); U64 z = dw_expr__stack_pop(&stack); U64 x = 0; if (y < 64) { x = z >> y; // sign extensions if (y > 0 && (z & (1ull << 63))) { x |= ~((1 << (64 - y)) - 1); } } dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Xor: { U64 y = dw_expr__stack_pop(&stack); U64 z = dw_expr__stack_pop(&stack); U64 x = y ^ z; dw_expr__stack_push(scratch.arena, &stack, x); } break; //// control flow operations //// case DW_ExprOp_Le: { S64 b = (S64)dw_expr__stack_pop(&stack); S64 a = (S64)dw_expr__stack_pop(&stack); U64 x = (a <= b); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Ge: { S64 b = (S64)dw_expr__stack_pop(&stack); S64 a = (S64)dw_expr__stack_pop(&stack); U64 x = (a >= b); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Eq: { S64 b = (S64)dw_expr__stack_pop(&stack); S64 a = (S64)dw_expr__stack_pop(&stack); U64 x = (a == b); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Lt: { S64 b = (S64)dw_expr__stack_pop(&stack); S64 a = (S64)dw_expr__stack_pop(&stack); U64 x = (a < b); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Gt: { S64 b = (S64)dw_expr__stack_pop(&stack); S64 a = (S64)dw_expr__stack_pop(&stack); U64 x = (a > b); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Ne: { S64 b = (S64)dw_expr__stack_pop(&stack); S64 a = (S64)dw_expr__stack_pop(&stack); U64 x = (a != b); dw_expr__stack_push(scratch.arena, &stack, x); } break; case DW_ExprOp_Skip: { S16 d = 0; step_cursor += dw_based_range_read(base, range, step_cursor, 2, &d); step_cursor = step_cursor + d; } break; case DW_ExprOp_Bra: { S16 d = 0; step_cursor += dw_based_range_read(base, range, step_cursor, 2, &d); U64 b = dw_expr__stack_pop(&stack); if (b != 0) { step_cursor = step_cursor + d; } } break; case DW_ExprOp_Call2: { U16 p = 0; step_cursor += dw_based_range_read(base, range, step_cursor, 2, &p); if (config->call.func != 0) { String8 sub_data = config->call.func(config->call.user_ptr, p); dw_expr__call_push(scratch.arena, &call_stack, sub_data.str, sub_data.size); } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingCallResolution; goto finish; } } break; case DW_ExprOp_Call4: { U32 p = 0; step_cursor += dw_based_range_read(base, range, step_cursor, 4, &p); if (config->call.func != 0) { String8 sub_data = config->call.func(config->call.user_ptr, p); dw_expr__call_push(scratch.arena, &call_stack, sub_data.str, sub_data.size); } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingCallResolution; goto finish; } } break; case DW_ExprOp_CallRef: { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_NotSupported; goto finish; } break; //// special operations //// case DW_ExprOp_Nop:break; //// register location descriptions //// case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8: case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20: case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: { U64 reg_idx = op - DW_ExprOp_Reg0; stashed_loc.kind = DW_SimpleLocKind_Register; stashed_loc.reg_idx = reg_idx; } break; case DW_ExprOp_RegX: { U64 reg_idx = 0; step_cursor += dw_based_range_read(base, range, step_cursor, size_param, ®_idx); stashed_loc.kind = DW_SimpleLocKind_Register; stashed_loc.reg_idx = reg_idx; } break; //// implicit location descriptions //// case DW_ExprOp_ImplicitValue: { U64 size = 0; step_cursor += dw_based_range_read(base, range, step_cursor, size_param, &size); if (step_cursor + size <= range.max) { void *data = (U8*)base + range.min + step_cursor; stashed_loc.kind = DW_SimpleLocKind_ValueLong; stashed_loc.val_long.str = (U8*)data; stashed_loc.val_long.size = size; } else { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_BadData; goto finish; } step_cursor += size; } break; case DW_ExprOp_StackValue: { U64 x = dw_expr__stack_pop(&stack); stashed_loc.kind = DW_SimpleLocKind_Value; stashed_loc.val = x; } break; //// composite location descriptions //// case DW_ExprOp_Piece: case DW_ExprOp_BitPiece: { if (arena_optional == 0) { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_MissingArenaForComposite; goto finish; } else { // determine this piece's size & offset U64 bit_size = 0; U64 bit_off = 0; B32 is_bit_loc = 0; switch (op) { case DW_ExprOp_Piece: { U64 size = 0; step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &size); bit_size = size*8; } break; case DW_ExprOp_BitPiece: { step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &bit_size); step_cursor += dw_based_range_read_uleb128(base, range, step_cursor, &bit_off); is_bit_loc = 1; } break; } // determine this piece's location information DW_SimpleLoc piece_loc = stashed_loc; if (piece_loc.kind == DW_SimpleLocKind_Address) { if (dw_expr__stack_is_empty(&stack)) { piece_loc.kind = DW_SimpleLocKind_Empty; } else { U64 x = dw_expr__stack_pop(&stack); piece_loc.addr = x; } } // push the piece DW_Piece *piece = push_array(arena_optional, DW_Piece, 1); SLLQueuePush(result.first_piece, result.last_piece, piece); piece->loc = piece_loc; piece->bit_size = bit_size; piece->bit_off = bit_off; piece->is_bit_loc = is_bit_loc; // zero the stached loc MemoryZeroStruct(&stashed_loc); } } break; //// final fallback //// default: { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_NotSupported; goto finish; } break; } // increment cursor cursor = step_cursor; } // advance cursor or finish call if (cursor < call->size) { call->cursor = cursor; } else { dw_expr__call_pop(&call_stack); } // advance step counter step_counter += 1; if (step_counter == max_step_count) { stashed_loc.kind = DW_SimpleLocKind_Fail; stashed_loc.fail_kind = DW_LocFailKind_TimeOut; goto finish; } } finish:; // non-piece location { DW_SimpleLoc loc = stashed_loc; if (result.first_piece == 0) { // normal location resolution loc = stashed_loc; if (loc.kind == DW_SimpleLocKind_Address) { if (dw_expr__stack_is_empty(&stack)) { loc.kind = DW_SimpleLocKind_Empty; } else { U64 x = dw_expr__stack_pop(&stack); loc.addr = x; } } } // non-piece location resolution after composite else { // change the default kind to empty if (loc.kind == DW_SimpleLocKind_Address) { loc.kind = DW_SimpleLocKind_Empty; } // the non-piece should either be empty or fail if (loc.kind != DW_SimpleLocKind_Empty && loc.kind != DW_SimpleLocKind_Fail) { loc.kind = DW_SimpleLocKind_Fail; loc.fail_kind = DW_LocFailKind_BadData; } } result.non_piece_loc = loc; } // clear stack scratch_end(scratch); return result; #endif DW_Location result = {0}; return result; } //- dw expr val stack internal DW_ExprStack dw_expr__stack_make(Arena *arena) { DW_ExprStack result = {0}; return result; } internal void dw_expr__stack_push(Arena *arena, DW_ExprStack *stack, U64 x) { DW_ExprStackNode *node = stack->free_nodes; if (node == 0) { SLLStackPop(stack->free_nodes); } else { node = push_array(arena, DW_ExprStackNode, 1); } SLLStackPush(stack->stack, node); node->val = x; stack->count += 1; } internal U64 dw_expr__stack_pop(DW_ExprStack *stack) { U64 result = 0; DW_ExprStackNode *node = stack->stack; if (node != 0) { SLLStackPop(stack->stack); stack->count -= 1; result = node->val; } return result; } internal U64 dw_expr__stack_pick(DW_ExprStack *stack, U64 idx) { U64 result = 0; if (idx < stack->count) { U64 counter = idx; DW_ExprStackNode *node = stack->stack; for (;node != 0 && counter > 0; node = node->next, counter -= 1); if (counter == 0 && node != 0) { result = node->val; } } return result; } internal B32 dw_expr__stack_is_empty(DW_ExprStack *stack) { B32 result = (stack->count == 0); return result; } //- dw expr call stack internal DW_ExprCall* dw_expr__call_top(DW_ExprCallStack *stack) { DW_ExprCall *call = stack->stack; return call; } internal void dw_expr__call_push(Arena *arena, DW_ExprCallStack *stack, void *ptr, U64 size) { DW_ExprCall *call = 0; if (call != 0) { SLLStackPop(stack->free_calls); } else { call = push_array(arena, DW_ExprCall, 1); } MemoryZeroStruct(call); SLLStackPush(stack->stack, call); stack->depth += 1; } internal void dw_expr__call_pop(DW_ExprCallStack *stack) { DW_ExprCall *top = stack->stack; if (top != 0) { SLLStackPop(stack->stack); SLLStackPush(stack->free_calls, top); } } //- analysis tasks internal DW_ExprAnalysisTask* dw_expr__analysis_task_from_p(DW_ExprAnalysisTask *first, U64 p) { DW_ExprAnalysisTask *result = 0; for (DW_ExprAnalysisTask *task = first; task != 0; task = task->next) { if (task->p == p) { result = task; break; } } return result; }