handle DW_Op_FBReg, DW_ExprOp_Deref, DW_ExprOp_BReg*, and DW_ExprOp_BRegX

This commit is contained in:
Nikita Smith
2025-12-16 17:32:12 -08:00
parent 0aea6c4e8f
commit 43cb8b7b1a
4 changed files with 263 additions and 9 deletions
+2 -2
View File
@@ -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)
+72 -2
View File
@@ -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, &reg_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, &reg_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;
+1 -1
View File
@@ -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);
+188 -4
View File
@@ -4966,13 +4966,13 @@ t_value_in_register(void)
MemoryCopy((U8 *)&regs + 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, &regs, &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, &regs, 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 *)&regs + 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, &regs, 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 *)&regs + 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, &regs, 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 *)&regs + 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, &regs, 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
};