Skip to main content

ddbug_parser/
function.rs

1use std::borrow::Cow;
2use std::cmp;
3use std::sync::Arc;
4
5use fnv::FnvHashMap as HashMap;
6
7use crate::file::FileHash;
8use crate::location::{self, FrameLocation, Piece, Register};
9use crate::namespace::Namespace;
10use crate::range::Range;
11use crate::source::Source;
12use crate::types::{MemberOffset, ParameterType, Type, TypeOffset};
13use crate::variable::{LocalVariable, Variable, VariableOffset};
14use crate::{Address, Id, Size};
15
16/// The debuginfo offset of a function.
17///
18/// This is unique for all functions in a file.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct FunctionOffset(usize);
21
22impl FunctionOffset {
23    #[inline]
24    pub(crate) fn new(offset: usize) -> FunctionOffset {
25        debug_assert!(FunctionOffset(offset) != FunctionOffset::none());
26        FunctionOffset(offset)
27    }
28
29    #[inline]
30    pub(crate) fn none() -> FunctionOffset {
31        FunctionOffset(usize::MAX)
32    }
33
34    #[inline]
35    pub(crate) fn is_none(self) -> bool {
36        self == Self::none()
37    }
38
39    #[inline]
40    pub(crate) fn is_some(self) -> bool {
41        self != Self::none()
42    }
43
44    #[inline]
45    pub(crate) fn get(self) -> Option<usize> {
46        if self.is_none() { None } else { Some(self.0) }
47    }
48}
49
50impl Default for FunctionOffset {
51    #[inline]
52    fn default() -> Self {
53        FunctionOffset::none()
54    }
55}
56
57/// A function.
58#[derive(Debug, Default)]
59pub struct Function<'input> {
60    pub(crate) id: Id,
61    pub(crate) offset: FunctionOffset,
62    pub(crate) namespace: Option<Arc<Namespace<'input>>>,
63    pub(crate) name: Option<&'input str>,
64    pub(crate) linkage_name: Option<&'input str>,
65    pub(crate) symbol_name: Option<&'input str>,
66    pub(crate) source: Source<'input>,
67    pub(crate) address: Address,
68    pub(crate) size: Size,
69    pub(crate) ranges: Vec<Range>,
70    pub(crate) inline: bool,
71    pub(crate) declaration: bool,
72    pub(crate) parameters: Vec<ParameterType<'input>>,
73    pub(crate) return_type: TypeOffset,
74}
75
76/// Extra function details.
77///
78/// These are kept separate from `Function` so that they can be loaded only when needed.
79#[derive(Debug, Default)]
80pub struct FunctionDetails<'input> {
81    pub(crate) parameters: Vec<Parameter<'input>>,
82    pub(crate) variables: Vec<LocalVariable<'input>>,
83    pub(crate) inlined_functions: Vec<InlinedFunction<'input>>,
84    pub(crate) calls: Vec<FunctionCall<'input>>,
85}
86
87impl<'input> Function<'input> {
88    pub(crate) fn from_offset<'a>(
89        hash: &'a FileHash<'input>,
90        offset: FunctionOffset,
91    ) -> Option<&'input Function<'input>> {
92        if offset.is_none() {
93            return None;
94        }
95        hash.functions_by_offset.get(&offset).cloned()
96    }
97
98    /// The user defined id for this function.
99    #[inline]
100    pub fn id(&self) -> usize {
101        self.id.get()
102    }
103
104    /// Set a user defined id for this function.
105    #[inline]
106    pub fn set_id(&self, id: usize) {
107        self.id.set(id)
108    }
109
110    /// The namespace of the function.
111    pub fn namespace(&self) -> Option<&Namespace<'input>> {
112        self.namespace.as_deref()
113    }
114
115    /// The name of the function.
116    #[inline]
117    pub fn name(&self) -> Option<&'input str> {
118        self.name
119    }
120
121    /// The linkage name of the variable.
122    #[inline]
123    pub fn linkage_name(&self) -> Option<&'input str> {
124        self.linkage_name
125    }
126
127    /// The symbol name of the function.
128    ///
129    /// This is determined from a symbol table entry with a matching address.
130    #[inline]
131    pub fn symbol_name(&self) -> Option<&'input str> {
132        self.symbol_name
133    }
134
135    /// The source information for the function.
136    #[inline]
137    pub fn source(&self) -> &Source<'input> {
138        &self.source
139    }
140
141    /// The address of the function.
142    #[inline]
143    pub fn address(&self) -> Option<u64> {
144        self.address.get()
145    }
146
147    /// The size in bytes of the function.
148    ///
149    /// This may exclude padding, and may be non-contiguous.
150    #[inline]
151    pub fn size(&self) -> Option<u64> {
152        self.size.get()
153    }
154
155    /// The address ranges of the function.
156    pub fn ranges(&self) -> &[Range] {
157        &self.ranges
158    }
159
160    /// Return true if this is an inlined function.
161    #[inline]
162    pub fn is_inline(&self) -> bool {
163        self.inline
164    }
165
166    /// Return true if this is a declaration.
167    #[inline]
168    pub fn is_declaration(&self) -> bool {
169        self.declaration
170    }
171
172    /// The function parameter types.
173    #[inline]
174    pub fn parameters(&self) -> &[ParameterType<'input>] {
175        &self.parameters
176    }
177
178    /// The return type.
179    ///
180    /// Returns `None` if the return type is invalid.
181    #[inline]
182    pub fn return_type<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
183        Type::from_offset(hash, self.return_type)
184    }
185
186    /// Extra function details.
187    pub fn details(&self, hash: &FileHash<'input>) -> FunctionDetails<'input> {
188        hash.file.get_function_details(self.offset, hash)
189    }
190
191    /// Compare the identifying information of two functions.
192    ///
193    /// Functions are equal if they have the same namespace and name.
194    ///
195    /// This can be used to sort, and to determine if two functions refer to the same definition
196    /// (even if there are differences in the definitions).
197    pub fn cmp_id(
198        _hash_a: &FileHash,
199        a: &Function,
200        _hash_b: &FileHash,
201        b: &Function,
202    ) -> cmp::Ordering {
203        Namespace::cmp_ns_and_name(a.namespace(), a.name(), b.namespace(), b.name())
204    }
205}
206
207impl<'input> FunctionDetails<'input> {
208    /// The function parameters.
209    #[inline]
210    pub fn parameters(&self) -> &[Parameter<'input>] {
211        &self.parameters
212    }
213
214    /// The local variables.
215    #[inline]
216    pub fn variables(&self) -> &[LocalVariable<'input>] {
217        &self.variables
218    }
219
220    /// The inlined functions.
221    #[inline]
222    pub fn inlined_functions(&self) -> &[InlinedFunction<'input>] {
223        &self.inlined_functions
224    }
225
226    /// The calls to non-inlined functions.
227    #[inline]
228    pub fn calls(&self) -> &[FunctionCall<'input>] {
229        &self.calls
230    }
231}
232
233/// The debuginfo offset of a parameter formal specification/definition.
234///
235/// This is unique for all parameter specifications/definitions in a file.
236#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
237pub struct ParameterOffset(usize);
238
239impl ParameterOffset {
240    #[inline]
241    pub(crate) fn new(offset: usize) -> ParameterOffset {
242        debug_assert!(ParameterOffset(offset) != ParameterOffset::none());
243        ParameterOffset(offset)
244    }
245
246    #[inline]
247    pub(crate) fn none() -> ParameterOffset {
248        ParameterOffset(usize::MAX)
249    }
250}
251
252impl Default for ParameterOffset {
253    #[inline]
254    fn default() -> Self {
255        ParameterOffset::none()
256    }
257}
258
259/// A function parameter definition.
260#[derive(Debug, Default, Clone)]
261pub struct Parameter<'input> {
262    pub(crate) offset: ParameterOffset,
263    pub(crate) name: Option<&'input str>,
264    pub(crate) ty: TypeOffset,
265    // TODO: move this to ParameterDetails
266    pub(crate) locations: Vec<(Range, Piece)>,
267}
268
269impl<'input> Parameter<'input> {
270    /// The name of the parameter.
271    #[inline]
272    pub fn name(&self) -> Option<&'input str> {
273        self.name
274    }
275
276    /// The debuginfo offset of this parameter.
277    #[inline]
278    pub fn offset(&self) -> ParameterOffset {
279        self.offset
280    }
281
282    /// The type offset of the parameter.
283    ///
284    /// A type offset is unique for all types in a file.
285    #[inline]
286    pub fn type_offset(&self) -> TypeOffset {
287        self.ty
288    }
289
290    /// The type of the parameter.
291    #[inline]
292    pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
293        Type::from_offset(hash, self.ty)
294    }
295
296    /// The size in bytes of the parameter.
297    pub fn byte_size(&self, hash: &FileHash) -> Option<u64> {
298        self.ty(hash).and_then(|v| v.byte_size(hash))
299    }
300
301    /// A list of all locations where this parameter is stored.
302    pub fn locations(&self) -> &[(Range, Piece)] {
303        &self.locations
304    }
305
306    /// The registers in which this parameter is stored.
307    pub fn registers(&self) -> impl Iterator<Item = (Range, Register)> + '_ {
308        location::registers(&self.locations)
309    }
310
311    /// The registers pointing to where this variable is stored.
312    pub fn register_offsets(&self) -> impl Iterator<Item = (Range, Register, i64)> + '_ {
313        location::register_offsets(&self.locations)
314    }
315
316    /// The stack frame locations at which this parameter is stored.
317    pub fn frame_locations(&self) -> impl Iterator<Item = (Range, FrameLocation)> + '_ {
318        location::frame_locations(&self.locations)
319    }
320
321    /// Compare the identifying information of two parameters.
322    ///
323    /// Parameters are considered equal if their name and type are equal.
324    ///
325    /// This can be used to sort, and to determine if two types refer to the same definition
326    /// (even if there are differences in the definitions).
327    #[allow(dead_code)]
328    fn cmp_id(hash_a: &FileHash, a: &Parameter, hash_b: &FileHash, b: &Parameter) -> cmp::Ordering {
329        let ord = Self::cmp_type(hash_a, a, hash_b, b);
330        if ord != cmp::Ordering::Equal {
331            return ord;
332        }
333        a.name.cmp(&b.name)
334    }
335
336    /// Compare the types of two parameters.
337    pub fn cmp_type(
338        hash_a: &FileHash,
339        a: &Parameter,
340        hash_b: &FileHash,
341        b: &Parameter,
342    ) -> cmp::Ordering {
343        match (&a.ty(hash_a), &b.ty(hash_b)) {
344            (Some(ty_a), Some(ty_b)) => Type::cmp_id(hash_a, ty_a, hash_b, ty_b),
345            (Some(_), None) => cmp::Ordering::Less,
346            (None, Some(_)) => cmp::Ordering::Greater,
347            (None, None) => cmp::Ordering::Equal,
348        }
349    }
350}
351
352/// An inlined instance of a function.
353#[derive(Debug, Default)]
354pub struct InlinedFunction<'input> {
355    pub(crate) abstract_origin: FunctionOffset,
356    pub(crate) size: Size,
357    pub(crate) ranges: Vec<Range>,
358    pub(crate) parameters: Vec<Parameter<'input>>,
359    pub(crate) variables: Vec<LocalVariable<'input>>,
360    pub(crate) inlined_functions: Vec<InlinedFunction<'input>>,
361    pub(crate) calls: Vec<FunctionCall<'input>>,
362    pub(crate) call_source: Source<'input>,
363}
364
365impl<'input> InlinedFunction<'input> {
366    /// The function that this is an inlined instance of.
367    #[inline]
368    pub fn abstract_origin<'a>(&self, hash: &'a FileHash<'input>) -> Option<&'a Function<'input>> {
369        Function::from_offset(hash, self.abstract_origin)
370    }
371
372    /// The address ranges of the inlined function instance.
373    #[inline]
374    pub fn ranges(&self) -> &[Range] {
375        &self.ranges
376    }
377
378    /// The size of the inlined function instance.
379    #[inline]
380    pub fn size(&self) -> Option<u64> {
381        self.size.get()
382    }
383
384    /// The source information for call location.
385    #[inline]
386    pub fn call_source(&self) -> &Source<'input> {
387        &self.call_source
388    }
389
390    /// The function parameters.
391    #[inline]
392    pub fn parameters(&self) -> &[Parameter<'input>] {
393        &self.parameters
394    }
395
396    /// The local variables.
397    #[inline]
398    pub fn variables(&self) -> &[LocalVariable<'input>] {
399        &self.variables
400    }
401
402    /// The inlined functions within this inlined functions.
403    #[inline]
404    pub fn inlined_functions(&self) -> &[InlinedFunction<'input>] {
405        &self.inlined_functions
406    }
407
408    /// The calls to non-inlined functions.
409    #[inline]
410    pub fn calls(&self) -> &[FunctionCall<'input>] {
411        &self.calls
412    }
413}
414
415/// A call to another (non-inlined) function.
416#[derive(Debug, Default)]
417pub struct FunctionCall<'input> {
418    pub(crate) kind: FunctionCallKind,
419    pub(crate) return_address: Option<u64>,
420    pub(crate) call_address: Option<u64>,
421    pub(crate) origin: Option<FunctionCallOrigin<'input>>,
422    pub(crate) target: Vec<Piece>,
423    pub(crate) target_is_clobbered: bool,
424    pub(crate) ty: Option<TypeOffset>,
425    pub(crate) called_from_source: Source<'input>,
426    pub(crate) parameters: Vec<FunctionCallParameter<'input>>,
427}
428
429impl<'input> FunctionCall<'input> {
430    /// The kind of function call (normal or tail call).
431    #[inline]
432    pub fn kind(&self) -> FunctionCallKind {
433        self.kind
434    }
435
436    /// The return address after the call.
437    #[inline]
438    pub fn return_address(&self) -> Option<u64> {
439        self.return_address
440    }
441
442    /// The address of the call instruction.
443    ///
444    /// This the call-like instruction for a normal call or the jump-like instruction
445    /// for a tail call.
446    #[inline]
447    pub fn call_address(&self) -> Option<u64> {
448        self.call_address
449    }
450
451    /// The origin of the target of the call.
452    ///
453    /// This determines whether the call is direct or indirect,
454    /// and if it is indirect, where the function pointer comes from.
455    #[inline]
456    pub fn origin(&self) -> Option<&FunctionCallOrigin<'input>> {
457        self.origin.as_ref()
458    }
459
460    /// The computed value of the target of the call.
461    ///
462    /// This is only used if the target is indirect.
463    #[inline]
464    pub fn target(&self) -> &[Piece] {
465        &self.target
466    }
467
468    /// Whether the expression for the value of `target` is only valid before the call.
469    #[inline]
470    pub fn target_is_clobbered(&self) -> bool {
471        self.target_is_clobbered
472    }
473
474    /// The type of the target of the call.
475    ///
476    /// This is usually omitted if `origin` is specified.
477    ///
478    /// Returns `None` if the type is invalid or unknown.
479    // TODO: add a convenient way to get the type from either `origin` or `ty`.
480    #[inline]
481    pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
482        if let Some(ty) = self.ty {
483            Type::from_offset(hash, ty)
484        } else {
485            None
486        }
487    }
488
489    /// The source location of the call.
490    #[inline]
491    pub fn source(&self) -> &Source<'input> {
492        &self.called_from_source
493    }
494
495    /// The function parameters for this call.
496    #[inline]
497    pub fn parameters(&self) -> &[FunctionCallParameter<'input>] {
498        &self.parameters
499    }
500}
501
502/// The kind of function call being made.
503#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
504pub enum FunctionCallKind {
505    /// This is a normal function call made via a call-like instruction.
506    #[default]
507    Normal,
508    /// This is a tail call made via a jump-like instruction.
509    Tail,
510}
511
512/// The origin of the target of a function call.
513#[derive(Debug)]
514pub enum FunctionCallOrigin<'input> {
515    /// The definition of the function for a direct call.
516    Direct(&'input Function<'input>),
517    /// The origin of the function pointer for an indirect call.
518    Indirect(FunctionCallIndirectOrigin<'input>),
519}
520
521/// The origin of the function pointer for an indirect function call.
522#[derive(Debug)]
523pub enum FunctionCallIndirectOrigin<'input> {
524    /// The function pointer is stored in this variable at the time of the call.
525    Variable(&'input Variable<'input>),
526    /// The function pointer is stored in one of the caller function's local variables at the time of the call.
527    LocalVariable(VariableOffset),
528    /// The function origin is stored in one of the caller function's parameters at the time of the call.
529    Parameter(ParameterOffset),
530    /// The function origin is stored in this member at the time of the call.
531    ///
532    /// We store this as the unique member offset. This allows for later traversal down the types
533    /// in order to locate this exact member if the caller is interested.
534    Member(MemberOffset),
535}
536
537/// Represents one of the parameters for a function call.
538#[derive(Debug, Default)]
539pub struct FunctionCallParameter<'input> {
540    pub(crate) name: Option<&'input str>,
541    pub(crate) ty: Option<TypeOffset>,
542    pub(crate) location: Vec<Piece>,
543    pub(crate) value: Vec<Piece>,
544    pub(crate) data_location: Vec<Piece>,
545    pub(crate) data_value: Vec<Piece>,
546}
547
548impl<'input> FunctionCallParameter<'input> {
549    /// The name of the parameter.
550    #[inline]
551    pub fn name(&self) -> Option<&'input str> {
552        self.name
553    }
554
555    /// The type of the parameter.
556    ///
557    /// Returns `None` if the type is invalid or unknown.
558    #[inline]
559    pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
560        if let Some(ty) = self.ty {
561            Type::from_offset(hash, ty)
562        } else {
563            None
564        }
565    }
566
567    /// The location where the parameter is passed for the call.
568    pub fn location(&self) -> &[Piece] {
569        &self.location
570    }
571
572    /// The value of the parameter at the time of the call.
573    pub fn value(&self) -> &[Piece] {
574        &self.value
575    }
576
577    /// If the parameter is a reference, the location where the referenced data is stored.
578    pub fn data_location(&self) -> &[Piece] {
579        &self.data_location
580    }
581
582    /// If the parameter is a reference, the value of the referenced data at the time of the call.
583    pub fn data_value(&self) -> &[Piece] {
584        &self.data_value
585    }
586}
587
588/// Abstracts over functions vs inlined functions at the details level
589#[derive(Debug, Clone, Copy)]
590pub enum FunctionInstance<'input> {
591    /// This is a non-inlined function
592    Normal(&'input FunctionDetails<'input>),
593    /// This is an inlined function
594    Inlined(&'input InlinedFunction<'input>),
595}
596
597impl<'input> FunctionInstance<'input> {
598    #[inline]
599    fn parameters(&self) -> &'input [Parameter<'input>] {
600        match self {
601            Self::Normal(f) => f.parameters(),
602            Self::Inlined(f) => f.parameters(),
603        }
604    }
605
606    #[inline]
607    fn variables(&self) -> &'input [LocalVariable<'input>] {
608        match self {
609            Self::Normal(f) => f.variables(),
610            Self::Inlined(f) => f.variables(),
611        }
612    }
613}
614
615/// An index of parameters and local variables within a function.
616///
617/// This requires the function details to be loaded.
618pub struct FunctionHash<'input> {
619    /// The function being indexed.
620    pub function: FunctionInstance<'input>,
621    /// All parameters by offset.
622    pub parameters_by_offset: HashMap<ParameterOffset, &'input Parameter<'input>>,
623    /// All local variables by offset.
624    pub local_variables_by_offset: HashMap<VariableOffset, &'input LocalVariable<'input>>,
625}
626
627impl<'input> FunctionHash<'input> {
628    /// Create a new `FunctionHash` for the given function.
629    pub fn new(function: FunctionInstance<'input>) -> Self {
630        Self {
631            function,
632            parameters_by_offset: Self::parameters_by_offset(&function),
633            local_variables_by_offset: Self::local_variables_by_offset(&function),
634        }
635    }
636
637    fn parameters_by_offset(
638        function: &FunctionInstance<'input>,
639    ) -> HashMap<ParameterOffset, &'input Parameter<'input>> {
640        let mut parameters = HashMap::default();
641        for parameter in function.parameters() {
642            parameters.insert(parameter.offset, parameter);
643        }
644        parameters
645    }
646
647    fn local_variables_by_offset(
648        function: &FunctionInstance<'input>,
649    ) -> HashMap<VariableOffset, &'input LocalVariable<'input>> {
650        let mut local_variables = HashMap::default();
651        for local_variable in function.variables() {
652            local_variables.insert(local_variable.offset, local_variable);
653        }
654        local_variables
655    }
656}