1use gimli::{Dwarf, UnitOffset};
2use std::ops::Range;
3
4use crate::{GimliReader, MemoryInterface, stack_frame::StackFrameInfo};
5
6use super::{
7 ColumnType, DebugError, DebugInfo, SourceLocation, VariableLocation, debug_info, extract_file,
8 unit_info::{ExpressionResult, UnitInfo},
9};
10
11pub(crate) type Die = gimli::DebuggingInformationEntry<debug_info::GimliReader, usize>;
12
13#[derive(Clone)]
15pub(crate) struct FunctionDie<'data> {
16 pub(crate) unit_info: &'data UnitInfo,
18 pub(crate) function_die: Die,
20 pub(crate) specification_die: Option<(&'data UnitInfo, Die)>,
32 pub(crate) abstract_die: Option<(&'data UnitInfo, Die)>,
37 pub(crate) ranges: Vec<Range<u64>>,
39}
40
41impl<'a> FunctionDie<'a> {
42 pub(crate) fn function_ranges(
43 function_die: &Die,
44 unit_info: &UnitInfo,
45 dwarf: &Dwarf<GimliReader>,
46 ) -> Result<Option<Vec<Range<u64>>>, DebugError> {
47 let (gimli::DW_TAG_subprogram | gimli::DW_TAG_inlined_subroutine) = function_die.tag()
48 else {
49 return Ok(None);
51 };
52
53 let mut gimli_ranges = dwarf.die_ranges(&unit_info.unit, function_die)?;
55 let mut die_ranges = Vec::new();
56 while let Ok(Some(gimli_range)) = gimli_ranges.next() {
57 if gimli_range.begin == 0 {
58 return Ok(None);
61 }
62 die_ranges.push(gimli_range.begin..gimli_range.end);
63 }
64
65 Ok(Some(die_ranges))
66 }
67
68 pub(crate) fn new(
73 function_die: Die,
74 unit_info: &'a UnitInfo,
75 debug_info: &'a DebugInfo,
76 address: u64,
77 ) -> Result<Option<Self>, DebugError> {
78 let is_inlined_function = match function_die.tag() {
79 gimli::DW_TAG_subprogram => false,
80 gimli::DW_TAG_inlined_subroutine => true,
81 _ => {
82 return Ok(None);
84 }
85 };
86
87 let Some(die_ranges) = Self::function_ranges(&function_die, unit_info, &debug_info.dwarf)?
88 else {
89 return Ok(None);
90 };
91 if !die_ranges.iter().any(|range| range.contains(&address)) {
92 return Ok(None);
93 }
94
95 let specification_die;
96
97 let abstract_die = if is_inlined_function {
99 let Some((abstract_unit, abstract_die)) = debug_info
100 .resolve_die_reference_with_unit_info(
101 gimli::DW_AT_abstract_origin,
102 &function_die,
103 unit_info,
104 )
105 else {
106 tracing::debug!("No abstract origin found for inlined function");
107 return Ok(None);
108 };
109 specification_die = debug_info.resolve_die_reference_with_unit_info(
115 gimli::DW_AT_specification,
116 &abstract_die,
117 abstract_unit,
118 );
119 Some((abstract_unit, abstract_die))
120 } else {
121 specification_die = debug_info.resolve_die_reference_with_unit_info(
122 gimli::DW_AT_specification,
123 &function_die,
124 unit_info,
125 );
126 None
127 };
128
129 Ok(Some(Self {
130 unit_info,
131 function_die,
132 specification_die,
133 abstract_die,
134 ranges: die_ranges,
135 }))
136 }
137
138 pub(crate) fn range_contains(&self, address: u64) -> bool {
142 self.ranges.iter().any(|range| range.contains(&address))
143 }
144
145 pub(crate) fn low_pc(&self) -> Option<u64> {
148 self.ranges.first().map(|range| range.start)
149 }
150
151 pub(crate) fn high_pc(&self) -> Option<u64> {
154 self.ranges.last().map(|range| range.end)
155 }
156
157 pub(crate) fn is_inline(&self) -> bool {
159 self.abstract_die.is_some()
160 }
161
162 pub(crate) fn function_name(&self, debug_info: &super::DebugInfo) -> Option<String> {
164 let Some(fn_name_attr) = self.attribute(debug_info, gimli::DW_AT_name) else {
165 tracing::debug!("DW_AT_name attribute not found, unable to retrieve function name");
166 return None;
167 };
168 let value = fn_name_attr.value();
169 let gimli::AttributeValue::DebugStrRef(fn_name_ref) = value else {
170 tracing::debug!("Unexpected attribute value for DW_AT_name: {:?}", value);
171 return None;
172 };
173 match debug_info.dwarf.string(fn_name_ref) {
174 Ok(fn_name_raw) => {
175 let function_name = String::from_utf8_lossy(&fn_name_raw);
176
177 let language = crate::language::from_dwarf(self.unit_info.get_language());
178 Some(language.format_function_name(function_name.as_ref(), self, debug_info))
179 }
180 Err(error) => {
181 tracing::debug!("No value for DW_AT_name: {:?}: error", error);
182
183 None
184 }
185 }
186 }
187
188 pub(crate) fn inline_call_location(
193 &self,
194 debug_info: &super::DebugInfo,
195 ) -> Option<SourceLocation> {
196 if !self.is_inline() {
197 return None;
198 }
199
200 let file_name_attr = self.attribute(debug_info, gimli::DW_AT_call_file)?;
201
202 let path = extract_file(debug_info, &self.unit_info.unit, file_name_attr.value())?;
203 let line = self
204 .attribute(debug_info, gimli::DW_AT_call_line)
205 .and_then(|line| line.udata_value());
206
207 let column =
208 self.attribute(debug_info, gimli::DW_AT_call_column)
209 .map(|column| match column.udata_value() {
210 None => ColumnType::LeftEdge,
211 Some(c) => ColumnType::Column(c),
212 });
213
214 let address = self.low_pc();
215
216 Some(SourceLocation {
217 line,
218 column,
219 path,
220 address,
221 })
222 }
223
224 pub(crate) fn attribute(
226 &self,
227 debug_info: &super::DebugInfo,
228 attribute_name: gimli::DwAt,
229 ) -> Option<debug_info::GimliAttribute> {
230 let attribute = collapsed_attribute(
231 &self.function_die,
232 self.specification_die.as_ref().map(|(_, die)| die),
233 attribute_name,
234 );
235
236 if attribute.is_some() {
237 return attribute.cloned();
238 }
239
240 if let Some((abstract_unit, abstract_die)) = &self.abstract_die {
243 let inlined_specification_die = debug_info.resolve_die_reference(
244 gimli::DW_AT_specification,
245 abstract_die,
246 abstract_unit,
247 );
248 let inline_attribute = collapsed_attribute(
249 abstract_die,
250 inlined_specification_die.as_ref(),
251 attribute_name,
252 );
253
254 if inline_attribute.is_some() {
255 return inline_attribute.cloned();
256 }
257 }
258
259 None
260 }
261
262 pub fn frame_base(
264 &self,
265 debug_info: &super::DebugInfo,
266 memory: &mut dyn MemoryInterface,
267 frame_info: StackFrameInfo,
268 ) -> Result<Option<u64>, DebugError> {
269 match self.unit_info.extract_location(
270 debug_info,
271 &self.function_die,
272 &VariableLocation::Unknown,
273 memory,
274 frame_info,
275 )? {
276 ExpressionResult::Location(VariableLocation::Address(address)) => Ok(Some(address)),
277 ExpressionResult::Location(VariableLocation::RegisterValue(value)) => {
278 Ok(value.try_into().ok())
279 }
280 _ => Ok(None),
281 }
282 }
283
284 pub(crate) fn parent_offset(&self) -> Option<(&'a UnitInfo, UnitOffset)> {
290 let (unit, offset) = match &self.specification_die {
291 Some((unit, die)) => (*unit, die.offset()),
292 None => (self.unit_info, self.function_die.offset()),
293 };
294 unit.parent_offset(offset).map(|parent| (unit, parent))
295 }
296}
297
298fn collapsed_attribute<'a>(
300 function_die: &'a Die,
301 specification_die: Option<&'a Die>,
302 attribute_name: gimli::DwAt,
303) -> Option<&'a debug_info::GimliAttribute> {
304 specification_die
305 .as_ref()
306 .and_then(|specification_die| specification_die.attr(attribute_name))
307 .or_else(|| function_die.attr(attribute_name))
308}