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::{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     addr_tr: &'a AddressTransform,
40     frame_info: Option<&FunctionFrameInfo>,
41     out_unit: &mut write::Unit,
42     current_scope_id: write::UnitEntryId,
43     subprogram_range_builder: Option<RangeInfoBuilder>,
44     scope_ranges: Option<&Vec<(u64, u64)>>,
45     out_strings: &mut write::StringTable,
46     pending_die_refs: &mut PendingUnitRefs,
47     pending_di_refs: &mut PendingDebugInfoRefs,
48     file_context: FileAttributeContext<'a>,
49     isa: &dyn TargetIsa,
50 ) -> Result<(), Error>
51 where
52     R: Reader,
53 {
54     let unit_encoding = unit.encoding();
55 
56     let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder {
57         subprogram_range_builder
58     } else {
59         // FIXME for CU: currently address_transform operate on a single
60         // function range, and when CU spans multiple ranges the
61         // transformation may be incomplete.
62         RangeInfoBuilder::from(dwarf, unit, entry)?
63     };
64     range_info.build(addr_tr, out_unit, current_scope_id);
65 
66     let mut attrs = entry.attrs();
67     while let Some(attr) = attrs.next()? {
68         match attr.name() {
69             gimli::DW_AT_low_pc | gimli::DW_AT_high_pc | gimli::DW_AT_ranges => {
70                 // Handled by RangeInfoBuilder.
71                 continue;
72             }
73             gimli::DW_AT_str_offsets_base
74             | gimli::DW_AT_addr_base
75             | gimli::DW_AT_rnglists_base
76             | gimli::DW_AT_loclists_base
77             | gimli::DW_AT_dwo_name
78             | gimli::DW_AT_GNU_addr_base
79             | gimli::DW_AT_GNU_ranges_base
80             | gimli::DW_AT_GNU_dwo_name
81             | gimli::DW_AT_GNU_dwo_id => {
82                 // DWARF encoding details that we don't need to copy.
83                 continue;
84             }
85             _ => {}
86         }
87         let attr_value = attr.value();
88         let out_attr_value = match attr_value {
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 = dwarf.address(unit, 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::Data8(d) => write::AttributeValue::Data8(d),
104             AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d),
105             AttributeValue::Flag(f) => write::AttributeValue::Flag(f),
106             AttributeValue::DebugLineRef(line_program_offset) => {
107                 if let FileAttributeContext::Root(o) = file_context {
108                     if o != Some(line_program_offset) {
109                         return Err(TransformError("invalid debug_line offset").into());
110                     }
111                     write::AttributeValue::LineProgramRef
112                 } else {
113                     return Err(TransformError("unexpected debug_line index attribute").into());
114                 }
115             }
116             AttributeValue::FileIndex(i) => {
117                 if let FileAttributeContext::Children {
118                     file_map,
119                     file_index_base,
120                     ..
121                 } = file_context
122                 {
123                     let index = usize::try_from(i - file_index_base)
124                         .ok()
125                         .and_then(|i| file_map.get(i).copied());
126                     match index {
127                         Some(index) => write::AttributeValue::FileIndex(Some(index)),
128                         // This was seen to be invalid in #8884 and #8904 so
129                         // ignore this seemingly invalid DWARF from LLVM
130                         None => continue,
131                     }
132                 } else {
133                     return Err(TransformError("unexpected file index attribute").into());
134                 }
135             }
136             AttributeValue::DebugStrRef(_) | AttributeValue::DebugStrOffsetsIndex(_) => {
137                 let s = dwarf
138                     .attr_string(unit, attr_value)?
139                     .to_string_lossy()?
140                     .into_owned();
141                 write::AttributeValue::StringRef(out_strings.add(s))
142             }
143             AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => {
144                 let r = dwarf.attr_ranges_offset(unit, attr_value)?.unwrap();
145                 let range_info = RangeInfoBuilder::from_ranges_ref(dwarf, unit, r)?;
146                 let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges);
147                 write::AttributeValue::RangeListRef(range_list_id)
148             }
149             AttributeValue::LocationListsRef(_) | AttributeValue::DebugLocListsIndex(_) => {
150                 let r = dwarf.attr_locations_offset(unit, attr_value)?.unwrap();
151                 let low_pc = 0;
152                 let mut locs = dwarf.locations.locations(
153                     r,
154                     unit_encoding,
155                     low_pc,
156                     &dwarf.debug_addr,
157                     unit.addr_base,
158                 )?;
159                 let frame_base =
160                     if let FileAttributeContext::Children { frame_base, .. } = file_context {
161                         frame_base
162                     } else {
163                         None
164                     };
165 
166                 let mut result: Option<Vec<_>> = None;
167                 while let Some(loc) = locs.next()? {
168                     if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? {
169                         let chunk = expr
170                             .build_with_locals(
171                                 &[(loc.range.begin, loc.range.end)],
172                                 addr_tr,
173                                 frame_info,
174                                 isa,
175                             )
176                             .filter(|i| {
177                                 // Ignore empty range
178                                 if let Ok((_, 0, _)) = i {
179                                     false
180                                 } else {
181                                     true
182                                 }
183                             })
184                             .map(|i| {
185                                 i.map(|(start, len, expr)| write::Location::StartLength {
186                                     begin: start,
187                                     length: len,
188                                     data: expr,
189                                 })
190                             })
191                             .collect::<Result<Vec<_>, _>>()?;
192                         match &mut result {
193                             Some(r) => r.extend(chunk),
194                             x @ None => *x = Some(chunk),
195                         }
196                     } else {
197                         // FIXME _expr contains invalid expression
198                         continue; // ignore entry
199                     }
200                 }
201                 if result.is_none() {
202                     continue; // no valid locations
203                 }
204                 let list_id = out_unit.locations.add(write::LocationList(result.unwrap()));
205                 write::AttributeValue::LocationListRef(list_id)
206             }
207             AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
208                 // We do not really "rewrite" the frame base so much as replace it outright.
209                 // References to it through the DW_OP_fbreg opcode will be expanded below.
210                 let mut cfa = write::Expression::new();
211                 cfa.op(gimli::DW_OP_call_frame_cfa);
212                 write::AttributeValue::Exprloc(cfa)
213             }
214             AttributeValue::Exprloc(ref expr) => {
215                 let frame_base =
216                     if let FileAttributeContext::Children { frame_base, .. } = file_context {
217                         frame_base
218                     } else {
219                         None
220                     };
221                 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? {
222                     if expr.is_simple() {
223                         if let Some(expr) = expr.build() {
224                             write::AttributeValue::Exprloc(expr)
225                         } else {
226                             continue;
227                         }
228                     } else {
229                         // Conversion to loclist is required.
230                         if let Some(scope_ranges) = scope_ranges {
231                             let exprs = expr
232                                 .build_with_locals(scope_ranges, addr_tr, frame_info, isa)
233                                 .collect::<Result<Vec<_>, _>>()?;
234                             if exprs.is_empty() {
235                                 continue;
236                             }
237                             let found_single_expr = {
238                                 // Micro-optimization all expressions alike, use one exprloc.
239                                 let mut found_expr: Option<write::Expression> = None;
240                                 for (_, _, expr) in &exprs {
241                                     if let Some(ref prev_expr) = found_expr {
242                                         if expr == prev_expr {
243                                             continue; // the same expression
244                                         }
245                                         found_expr = None;
246                                         break;
247                                     }
248                                     found_expr = Some(expr.clone())
249                                 }
250                                 found_expr
251                             };
252                             if let Some(expr) = found_single_expr {
253                                 write::AttributeValue::Exprloc(expr)
254                             } else if is_exprloc_to_loclist_allowed(attr.name()) {
255                                 // Converting exprloc to loclist.
256                                 let mut locs = Vec::new();
257                                 for (begin, length, data) in exprs {
258                                     if length == 0 {
259                                         // Ignore empty range
260                                         continue;
261                                     }
262                                     locs.push(write::Location::StartLength {
263                                         begin,
264                                         length,
265                                         data,
266                                     });
267                                 }
268                                 let list_id = out_unit.locations.add(write::LocationList(locs));
269                                 write::AttributeValue::LocationListRef(list_id)
270                             } else {
271                                 continue;
272                             }
273                         } else {
274                             continue;
275                         }
276                     }
277                 } else {
278                     // FIXME _expr contains invalid expression
279                     continue; // ignore attribute
280                 }
281             }
282             AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e),
283             AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e),
284             AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e),
285             AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e),
286             AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e),
287             AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e),
288             AttributeValue::Language(e) => write::AttributeValue::Language(e),
289             AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e),
290             AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e),
291             AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e),
292             AttributeValue::Inline(e) => write::AttributeValue::Inline(e),
293             AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e),
294             AttributeValue::UnitRef(offset) => {
295                 pending_die_refs.insert(current_scope_id, attr.name(), offset);
296                 continue;
297             }
298             AttributeValue::DebugInfoRef(offset) => {
299                 pending_di_refs.insert(current_scope_id, attr.name(), offset);
300                 continue;
301             }
302             AttributeValue::String(d) => write::AttributeValue::String(d.to_slice()?.into_owned()),
303             a => bail!("Unexpected attribute: {:?}", a),
304         };
305         let current_scope = out_unit.get_mut(current_scope_id);
306         current_scope.set(attr.name(), out_attr_value);
307     }
308     Ok(())
309 }
310