1 use super::address_transform::AddressTransform;
2 use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo};
3 use super::range_info_builder::RangeInfoBuilder;
4 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs};
5 use super::{DebugInputContext, Reader, TransformError};
6 use anyhow::{bail, Error};
7 use cranelift_codegen::isa::TargetIsa;
8 use gimli::{
9     write, AttributeValue, DebugLineOffset, DebugLineStr, DebugStr, DebugStrOffsets,
10     DebuggingInformationEntry, Unit,
11 };
12 
13 #[derive(Debug)]
14 pub(crate) enum FileAttributeContext<'a> {
15     Root(Option<DebugLineOffset>),
16     Children {
17         file_map: &'a [write::FileId],
18         file_index_base: u64,
19         frame_base: Option<&'a CompiledExpression>,
20     },
21 }
22 
23 fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool {
24     match attr_name {
25         gimli::DW_AT_location
26         | gimli::DW_AT_string_length
27         | gimli::DW_AT_return_addr
28         | gimli::DW_AT_data_member_location
29         | gimli::DW_AT_frame_base
30         | gimli::DW_AT_segment
31         | gimli::DW_AT_static_link
32         | gimli::DW_AT_use_location
33         | gimli::DW_AT_vtable_elem_location => true,
34         _ => false,
35     }
36 }
37 
38 pub(crate) fn clone_die_attributes<'a, R>(
39     dwarf: &gimli::Dwarf<R>,
40     unit: &Unit<R, R::Offset>,
41     entry: &DebuggingInformationEntry<R>,
42     context: &DebugInputContext<R>,
43     addr_tr: &'a AddressTransform,
44     frame_info: Option<&FunctionFrameInfo>,
45     out_unit: &mut write::Unit,
46     current_scope_id: write::UnitEntryId,
47     subprogram_range_builder: Option<RangeInfoBuilder>,
48     scope_ranges: Option<&Vec<(u64, u64)>>,
49     cu_low_pc: u64,
50     out_strings: &mut write::StringTable,
51     pending_die_refs: &mut PendingUnitRefs,
52     pending_di_refs: &mut PendingDebugInfoRefs,
53     file_context: FileAttributeContext<'a>,
54     isa: &dyn TargetIsa,
55 ) -> Result<(), Error>
56 where
57     R: Reader,
58 {
59     let unit_encoding = unit.encoding();
60 
61     let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder {
62         subprogram_range_builder
63     } else {
64         // FIXME for CU: currently address_transform operate on a single
65         // function range, and when CU spans multiple ranges the
66         // transformation may be incomplete.
67         RangeInfoBuilder::from(dwarf, unit, entry, context, cu_low_pc)?
68     };
69     range_info.build(addr_tr, out_unit, current_scope_id);
70 
71     let mut attrs = entry.attrs();
72     while let Some(attr) = attrs.next()? {
73         let attr_value = match attr.value() {
74             AttributeValue::Addr(_) | AttributeValue::DebugAddrIndex(_)
75                 if attr.name() == gimli::DW_AT_low_pc =>
76             {
77                 continue;
78             }
79             AttributeValue::Udata(_) if attr.name() == gimli::DW_AT_high_pc => {
80                 continue;
81             }
82             AttributeValue::RangeListsRef(_) if attr.name() == gimli::DW_AT_ranges => {
83                 continue;
84             }
85             AttributeValue::DebugAddrBase(_) | AttributeValue::DebugStrOffsetsBase(_) => {
86                 continue;
87             }
88 
89             AttributeValue::Addr(u) => {
90                 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
91                 write::AttributeValue::Address(addr)
92             }
93             AttributeValue::DebugAddrIndex(i) => {
94                 let u = context.debug_addr.get_address(4, unit.addr_base, i)?;
95                 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
96                 write::AttributeValue::Address(addr)
97             }
98             AttributeValue::Block(d) => write::AttributeValue::Block(d.to_slice()?.into_owned()),
99             AttributeValue::Udata(u) => write::AttributeValue::Udata(u),
100             AttributeValue::Data1(d) => write::AttributeValue::Data1(d),
101             AttributeValue::Data2(d) => write::AttributeValue::Data2(d),
102             AttributeValue::Data4(d) => write::AttributeValue::Data4(d),
103             AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d),
104             AttributeValue::Flag(f) => write::AttributeValue::Flag(f),
105             AttributeValue::DebugLineRef(line_program_offset) => {
106                 if let FileAttributeContext::Root(o) = file_context {
107                     if o != Some(line_program_offset) {
108                         return Err(TransformError("invalid debug_line offset").into());
109                     }
110                     write::AttributeValue::LineProgramRef
111                 } else {
112                     return Err(TransformError("unexpected debug_line index attribute").into());
113                 }
114             }
115             AttributeValue::FileIndex(i) => {
116                 if let FileAttributeContext::Children {
117                     file_map,
118                     file_index_base,
119                     ..
120                 } = file_context
121                 {
122                     write::AttributeValue::FileIndex(Some(file_map[(i - file_index_base) as usize]))
123                 } else {
124                     return Err(TransformError("unexpected file index attribute").into());
125                 }
126             }
127             AttributeValue::DebugStrRef(str_offset) => {
128                 let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
129                 write::AttributeValue::StringRef(out_strings.add(s))
130             }
131             AttributeValue::DebugStrOffsetsIndex(i) => {
132                 let str_offset = context.debug_str_offsets.get_str_offset(
133                     gimli::Format::Dwarf32,
134                     unit.str_offsets_base,
135                     i,
136                 )?;
137                 let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
138                 write::AttributeValue::StringRef(out_strings.add(s))
139             }
140             AttributeValue::RangeListsRef(r) => {
141                 let r = dwarf.ranges_offset_from_raw(unit, r);
142                 let range_info = RangeInfoBuilder::from_ranges_ref(unit, r, context, cu_low_pc)?;
143                 let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges);
144                 write::AttributeValue::RangeListRef(range_list_id)
145             }
146             AttributeValue::LocationListsRef(r) => {
147                 let low_pc = 0;
148                 let mut locs = context.loclists.locations(
149                     r,
150                     unit_encoding,
151                     low_pc,
152                     &context.debug_addr,
153                     unit.addr_base,
154                 )?;
155                 let frame_base =
156                     if let FileAttributeContext::Children { frame_base, .. } = file_context {
157                         frame_base
158                     } else {
159                         None
160                     };
161 
162                 let mut result: Option<Vec<_>> = None;
163                 while let Some(loc) = locs.next()? {
164                     if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? {
165                         let chunk = expr
166                             .build_with_locals(
167                                 &[(loc.range.begin, loc.range.end)],
168                                 addr_tr,
169                                 frame_info,
170                                 isa,
171                             )
172                             .filter(|i| {
173                                 // Ignore empty range
174                                 if let Ok((_, 0, _)) = i {
175                                     false
176                                 } else {
177                                     true
178                                 }
179                             })
180                             .map(|i| {
181                                 i.map(|(start, len, expr)| write::Location::StartLength {
182                                     begin: start,
183                                     length: len,
184                                     data: expr,
185                                 })
186                             })
187                             .collect::<Result<Vec<_>, _>>()?;
188                         match &mut result {
189                             Some(r) => r.extend(chunk),
190                             x @ None => *x = Some(chunk),
191                         }
192                     } else {
193                         // FIXME _expr contains invalid expression
194                         continue; // ignore entry
195                     }
196                 }
197                 if result.is_none() {
198                     continue; // no valid locations
199                 }
200                 let list_id = out_unit.locations.add(write::LocationList(result.unwrap()));
201                 write::AttributeValue::LocationListRef(list_id)
202             }
203             AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
204                 // We do not really "rewrite" the frame base so much as replace it outright.
205                 // References to it through the DW_OP_fbreg opcode will be expanded below.
206                 let mut cfa = write::Expression::new();
207                 cfa.op(gimli::DW_OP_call_frame_cfa);
208                 write::AttributeValue::Exprloc(cfa)
209             }
210             AttributeValue::Exprloc(ref expr) => {
211                 let frame_base =
212                     if let FileAttributeContext::Children { frame_base, .. } = file_context {
213                         frame_base
214                     } else {
215                         None
216                     };
217                 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? {
218                     if expr.is_simple() {
219                         if let Some(expr) = expr.build() {
220                             write::AttributeValue::Exprloc(expr)
221                         } else {
222                             continue;
223                         }
224                     } else {
225                         // Conversion to loclist is required.
226                         if let Some(scope_ranges) = scope_ranges {
227                             let exprs = expr
228                                 .build_with_locals(scope_ranges, addr_tr, frame_info, isa)
229                                 .collect::<Result<Vec<_>, _>>()?;
230                             if exprs.is_empty() {
231                                 continue;
232                             }
233                             let found_single_expr = {
234                                 // Micro-optimization all expressions alike, use one exprloc.
235                                 let mut found_expr: Option<write::Expression> = None;
236                                 for (_, _, expr) in &exprs {
237                                     if let Some(ref prev_expr) = found_expr {
238                                         if expr == prev_expr {
239                                             continue; // the same expression
240                                         }
241                                         found_expr = None;
242                                         break;
243                                     }
244                                     found_expr = Some(expr.clone())
245                                 }
246                                 found_expr
247                             };
248                             if let Some(expr) = found_single_expr {
249                                 write::AttributeValue::Exprloc(expr)
250                             } else if is_exprloc_to_loclist_allowed(attr.name()) {
251                                 // Converting exprloc to loclist.
252                                 let mut locs = Vec::new();
253                                 for (begin, length, data) in exprs {
254                                     if length == 0 {
255                                         // Ignore empty range
256                                         continue;
257                                     }
258                                     locs.push(write::Location::StartLength {
259                                         begin,
260                                         length,
261                                         data,
262                                     });
263                                 }
264                                 let list_id = out_unit.locations.add(write::LocationList(locs));
265                                 write::AttributeValue::LocationListRef(list_id)
266                             } else {
267                                 continue;
268                             }
269                         } else {
270                             continue;
271                         }
272                     }
273                 } else {
274                     // FIXME _expr contains invalid expression
275                     continue; // ignore attribute
276                 }
277             }
278             AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e),
279             AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e),
280             AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e),
281             AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e),
282             AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e),
283             AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e),
284             AttributeValue::Language(e) => write::AttributeValue::Language(e),
285             AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e),
286             AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e),
287             AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e),
288             AttributeValue::Inline(e) => write::AttributeValue::Inline(e),
289             AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e),
290             AttributeValue::UnitRef(offset) => {
291                 pending_die_refs.insert(current_scope_id, attr.name(), offset);
292                 continue;
293             }
294             AttributeValue::DebugInfoRef(offset) => {
295                 pending_di_refs.insert(current_scope_id, attr.name(), offset);
296                 continue;
297             }
298             AttributeValue::String(d) => write::AttributeValue::String(d.to_slice()?.into_owned()),
299             a => bail!("Unexpected attribute: {:?}", a),
300         };
301         let current_scope = out_unit.get_mut(current_scope_id);
302         current_scope.set(attr.name(), attr_value);
303     }
304     Ok(())
305 }
306 
307 pub(crate) fn clone_attr_string<R>(
308     attr_value: &AttributeValue<R>,
309     form: gimli::DwForm,
310     unit: &Unit<R, R::Offset>,
311     debug_str: &DebugStr<R>,
312     debug_str_offsets: &DebugStrOffsets<R>,
313     debug_line_str: &DebugLineStr<R>,
314     out_strings: &mut write::StringTable,
315 ) -> Result<write::LineString, Error>
316 where
317     R: Reader,
318 {
319     let content = match attr_value {
320         AttributeValue::DebugStrRef(str_offset) => {
321             debug_str.get_str(*str_offset)?.to_slice()?.to_vec()
322         }
323         AttributeValue::DebugStrOffsetsIndex(i) => {
324             let str_offset = debug_str_offsets.get_str_offset(
325                 gimli::Format::Dwarf32,
326                 unit.str_offsets_base,
327                 *i,
328             )?;
329             debug_str.get_str(str_offset)?.to_slice()?.to_vec()
330         }
331         AttributeValue::DebugLineStrRef(str_offset) => {
332             debug_line_str.get_str(*str_offset)?.to_slice()?.to_vec()
333         }
334         AttributeValue::String(b) => b.to_slice()?.to_vec(),
335         v => bail!("Unexpected attribute value: {:?}", v),
336     };
337     Ok(match form {
338         gimli::DW_FORM_strp => {
339             let id = out_strings.add(content);
340             write::LineString::StringRef(id)
341         }
342         gimli::DW_FORM_string => write::LineString::String(content),
343         _ => bail!("DW_FORM_line_strp or other not supported"),
344     })
345 }
346