Skip to main content

bugstalker/debugger/variable/
execute.rs

1use crate::debugger::context::gcx;
2use crate::debugger::debugee::dwarf::DebugInformation;
3use crate::debugger::debugee::dwarf::eval::{EvaluationContext, ExpressionEvaluator};
4use crate::debugger::debugee::dwarf::r#type::ComplexType;
5use crate::debugger::debugee::dwarf::unit::BsUnit;
6use crate::debugger::debugee::dwarf::unit::die_ref::{Argument, FatDieRef, Typed, Variable};
7use crate::debugger::error::Error;
8use crate::debugger::error::Error::FunctionNotFound;
9use crate::debugger::variable::dqe::{DataCast, Dqe, PointerCast, Selector};
10use crate::debugger::variable::value::Value;
11use crate::debugger::variable::value::parser::{ParseContext, ValueModifiers, ValueParser};
12use crate::debugger::variable::r#virtual::VirtualVariableDie;
13use crate::debugger::variable::{Identity, ObjectBinaryRepr};
14use crate::debugger::{Debugger, read_memory_by_pid};
15use crate::{ref_resolve_unit_call, resolve_unit_call, weak_error};
16use bytes::Bytes;
17use gimli::Range;
18use std::fmt::Debug;
19use std::rc::Rc;
20
21#[derive(Debug, Clone, Copy, PartialEq)]
22pub enum QueryResultKind {
23    /// Result value is an argument or variable
24    Root,
25    /// Result value calculated using DQE
26    Expression,
27}
28
29/// Result of DQE evaluation.
30#[derive(Clone)]
31pub struct QueryResult<'a> {
32    // TODO tmp pub
33    pub value: Option<Value>,
34    scope: Option<Box<[Range]>>,
35    kind: QueryResultKind,
36    base_type: Rc<ComplexType>,
37    identity: Identity,
38    evcx_builder: EvaluationContextBuilder<'a>,
39}
40
41impl QueryResult<'_> {
42    /// Return CU in which result values are located.
43    pub fn unit(&self) -> &BsUnit {
44        self.evcx_builder.unit()
45    }
46
47    /// Return underlying typed value representation.
48    #[inline(always)]
49    pub fn value(&self) -> &Value {
50        self.value.as_ref().expect("should be `Some`")
51    }
52
53    /// Return underlying typed value representation.
54    #[inline(always)]
55    pub fn into_value(mut self) -> Value {
56        self.value.take().expect("should be `Some`")
57    }
58
59    /// Return underlying value and result identity (variable or argument identity).
60    #[inline(always)]
61    pub fn into_identified_value(mut self) -> (Identity, Value) {
62        (self.identity, self.value.take().expect("should be `Some`"))
63    }
64
65    /// Return result kind:
66    /// - `Root` kind means that value is an argument or variable
67    /// - `Expression` kind means that value calculated using DQE
68    #[inline(always)]
69    pub fn kind(&self) -> QueryResultKind {
70        self.kind
71    }
72
73    /// Return type graph using for parse a result.
74    #[inline(always)]
75    pub fn type_graph(&self) -> &ComplexType {
76        self.base_type.as_ref()
77    }
78
79    /// Return result identity.
80    #[inline(always)]
81    pub fn identity(&self) -> &Identity {
82        &self.identity
83    }
84
85    /// Return variable or argument scope. Scope is a PC ranges where value is valid,
86    /// `None` for global or virtual variables.
87    #[inline(always)]
88    pub fn scope(&self) -> &Option<Box<[Range]>> {
89        &self.scope
90    }
91
92    /// Evaluate any function with evaluation context.
93    pub fn with_evcx<T, F: FnOnce(&EvaluationContext) -> T>(&self, cb: F) -> T {
94        self.evcx_builder.with_evcx(cb)
95    }
96
97    /// Modify the underlying value and return a new result extended from the current one.
98    pub fn modify_value<F: FnOnce(&ParseContext, Value) -> Option<Value>>(
99        mut self,
100        cb: F,
101    ) -> Option<Self> {
102        let value = self.value.take().expect("should be `Some`");
103        let type_graph = self.type_graph();
104        let eval_cb = |evcx: &EvaluationContext| {
105            let pcx = &ParseContext { evcx, type_graph };
106            cb(pcx, value)
107        };
108        let new_value = self.evcx_builder.with_evcx(eval_cb)?;
109        self.value = Some(new_value);
110        Some(self)
111    }
112}
113
114impl PartialEq for QueryResult<'_> {
115    fn eq(&self, other: &Self) -> bool {
116        self.value == other.value && self.identity == other.identity
117    }
118}
119
120#[derive(Clone)]
121enum EvaluationContextBuilder<'a> {
122    Ready(&'a Debugger, ExpressionEvaluator<'a>),
123    Virtual {
124        debugger: &'a Debugger,
125        debug_info: &'a DebugInformation,
126        unit: &'a BsUnit,
127    },
128}
129
130impl EvaluationContextBuilder<'_> {
131    pub fn unit(&self) -> &BsUnit {
132        match self {
133            EvaluationContextBuilder::Ready(_, evaluator) => evaluator.unit(),
134            EvaluationContextBuilder::Virtual { unit, .. } => unit,
135        }
136    }
137
138    fn with_evcx<T, F: FnOnce(&EvaluationContext) -> T>(&self, cb: F) -> T {
139        let evaluator;
140        let evcx = match self {
141            EvaluationContextBuilder::Ready(debugger, evaluator) => EvaluationContext {
142                evaluator,
143                ecx: debugger.ecx(),
144            },
145            EvaluationContextBuilder::Virtual {
146                debugger,
147                debug_info,
148                unit,
149                ..
150            } => {
151                let dwarf = debug_info.dwarf();
152                evaluator = resolve_unit_call!(
153                    dwarf,
154                    unit,
155                    evaluator,
156                    &debugger.debugee,
157                    debug_info.dwarf()
158                );
159                EvaluationContext {
160                    evaluator: &evaluator,
161                    ecx: debugger.ecx(),
162                }
163            }
164        };
165        cb(&evcx)
166    }
167}
168
169#[macro_export]
170macro_rules! type_from_cache {
171    ($variable: expr, $cache: expr) => {
172        $variable
173            .deref_ensure()
174            .type_ref()
175            .and_then(
176                |type_ref| match $cache.entry(($variable.unit().id, type_ref)) {
177                    std::collections::hash_map::Entry::Occupied(o) => {
178                        Some(std::rc::Rc::clone(o.get()))
179                    }
180                    std::collections::hash_map::Entry::Vacant(v) => $variable.r#type().map(|t| {
181                        let t = std::rc::Rc::new(t);
182                        v.insert(t.clone());
183                        t
184                    }),
185                },
186            )
187            .ok_or_else(|| {
188                $crate::debugger::variable::value::ParsingError::Assume(
189                    $crate::debugger::variable::value::AssumeError::NoType("variable"),
190                )
191            })
192    };
193}
194
195/// Evaluate DQE at current location.
196pub struct DqeExecutor<'a> {
197    debugger: &'a Debugger,
198}
199
200impl<'dbg> DqeExecutor<'dbg> {
201    pub fn new(debugger: &'dbg Debugger) -> Self {
202        Self { debugger }
203    }
204
205    fn variable_die_by_selector(
206        &self,
207        selector: &Selector,
208    ) -> Result<Vec<FatDieRef<'dbg, Variable>>, Error> {
209        let ecx = self.debugger.ecx();
210
211        let debugee = &self.debugger.debugee;
212        let (current_func, _) = debugee
213            .debug_info(ecx.location().pc)?
214            .find_function_by_pc(ecx.location().global_pc)?
215            .ok_or(FunctionNotFound(ecx.location().global_pc))?;
216
217        let vars = match selector {
218            Selector::Name {
219                var_name,
220                local_only: local,
221            } => {
222                let local_variants = current_func
223                    .local_variable(ecx.location().global_pc, var_name)
224                    .map(|v| vec![v])
225                    .unwrap_or_default();
226
227                let local = *local;
228
229                // local variables is in priority anyway, if there are no local variables and
230                // selector allow non-locals then try to search in a whole object
231                if !local && local_variants.is_empty() {
232                    debugee
233                        .debug_info(ecx.location().pc)?
234                        .find_variables(ecx.location(), var_name)?
235                } else {
236                    local_variants
237                }
238            }
239            Selector::Any => current_func.local_variables(ecx.location().global_pc),
240        };
241
242        Ok(vars)
243    }
244
245    fn param_die_by_selector(
246        &self,
247        selector: &Selector,
248    ) -> Result<Vec<FatDieRef<'dbg, Argument>>, Error> {
249        let ecx_loc = self.debugger.ecx().location();
250        let debugee = &self.debugger.debugee;
251        let (current_function, _) = debugee
252            .debug_info(ecx_loc.pc)?
253            .find_function_by_pc(ecx_loc.global_pc)?
254            .ok_or(FunctionNotFound(ecx_loc.global_pc))?;
255        let params = current_function.parameters();
256        let params = match selector {
257            Selector::Name { var_name, .. } => params
258                .into_iter()
259                .filter(|r| r.deref_ensure().name().as_ref() == Some(var_name))
260                .collect::<Vec<_>>(),
261            Selector::Any => params,
262        };
263        Ok(params)
264    }
265
266    /// Select variables or arguments from debugee state.
267    fn apply_select_die(
268        &self,
269        selector: &Selector,
270        on_args: bool,
271    ) -> Result<Vec<QueryResult<'dbg>>, Error> {
272        fn root_from_die<'dbg, H: Typed>(
273            debugger: &'dbg Debugger,
274            die_ref: &FatDieRef<'dbg, H>,
275            ranges: Option<Box<[Range]>>,
276        ) -> Option<QueryResult<'dbg>> {
277            let r#type = gcx().with_type_cache(|tc| weak_error!(type_from_cache!(die_ref, tc)))?;
278
279            let evaluator = ref_resolve_unit_call!(
280                die_ref,
281                evaluator,
282                &debugger.debugee,
283                die_ref.debug_info.dwarf()
284            );
285            let context_builder = EvaluationContextBuilder::Ready(debugger, evaluator);
286
287            let value = context_builder.with_evcx(|evcx| {
288                let data = die_ref.read_value(debugger.ecx(), &debugger.debugee, &r#type);
289
290                let parser = ValueParser::new();
291                let pcx = &ParseContext {
292                    evcx,
293                    type_graph: &r#type,
294                };
295                let modifiers = &ValueModifiers::from_identity(pcx, Identity::from_die(die_ref));
296                parser.parse(pcx, data, modifiers)
297            })?;
298
299            Some(QueryResult {
300                value: Some(value),
301                scope: ranges,
302                kind: QueryResultKind::Root,
303                base_type: r#type,
304                identity: Identity::from_die(die_ref),
305                evcx_builder: context_builder,
306            })
307        }
308
309        match on_args {
310            true => {
311                let params = self.param_die_by_selector(selector)?;
312                Ok(params
313                    .iter()
314                    .filter_map(|arg_die| {
315                        root_from_die(
316                            self.debugger,
317                            arg_die,
318                            arg_die.max_range().map(|r| {
319                                let scope: Box<[Range]> = Box::new([r]);
320                                scope
321                            }),
322                        )
323                    })
324                    .collect())
325            }
326            false => {
327                let vars = self.variable_die_by_selector(selector)?;
328                Ok(vars
329                    .iter()
330                    .filter_map(|var_die| root_from_die(self.debugger, var_die, var_die.ranges()))
331                    .collect())
332            }
333        }
334    }
335
336    /// Create virtual DIE from an existing type,
337    /// then return a query result with a value from this DIE and address in debugee memory.
338    fn apply_ptr_cast_op(&self, ptr_cast: &PointerCast) -> Result<QueryResult<'dbg>, Error> {
339        let mut var_die = VirtualVariableDie::workpiece();
340        let var_die_ref = var_die.init_with_type(&self.debugger.debugee, &ptr_cast.ty)?;
341
342        let r#type = gcx().with_type_cache(|tc| type_from_cache!(var_die_ref, tc))?;
343
344        let context_builder = EvaluationContextBuilder::Virtual {
345            debugger: self.debugger,
346            debug_info: var_die_ref.debug_info,
347            unit: var_die_ref.unit(),
348        };
349
350        let value = context_builder.with_evcx(|evcx| {
351            let data = ObjectBinaryRepr {
352                raw_data: Bytes::copy_from_slice(&ptr_cast.ptr.to_le_bytes()),
353                address: None,
354                size: std::mem::size_of::<usize>(),
355            };
356
357            let parser = ValueParser::new();
358            let pcx = &ParseContext {
359                evcx,
360                type_graph: &r#type,
361            };
362            parser.parse(pcx, Some(data), &ValueModifiers::default())
363        });
364
365        Ok(QueryResult {
366            value,
367            scope: None,
368            kind: QueryResultKind::Expression,
369            base_type: r#type,
370            identity: Identity::default(),
371            evcx_builder: context_builder,
372        })
373    }
374
375    /// Create virtual DIE from an existing type,
376    /// then return a query result with a value from this DIE and address in debugee memory.
377    fn apply_data_cast(&self, data_cast: &DataCast) -> Result<QueryResult<'dbg>, Error> {
378        let mut var_die = VirtualVariableDie::workpiece();
379        let debug_info = self
380            .debugger
381            .debugee
382            .debug_info_from_file(&data_cast.ty_debug_info)?;
383        let var_die_ref = var_die.init_with_known_type(
384            debug_info,
385            data_cast.ty_unit_off,
386            data_cast.ty_die_off,
387        )?;
388
389        let r#type = gcx().with_type_cache(|tc| type_from_cache!(var_die_ref, tc))?;
390
391        let context_builder = EvaluationContextBuilder::Virtual {
392            debugger: self.debugger,
393            debug_info: var_die_ref.debug_info,
394            unit: var_die_ref.unit(),
395        };
396
397        let value = context_builder.with_evcx(|evcx| {
398            let size = r#type.type_size_in_bytes(evcx, r#type.root())? as usize;
399
400            let raw_data = weak_error!(read_memory_by_pid(
401                evcx.ecx.pid_on_focus(),
402                data_cast.ptr,
403                size
404            ))?;
405
406            let data = ObjectBinaryRepr {
407                raw_data: Bytes::copy_from_slice(&raw_data),
408                address: Some(data_cast.ptr),
409                size,
410            };
411
412            let parser = ValueParser::new();
413            let pcx = &ParseContext {
414                evcx,
415                type_graph: &r#type,
416            };
417            parser.parse(pcx, Some(data), &ValueModifiers::default())
418        });
419
420        Ok(QueryResult {
421            value,
422            scope: None,
423            kind: QueryResultKind::Expression,
424            base_type: r#type,
425            identity: Identity::default(),
426            evcx_builder: context_builder,
427        })
428    }
429
430    fn apply_dqe(&self, dqe: &Dqe, on_args: bool) -> Result<Vec<QueryResult<'dbg>>, Error> {
431        match dqe {
432            Dqe::Variable(selector) => self.apply_select_die(selector, on_args),
433            Dqe::PtrCast(ptr_cast) => self.apply_ptr_cast_op(ptr_cast).map(|q| vec![q]),
434            Dqe::DataCast(data_cast) => self.apply_data_cast(data_cast).map(|q| vec![q]),
435            Dqe::Field(next, field) => {
436                let results = self.apply_dqe(next, on_args)?;
437                Ok(results
438                    .into_iter()
439                    .filter_map(|q| q.modify_value(|_, val| val.field(field)))
440                    .collect())
441            }
442            Dqe::Index(next, idx) => {
443                let results = self.apply_dqe(next, on_args)?;
444                Ok(results
445                    .into_iter()
446                    .filter_map(|q| q.modify_value(|_, val| val.index(idx)))
447                    .collect())
448            }
449            Dqe::Slice(next, left, right) => {
450                let results = self.apply_dqe(next, on_args)?;
451                Ok(results
452                    .into_iter()
453                    .filter_map(|q| q.modify_value(|pcx, val| val.slice(pcx, *left, *right)))
454                    .collect())
455            }
456            Dqe::Deref(next) => {
457                let results = self.apply_dqe(next, on_args)?;
458                Ok(results
459                    .into_iter()
460                    .filter_map(|q| q.modify_value(|pcx, val| val.deref(pcx)))
461                    .collect())
462            }
463            Dqe::Address(next) => {
464                let results = self.apply_dqe(next, on_args)?;
465                Ok(results
466                    .into_iter()
467                    .filter_map(|q| q.modify_value(|pcx, val| val.address(pcx)))
468                    .collect())
469            }
470            Dqe::Canonic(next) => {
471                let results = self.apply_dqe(next, on_args)?;
472                Ok(results
473                    .into_iter()
474                    .filter_map(|q| q.modify_value(|_, val| Some(val.canonic())))
475                    .collect())
476            }
477        }
478    }
479
480    /// Query variables and returns matched list.
481    pub fn query(&self, dqe: &Dqe) -> Result<Vec<QueryResult<'dbg>>, Error> {
482        self.apply_dqe(dqe, false)
483    }
484
485    /// Query only variable names.
486    /// Only filter expression supported.
487    ///
488    /// # Panics
489    ///
490    /// This method will panic if select expression
491    /// contains any operators excluding a variable selector.
492    pub fn query_names(&self, dqe: &Dqe) -> Result<Vec<String>, Error> {
493        match dqe {
494            Dqe::Variable(selector) => {
495                let vars = self.variable_die_by_selector(selector)?;
496                Ok(vars
497                    .into_iter()
498                    .filter_map(|die_ref| die_ref.deref_ensure().name())
499                    .collect())
500            }
501            _ => unreachable!("unexpected expression variant"),
502        }
503    }
504
505    /// Same as [`DqeExecutor::query`] but for function arguments.
506    pub fn query_arguments(&self, dqe: &Dqe) -> Result<Vec<QueryResult<'dbg>>, Error> {
507        self.apply_dqe(dqe, true)
508    }
509
510    /// Same as [`DqeExecutor::query_names`] but for function arguments.
511    pub fn query_arguments_names(&self, dqe: &Dqe) -> Result<Vec<String>, Error> {
512        match dqe {
513            Dqe::Variable(selector) => {
514                let params = self.param_die_by_selector(selector)?;
515                Ok(params
516                    .into_iter()
517                    .filter_map(|r| r.deref_ensure().name())
518                    .collect())
519            }
520            _ => unreachable!("unexpected expression variant"),
521        }
522    }
523}