From 43cb8b7b1a2c6420e9bfa0846a75f44955a26317 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Tue, 16 Dec 2025 17:32:12 -0800 Subject: [PATCH] handle DW_Op_FBReg, DW_ExprOp_Deref, DW_ExprOp_BReg*, and DW_ExprOp_BRegX --- src/dwarf/dwarf.h | 4 +- src/dwarf/dwarf_expr.c | 74 +++++++++++++++- src/dwarf/dwarf_expr.h | 2 +- src/torture/torture.c | 192 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 263 insertions(+), 9 deletions(-) diff --git a/src/dwarf/dwarf.h b/src/dwarf/dwarf.h index 349e8b35..9f3b0dfc 100644 --- a/src/dwarf/dwarf.h +++ b/src/dwarf/dwarf.h @@ -1675,8 +1675,8 @@ typedef enum DW_ExprOpEnum X(Ds, 53, ds, 0, 2) \ X(Fs, 54, fs, 0, 2) \ X(Gs, 55, gs, 0, 2) \ - X(FsBase, 58, fsbase, 0, 0) \ - X(GsBase, 59, gsbase, 0, 0) + X(FsBase, 58, fsbase, 0, 8) \ + X(GsBase, 59, gsbase, 0, 8) // privileged registers: // X(Tr, 62, nil, 0, 0) \ // X(Ldtr, 63, nil, 0, 0) diff --git a/src/dwarf/dwarf_expr.c b/src/dwarf/dwarf_expr.c index 74bc8a80..89087294 100644 --- a/src/dwarf/dwarf_expr.c +++ b/src/dwarf/dwarf_expr.c @@ -1128,7 +1128,17 @@ dw_expr_inst_from_delta(DW_ExprInst *inst, S16 delta) } internal DW_ExprEvalResult -dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 cfa, U64 tls, U64 exec_op_limit, DW_Expr expr, MachineOp_RegRead *reg_read, void *reg_read_ud, DW_ExprValue *value_out) +dw_eval_expr(Arena *arena, + Arch arch, + DW_Format format, + U64 frame_base, + U64 cfa, + U64 tls, + U64 exec_op_limit, + DW_Expr expr, + MachineOp_RegRead *reg_read, void *reg_read_ud, + MachineOp_MemRead *mem_read, void *mem_read_ud, + DW_ExprValue *value_out) { Temp scratch = scratch_begin(&arena, 1); @@ -1201,6 +1211,29 @@ dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 cfa, U64 tls, U64 ex dw_expr_stack_push_unsigned(scratch.arena, stack, reg_value, reg_size); } 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: { + DW_Reg reg_id = inst->opcode - DW_ExprOp_BReg0; + U64 reg_size = dw_reg_size_from_code(arch, reg_id); + AssertAlways(reg_size <= byte_size_from_arch(arch)); + + U64 reg_value; + MachineOpResult reg_read_result = reg_read(reg_id, ®_value, reg_size, reg_read_ud); + if (reg_read_result != MachineOpResult_Ok) { goto exit; } + + U64 addr = (U64)((S64)reg_value + inst->operands[0].s64); + dw_expr_stack_push(scratch.arena, stack, (DW_ExprValue){ .type = DW_ExprValueType_Addr, .addr = addr}); + } break; + case DW_ExprOp_RegX: { U64 reg_size = dw_reg_size_from_code(arch, inst->operands[0].u64); U8 *reg_value = push_array(scratch.arena, U8, reg_size); @@ -1209,6 +1242,24 @@ dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 cfa, U64 tls, U64 ex dw_expr_stack_push_unsigned(scratch.arena, stack, reg_value, reg_size); } break; + case DW_ExprOp_BRegX: { + DW_Reg reg_id = (DW_Reg)inst->operands[0].u64; + U64 reg_size = dw_reg_size_from_code(arch, reg_id); + AssertAlways(reg_size <= byte_size_from_arch(arch)); + + U64 reg_value; + MachineOpResult reg_read_result = reg_read(reg_id, ®_value, reg_size, reg_read_ud); + if (reg_read_result != MachineOpResult_Ok) { goto exit; } + + U64 addr = (U64)((S64)reg_value + inst->operands[1].s64); + dw_expr_stack_push(scratch.arena, stack, (DW_ExprValue){ .type = DW_ExprValueType_Addr, .addr = addr}); + } break; + + case DW_ExprOp_FBReg: { + U64 addr = frame_base + inst->operands[0].s64; + dw_expr_stack_push(scratch.arena, stack, (DW_ExprValue){ .type = DW_ExprValueType_Addr, .addr = addr}); + } break; + case DW_ExprOp_ImplicitValue: { if (inst->operands[0].block.size <= sizeof(U512)) { dw_expr_stack_push_unsigned(scratch.arena, stack, inst->operands[0].block.str, inst->operands[0].block.size); @@ -1340,7 +1391,7 @@ dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 cfa, U64 tls, U64 ex case DW_ExprOp_GNU_EntryValue: { DW_Expr entry_value_expr = dw_expr_from_data(scratch.arena, format, byte_size_from_arch(arch), inst->operands[0].block); DW_ExprValue entry_value; - result = dw_eval_expr(scratch.arena, arch, format, cfa, tls, exec_op_limit, entry_value_expr, reg_read, reg_read_ud, &entry_value); + result = dw_eval_expr(scratch.arena, arch, format, frame_base, cfa, tls, exec_op_limit, entry_value_expr, reg_read, reg_read_ud, mem_read, mem_read_ud, &entry_value); if (result != DW_ExprEvalResult_Ok) { goto exit; } dw_expr_stack_push(scratch.arena, stack, entry_value); } break; @@ -1401,6 +1452,25 @@ dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 cfa, U64 tls, U64 ex dw_expr_stack_pop(stack); } break; + case DW_ExprOp_Deref: { + DW_ExprValue value = dw_expr_stack_pop(stack); + + // expression must be of an integral type + if (!DW_ExprValueType_IsInt(value.type)) { goto exit; } + + // treat value as an address + DW_ExprValue addr = dw_expr_cast(value, DW_ExprValueType_Addr); + + // read pointer size from the address + U64 addr_size = byte_size_from_arch(arch); + U8 *generic_value = push_array(scratch.arena, U8, addr_size); + MachineOpResult read_result = mem_read(addr.addr, generic_value, addr_size, mem_read_ud); + if (read_result != MachineOpResult_Ok) { goto exit; } + + // push generic data + dw_expr_stack_push(scratch.arena, stack, (DW_ExprValue){ .type = DW_ExprValueType_Generic, .generic = str8(generic_value, addr_size) }); + } break; + case DW_ExprOp_StackValue: { stack->top->v.type = dw_expr_unsigned_value_type_from_bit_size(bit_size_from_arch(arch)); } break; diff --git a/src/dwarf/dwarf_expr.h b/src/dwarf/dwarf_expr.h index 6237d2e8..278481b1 100644 --- a/src/dwarf/dwarf_expr.h +++ b/src/dwarf/dwarf_expr.h @@ -235,7 +235,7 @@ internal DW_ExprValueNode * dw_expr_stack_pick(DW_ExprStack *stack, U64 idx); internal String8 dw_string_from_expr_value(Arena *arena, U64 addr_size, DW_ExprValue v); // evaluator -internal DW_ExprEvalResult dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 cfa, U64 tls_base, U64 op_limit, DW_Expr expr, MachineOp_RegRead *reg_read, void *reg_read_ud, DW_ExprValue *value_out); +internal DW_ExprEvalResult dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 frame_base, U64 cfa, U64 tls_base, U64 op_limit, DW_Expr expr, MachineOp_RegRead *reg_read, void *reg_read_ud, MachineOp_MemRead *mem_read, void *mem_read_ud, DW_ExprValue *value_out); // encoder internal String8 dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count); diff --git a/src/torture/torture.c b/src/torture/torture.c index 725d657b..2a9690cd 100644 --- a/src/torture/torture.c +++ b/src/torture/torture.c @@ -4966,13 +4966,13 @@ t_value_in_register(void) MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); // compile a simple program which reads the value from register 3 - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Reg3) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Reg3) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); // evaluate the program DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, &expr_value); + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); // validate eval result if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } @@ -4985,6 +4985,184 @@ exit:; return result; } +internal T_Result +t_value_in_x_register(void) +{ + Temp scratch = scratch_begin(0, 0); + T_Result result = T_Result_Fail; + + // setup register context + REGS_RegBlockX64 regs = {0}; + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_RegX64_FsBase); + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 value = 0xc0ffee; + MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); + + // compile a simple program which reads the value from register 3 + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(RegX), DW_ExprEnc_ULEB128(DW_RegX64_FsBase) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + // evaluate the program + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); + + // validate eval result + if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } + if (expr_value.type != DW_ExprValueType_U64) { goto exit; } + if (expr_value.u64 != value) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_address_of_value(void) +{ + Temp scratch = scratch_begin(0, 0); + T_Result result = T_Result_Fail; + + // compile a simple program which reads address + U64 addr = 0xdeadbeef; + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Addr), DW_ExprEnc_U64(addr) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + // evaluate the program + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, 0, 0, 0, 0, &expr_value); + + // validate eval result + if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } + if (expr_value.type != DW_ExprValueType_Addr) { goto exit; } + if (expr_value.addr != addr) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_register_relative_variable(void) +{ + Temp scratch = scratch_begin(0, 0); + T_Result result = T_Result_Fail; + + // setup register context + REGS_RegBlockX64 regs = {0}; + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_BReg11 - DW_ExprOp_BReg0); + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 value = 1; + MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); + + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(BReg11), DW_ExprEnc_SLEB128(44) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); + + // validate eval result + if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } + if (expr_value.type != DW_ExprValueType_Addr) { goto exit; } + if (expr_value.addr != (1 + 44)) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_frame_relative_variable(void) +{ + Temp scratch = scratch_begin(0, 0); + T_Result result = T_Result_Fail; + + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(FBReg), DW_ExprEnc_SLEB128(-50) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + U64 frame_base = 123; + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, frame_base, 0, 0, max_U64, expr, 0, 0, 0, 0, &expr_value); + + if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } + if (expr_value.type != DW_ExprValueType_Addr) { goto exit; } + if (expr_value.addr != frame_base -50) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal +MACHINE_OP_MEM_READ(t_machine_op_mem_read) +{ + MemoryCopy(buffer, PtrFromInt(addr), buffer_size); + return MachineOpResult_Ok; +} + +internal T_Result +t_call_by_reference(void) +{ + Temp scratch = scratch_begin(0, 0); + T_Result result = T_Result_Fail; + + U8 *memory = push_array(scratch.arena, U8, 128); + U64 value = 0xc0ffee; + MemoryCopy(memory + 32, &value, sizeof(value)); + + REGS_RegBlockX64 regs = {0}; + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, 58); // fsbase + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 memory_ptr = IntFromPtr(memory); + MemoryCopy((U8 *)®s + reg_range.min, &memory_ptr, sizeof(memory_ptr)); + + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(BRegX), DW_ExprEnc_ULEB128(58), DW_ExprEnc_SLEB128(32), DW_ExprEnc_Op(Deref) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, t_machine_op_mem_read, 0, &expr_value); + + if (expr_value.type != DW_ExprValueType_Generic) { goto exit; } + if (expr_value.generic.size != sizeof(U64)) { goto exit; } + if (*(U64 *)expr_value.generic.str != value) { goto exit; } + + result = T_Result_Pass; + exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_plus_uconst(void) +{ + Temp scratch = scratch_begin(0, 0); + T_Result result = T_Result_Fail; + + U64 struct_addr = 0x123; + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Addr), DW_ExprEnc_Addr(struct_addr), DW_ExprEnc_Op(PlusUConst), DW_ExprEnc_ULEB128(4) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, 0, 0, t_machine_op_mem_read, 0, &expr_value); + + if (expr_value.type != DW_ExprValueType_Addr) { goto exit; } + if (expr_value.addr != 0x123 + 4) { goto exit; } + + result = T_Result_Pass; + exit:; + scratch_end(scratch); + return result; +} + //////////////////////////////////////////////////////////////// internal void @@ -5067,6 +5245,12 @@ entry_point(CmdLine *cmdline) // DWARF Expression Tests T(value_in_register), + T(value_in_x_register), + T(address_of_value), + T(register_relative_variable), + T(frame_relative_variable), + T(call_by_reference), + T(plus_uconst), #undef T };