rudy_dwarf/
expressions.rs1use anyhow::{Context, Result};
4
5use crate::{file::Expression, Die, DwarfDb};
6
7#[salsa::tracked]
9pub fn get_location_expr<'db>(
10 db: &'db dyn DwarfDb,
11 entry: Die,
12 attr: gimli::DwAt,
13) -> Option<Expression> {
14 let location = match entry.get_attr(db, attr) {
15 Ok(l) => l,
16 Err(e) => {
17 tracing::warn!(
18 "Failed to get location attribute for entry: {}: {e}",
19 entry.print(db)
20 );
21 return None;
22 }
23 };
24
25 let gimli::AttributeValue::Exprloc(expr) = location else {
26 tracing::error!("Location not an expression for entry: {}", entry.print(db));
27 return None;
28 };
29
30 Some(expr)
31}
32
33pub trait ExpressionContext {
34 fn get_register(&self, register: u16) -> Result<u64>;
35 fn get_stack_pointer(&self) -> Result<u64>;
36}
37
38fn get_function_frame_base(
40 db: &dyn DwarfDb,
41 function_entry: Die,
42 context: &dyn ExpressionContext,
43) -> anyhow::Result<u64> {
44 let Some(loc_exp) = get_location_expr(db, function_entry, gimli::DW_AT_frame_base) else {
45 anyhow::bail!("Failed to get location expression for function");
46 };
47 let unit_ref = function_entry.unit_ref(db)?;
49
50 let mut eval = loc_exp.evaluation(unit_ref.encoding());
51 let mut result = eval.evaluate()?;
52 let result = loop {
53 match result {
54 gimli::EvaluationResult::Complete => {
55 break eval.result();
57 }
58 gimli::EvaluationResult::RequiresCallFrameCfa => {
59 let sp = context.get_stack_pointer()?;
60 result = eval.resume_with_call_frame_cfa(sp).with_context(|| {
61 format!("Failed to resume evaluation with call frame CFA with sp: {sp:#x}")
62 })?;
63 }
64 r => {
65 todo!("handle incomplete evaluation: {r:?}");
66 }
67 }
68 };
69
70 debug_assert_eq!(result.len(), 1, "got: {result:#?}");
71
72 let result = result[0].clone();
73
74 match result.location {
75 gimli::Location::Address { address } => {
77 tracing::debug!("frame base address: {address:#x}");
78 Ok(address)
79 }
80 gimli::Location::Register { register, .. } => {
81 let reg_value = context.get_register(register.0)?;
82 tracing::debug!("frame base register value: {reg_value:#x}");
83 Ok(reg_value)
84 }
85 loc => Err(anyhow::anyhow!(
86 "Unexpected location type for frame base: {loc:?}"
87 )),
88 }
89}
90
91pub fn resolve_data_location(
93 db: &dyn DwarfDb,
94 function: Die,
95 base_address: u64,
96 variable_entry_id: Die,
97 context: &dyn ExpressionContext,
98) -> Result<Option<u64>> {
99 let Some(expr) = get_location_expr(db, variable_entry_id, gimli::DW_AT_location) else {
100 return Ok(None);
101 };
102
103 let unit_ref = function.unit_ref(db)?;
104
105 let mut eval = expr.evaluation(unit_ref.encoding());
108 let mut result = eval.evaluate()?;
109 let result = loop {
110 match result {
111 gimli::EvaluationResult::Complete => {
112 break eval.result();
114 }
115 gimli::EvaluationResult::RequiresFrameBase => {
116 let frame_base = get_function_frame_base(db, function, context)?;
118 result = eval.resume_with_frame_base(frame_base)?;
119 }
120 gimli::EvaluationResult::RequiresRegister { register, .. } => {
121 let reg = register.0;
122 let reg_value = context.get_register(reg)?;
123 tracing::debug!("register value: {reg} = {reg_value:#x}");
124 result = eval.resume_with_register(gimli::Value::Generic(reg_value))?;
125 }
126 gimli::EvaluationResult::RequiresRelocatedAddress(addr) => {
127 let relocated_addr = base_address + addr;
130
131 tracing::debug!("relocated address: {addr:#x} -> {relocated_addr:#x}",);
132 result = eval.resume_with_relocated_address(relocated_addr)?;
133 }
134 r => {
135 todo!("handle incomplete evaluation: {r:?}");
136 }
137 }
138 };
139
140 if let [piece] = &result[..] {
142 tracing::debug!("single piece: {piece:#?}");
143 match &piece.location {
144 gimli::Location::Address { address } => {
145 tracing::debug!("address: {address:#x}");
146 Ok(Some(*address))
147 }
148 loc => {
149 todo!("handle location: {loc:#?}");
150 }
151 }
152 } else {
153 todo!("support multiple pieces: {result:#?}");
154 }
155}