wasmtime_cranelift/debug/transform/
attr.rs

1use super::address_transform::AddressTransform;
2use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo};
3use super::range_info_builder::RangeInfoBuilder;
4use super::refs::{PendingDebugInfoRefs, PendingUnitRefs};
5use super::{DebugInputContext, Reader, TransformError};
6use anyhow::{bail, Error};
7use cranelift_codegen::isa::TargetIsa;
8use gimli::{
9    write, AttributeValue, DebugLineOffset, DebugLineStr, DebugStr, DebugStrOffsets,
10    DebuggingInformationEntry, Unit,
11};
12
13#[derive(Debug)]
14pub(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
23fn 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
38pub(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>
56where
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::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
86                continue;
87            }
88            AttributeValue::DebugAddrBase(_) | AttributeValue::DebugStrOffsetsBase(_) => {
89                continue;
90            }
91
92            AttributeValue::Addr(u) => {
93                let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
94                write::AttributeValue::Address(addr)
95            }
96            AttributeValue::DebugAddrIndex(i) => {
97                let u = context.debug_addr.get_address(4, unit.addr_base, i)?;
98                let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
99                write::AttributeValue::Address(addr)
100            }
101            AttributeValue::Udata(u) => write::AttributeValue::Udata(u),
102            AttributeValue::Data1(d) => write::AttributeValue::Data1(d),
103            AttributeValue::Data2(d) => write::AttributeValue::Data2(d),
104            AttributeValue::Data4(d) => write::AttributeValue::Data4(d),
105            AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d),
106            AttributeValue::Flag(f) => write::AttributeValue::Flag(f),
107            AttributeValue::DebugLineRef(line_program_offset) => {
108                if let FileAttributeContext::Root(o) = file_context {
109                    if o != Some(line_program_offset) {
110                        return Err(TransformError("invalid debug_line offset").into());
111                    }
112                    write::AttributeValue::LineProgramRef
113                } else {
114                    return Err(TransformError("unexpected debug_line index attribute").into());
115                }
116            }
117            AttributeValue::FileIndex(i) => {
118                if let FileAttributeContext::Children {
119                    file_map,
120                    file_index_base,
121                    ..
122                } = file_context
123                {
124                    write::AttributeValue::FileIndex(Some(file_map[(i - file_index_base) as usize]))
125                } else {
126                    return Err(TransformError("unexpected file index attribute").into());
127                }
128            }
129            AttributeValue::DebugStrRef(str_offset) => {
130                let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
131                write::AttributeValue::StringRef(out_strings.add(s))
132            }
133            AttributeValue::DebugStrOffsetsIndex(i) => {
134                let str_offset = context.debug_str_offsets.get_str_offset(
135                    gimli::Format::Dwarf32,
136                    unit.str_offsets_base,
137                    i,
138                )?;
139                let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
140                write::AttributeValue::StringRef(out_strings.add(s))
141            }
142            AttributeValue::RangeListsRef(r) => {
143                let r = dwarf.ranges_offset_from_raw(unit, r);
144                let range_info = RangeInfoBuilder::from_ranges_ref(unit, r, context, cu_low_pc)?;
145                let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges);
146                write::AttributeValue::RangeListRef(range_list_id)
147            }
148            AttributeValue::LocationListsRef(r) => {
149                let low_pc = 0;
150                let mut locs = context.loclists.locations(
151                    r,
152                    unit_encoding,
153                    low_pc,
154                    &context.debug_addr,
155                    unit.addr_base,
156                )?;
157                let frame_base =
158                    if let FileAttributeContext::Children { frame_base, .. } = file_context {
159                        frame_base
160                    } else {
161                        None
162                    };
163
164                let mut result: Option<Vec<_>> = None;
165                while let Some(loc) = locs.next()? {
166                    if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? {
167                        let chunk = expr
168                            .build_with_locals(
169                                &[(loc.range.begin, loc.range.end)],
170                                addr_tr,
171                                frame_info,
172                                isa,
173                            )
174                            .filter(|i| {
175                                // Ignore empty range
176                                if let Ok((_, 0, _)) = i {
177                                    false
178                                } else {
179                                    true
180                                }
181                            })
182                            .map(|i| {
183                                i.map(|(start, len, expr)| write::Location::StartLength {
184                                    begin: start,
185                                    length: len,
186                                    data: expr,
187                                })
188                            })
189                            .collect::<Result<Vec<_>, _>>()?;
190                        match &mut result {
191                            Some(r) => r.extend(chunk),
192                            x @ None => *x = Some(chunk),
193                        }
194                    } else {
195                        // FIXME _expr contains invalid expression
196                        continue; // ignore entry
197                    }
198                }
199                if result.is_none() {
200                    continue; // no valid locations
201                }
202                let list_id = out_unit.locations.add(write::LocationList(result.unwrap()));
203                write::AttributeValue::LocationListRef(list_id)
204            }
205            AttributeValue::Exprloc(ref expr) => {
206                let frame_base =
207                    if let FileAttributeContext::Children { frame_base, .. } = file_context {
208                        frame_base
209                    } else {
210                        None
211                    };
212                if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? {
213                    if expr.is_simple() {
214                        if let Some(expr) = expr.build() {
215                            write::AttributeValue::Exprloc(expr)
216                        } else {
217                            continue;
218                        }
219                    } else {
220                        // Conversion to loclist is required.
221                        if let Some(scope_ranges) = scope_ranges {
222                            let exprs = expr
223                                .build_with_locals(scope_ranges, addr_tr, frame_info, isa)
224                                .collect::<Result<Vec<_>, _>>()?;
225                            if exprs.is_empty() {
226                                continue;
227                            }
228                            let found_single_expr = {
229                                // Micro-optimization all expressions alike, use one exprloc.
230                                let mut found_expr: Option<write::Expression> = None;
231                                for (_, _, expr) in &exprs {
232                                    if let Some(ref prev_expr) = found_expr {
233                                        if expr == prev_expr {
234                                            continue; // the same expression
235                                        }
236                                        found_expr = None;
237                                        break;
238                                    }
239                                    found_expr = Some(expr.clone())
240                                }
241                                found_expr
242                            };
243                            if let Some(expr) = found_single_expr {
244                                write::AttributeValue::Exprloc(expr)
245                            } else if is_exprloc_to_loclist_allowed(attr.name()) {
246                                // Converting exprloc to loclist.
247                                let mut locs = Vec::new();
248                                for (begin, length, data) in exprs {
249                                    if length == 0 {
250                                        // Ignore empty range
251                                        continue;
252                                    }
253                                    locs.push(write::Location::StartLength {
254                                        begin,
255                                        length,
256                                        data,
257                                    });
258                                }
259                                let list_id = out_unit.locations.add(write::LocationList(locs));
260                                write::AttributeValue::LocationListRef(list_id)
261                            } else {
262                                continue;
263                            }
264                        } else {
265                            continue;
266                        }
267                    }
268                } else {
269                    // FIXME _expr contains invalid expression
270                    continue; // ignore attribute
271                }
272            }
273            AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e),
274            AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e),
275            AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e),
276            AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e),
277            AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e),
278            AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e),
279            AttributeValue::Language(e) => write::AttributeValue::Language(e),
280            AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e),
281            AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e),
282            AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e),
283            AttributeValue::Inline(e) => write::AttributeValue::Inline(e),
284            AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e),
285            AttributeValue::UnitRef(offset) => {
286                pending_die_refs.insert(current_scope_id, attr.name(), offset);
287                continue;
288            }
289            AttributeValue::DebugInfoRef(offset) => {
290                pending_di_refs.insert(current_scope_id, attr.name(), offset);
291                continue;
292            }
293            a => bail!("Unexpected attribute: {:?}", a),
294        };
295        let current_scope = out_unit.get_mut(current_scope_id);
296        current_scope.set(attr.name(), attr_value);
297    }
298    Ok(())
299}
300
301pub(crate) fn clone_attr_string<R>(
302    attr_value: &AttributeValue<R>,
303    form: gimli::DwForm,
304    unit: &Unit<R, R::Offset>,
305    debug_str: &DebugStr<R>,
306    debug_str_offsets: &DebugStrOffsets<R>,
307    debug_line_str: &DebugLineStr<R>,
308    out_strings: &mut write::StringTable,
309) -> Result<write::LineString, Error>
310where
311    R: Reader,
312{
313    let content = match attr_value {
314        AttributeValue::DebugStrRef(str_offset) => {
315            debug_str.get_str(*str_offset)?.to_slice()?.to_vec()
316        }
317        AttributeValue::DebugStrOffsetsIndex(i) => {
318            let str_offset = debug_str_offsets.get_str_offset(
319                gimli::Format::Dwarf32,
320                unit.str_offsets_base,
321                *i,
322            )?;
323            debug_str.get_str(str_offset)?.to_slice()?.to_vec()
324        }
325        AttributeValue::DebugLineStrRef(str_offset) => {
326            debug_line_str.get_str(*str_offset)?.to_slice()?.to_vec()
327        }
328        AttributeValue::String(b) => b.to_slice()?.to_vec(),
329        v => bail!("Unexpected attribute value: {:?}", v),
330    };
331    Ok(match form {
332        gimli::DW_FORM_strp => {
333            let id = out_strings.add(content);
334            write::LineString::StringRef(id)
335        }
336        gimli::DW_FORM_string => write::LineString::String(content),
337        _ => bail!("DW_FORM_line_strp or other not supported"),
338    })
339}