wasmtime_cranelift/debug/transform/
attr.rs1use 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 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 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 continue; }
198 }
199 if result.is_none() {
200 continue; }
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 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 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; }
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 let mut locs = Vec::new();
248 for (begin, length, data) in exprs {
249 if length == 0 {
250 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 continue; }
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}