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