1 use crate::debug::Reader;
2 use crate::debug::transform::utils::resolve_die_ref;
3
4 use super::address_transform::AddressTransform;
5 use super::dbi_log;
6 use super::expression::{CompiledExpression, FunctionFrameInfo, compile_expression};
7 use super::range_info_builder::RangeInfoBuilder;
8 use super::unit::InheritedAttr;
9 use cranelift_codegen::isa::TargetIsa;
10 use gimli::{AttributeValue, UnitOffset, write};
11 use wasmtime_environ::error::Error;
12
13 #[derive(Debug)]
14 pub(crate) struct EntryAttributesContext<'a> {
15 pub subprograms: &'a mut InheritedAttr<SubprogramContext>,
16 pub frame_base: Option<&'a CompiledExpression>,
17 }
18
19 #[derive(Debug)]
20 pub struct SubprogramContext {
21 pub obj_ptr: UnitOffset,
22 pub param_num: isize,
23 }
24
is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool25 fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool {
26 match attr_name {
27 gimli::DW_AT_location
28 | gimli::DW_AT_string_length
29 | gimli::DW_AT_return_addr
30 | gimli::DW_AT_data_member_location
31 | gimli::DW_AT_frame_base
32 | gimli::DW_AT_segment
33 | gimli::DW_AT_static_link
34 | gimli::DW_AT_use_location
35 | gimli::DW_AT_vtable_elem_location => true,
36 _ => false,
37 }
38 }
39
clone_die_attributes<'a>( convert_unit: &mut gimli::write::ConvertUnit<Reader<'a>>, entry: &gimli::write::ConvertUnitEntry<Reader<'a>>, addr_tr: &AddressTransform, frame_info: Option<&FunctionFrameInfo>, out_entry_id: write::UnitEntryId, subprogram_range_builder: Option<RangeInfoBuilder>, scope_ranges: Option<&Vec<(u64, u64)>>, mut attr_context: EntryAttributesContext, isa: &dyn TargetIsa, ) -> Result<(), Error>40 pub(crate) fn clone_die_attributes<'a>(
41 convert_unit: &mut gimli::write::ConvertUnit<Reader<'a>>,
42 entry: &gimli::write::ConvertUnitEntry<Reader<'a>>,
43 addr_tr: &AddressTransform,
44 frame_info: Option<&FunctionFrameInfo>,
45 out_entry_id: write::UnitEntryId,
46 subprogram_range_builder: Option<RangeInfoBuilder>,
47 scope_ranges: Option<&Vec<(u64, u64)>>,
48 mut attr_context: EntryAttributesContext,
49 isa: &dyn TargetIsa,
50 ) -> Result<(), Error> {
51 let unit = entry.read_unit;
52 let unit_encoding = unit.encoding();
53
54 let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder {
55 subprogram_range_builder
56 } else {
57 // FIXME for CU: currently address_transform operate on a single
58 // function range, and when CU spans multiple ranges the
59 // transformation may be incomplete.
60 RangeInfoBuilder::from(entry)?
61 };
62 range_info.build(addr_tr, convert_unit.unit, out_entry_id);
63
64 let mut is_obj_ptr = false;
65 prepare_die_context(entry, &mut attr_context, &mut is_obj_ptr)?;
66
67 for attr in &entry.attrs {
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_object_pointer => {
74 // Our consumers cannot handle 'this' typed as a non-pointer (recall
75 // we translate all pointers to wrapper types), making it unusable.
76 // To remedy this, we 'strip' instance-ness off of methods by removing
77 // DW_AT_object_pointer and renaming 'this' to '__this'.
78 if let Some(ref mut subprogram) =
79 attr_context.subprograms.top_with_depth_mut(entry.depth)
80 {
81 // We expect this to reference a child entry in the same unit.
82 if let Some(unit_offs) = match attr.value() {
83 AttributeValue::DebugInfoRef(di_ref) => di_ref.to_unit_offset(&unit.header),
84 AttributeValue::UnitRef(unit_ref) => Some(unit_ref),
85 _ => None,
86 } {
87 subprogram.obj_ptr = unit_offs;
88 dbi_log!("Stripped DW_AT_object_pointer");
89 continue;
90 }
91 }
92 }
93 _ => {}
94 }
95
96 if is_obj_ptr {
97 match attr.name() {
98 gimli::DW_AT_artificial => {
99 dbi_log!("Object pointer: stripped DW_AT_artificial");
100 continue;
101 }
102 gimli::DW_AT_name => {
103 let old_name: &str = &unit.attr_string(attr.value())?.to_string_lossy();
104 let new_name = format!("__{old_name}");
105 dbi_log!(
106 "Object pointer: renamed '{}' -> '{}'",
107 old_name,
108 new_name.as_str()
109 );
110
111 let attr_value =
112 write::AttributeValue::StringRef(convert_unit.strings.add(new_name));
113 convert_unit
114 .unit
115 .get_mut(out_entry_id)
116 .set(gimli::DW_AT_name, attr_value);
117 continue;
118 }
119 _ => {}
120 }
121 }
122
123 let attr_value = attr.value();
124 let out_attr_value = match attr_value {
125 AttributeValue::Addr(u) => {
126 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
127 write::AttributeValue::Address(addr)
128 }
129 AttributeValue::DebugAddrIndex(i) => {
130 let u = unit.address(i)?;
131 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
132 write::AttributeValue::Address(addr)
133 }
134 AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => {
135 let r = unit.attr_ranges_offset(attr_value)?.unwrap();
136 let range_info = RangeInfoBuilder::from_ranges_ref(unit, r)?;
137 let range_list_id = range_info.build_ranges(addr_tr, &mut convert_unit.unit.ranges);
138 write::AttributeValue::RangeListRef(range_list_id)
139 }
140 AttributeValue::LocationListsRef(_) | AttributeValue::DebugLocListsIndex(_) => {
141 let mut locs = unit.attr_locations(attr_value)?.unwrap();
142 let frame_base = attr_context.frame_base;
143
144 let mut result: Option<Vec<_>> = None;
145 while let Some(loc) = locs.next()? {
146 if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? {
147 let chunk = expr
148 .build_with_locals(
149 &[(loc.range.begin, loc.range.end)],
150 addr_tr,
151 frame_info,
152 isa,
153 )
154 .expressions
155 .filter(|i| {
156 // Ignore empty range
157 if let Ok((_, 0, _)) = i { false } else { true }
158 })
159 .map(|i| {
160 i.map(|(start, len, expr)| write::Location::StartLength {
161 begin: start,
162 length: len,
163 data: expr,
164 })
165 })
166 .collect::<Result<Vec<_>, _>>()?;
167 match &mut result {
168 Some(r) => r.extend(chunk),
169 x @ None => *x = Some(chunk),
170 }
171 } else {
172 // FIXME _expr contains invalid expression
173 continue; // ignore entry
174 }
175 }
176 if result.is_none() {
177 continue; // no valid locations
178 }
179 let list_id = convert_unit
180 .unit
181 .locations
182 .add(write::LocationList(result.unwrap()));
183 write::AttributeValue::LocationListRef(list_id)
184 }
185 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
186 // We do not really "rewrite" the frame base so much as replace it outright.
187 // References to it through the DW_OP_fbreg opcode will be expanded below.
188 let mut cfa = write::Expression::new();
189 cfa.op(gimli::DW_OP_call_frame_cfa);
190 write::AttributeValue::Exprloc(cfa)
191 }
192 AttributeValue::Exprloc(ref expr) => {
193 let frame_base = attr_context.frame_base;
194 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? {
195 if expr.is_simple() {
196 if let Some(expr) = expr.build() {
197 write::AttributeValue::Exprloc(expr)
198 } else {
199 continue;
200 }
201 } else {
202 // Conversion to loclist is required.
203 if let Some(scope_ranges) = scope_ranges {
204 let built_expression =
205 expr.build_with_locals(scope_ranges, addr_tr, frame_info, isa);
206 let exprs = built_expression
207 .expressions
208 .collect::<Result<Vec<_>, _>>()?;
209 if exprs.is_empty() {
210 continue;
211 }
212 // Micro-optimization all expressions alike, use one exprloc.
213 let mut single_expr: Option<write::Expression> = None;
214 if built_expression.covers_entire_scope {
215 for (_, _, expr) in &exprs {
216 if let Some(ref prev_expr) = single_expr {
217 if expr == prev_expr {
218 continue; // the same expression
219 }
220 single_expr = None;
221 break;
222 }
223 single_expr = Some(expr.clone())
224 }
225 }
226 if let Some(expr) = single_expr {
227 write::AttributeValue::Exprloc(expr)
228 } else if is_exprloc_to_loclist_allowed(attr.name()) {
229 // Converting exprloc to loclist.
230 let mut locs = Vec::new();
231 for (begin, length, data) in exprs {
232 if length == 0 {
233 // Ignore empty range
234 continue;
235 }
236 locs.push(write::Location::StartLength {
237 begin,
238 length,
239 data,
240 });
241 }
242 let list_id =
243 convert_unit.unit.locations.add(write::LocationList(locs));
244 write::AttributeValue::LocationListRef(list_id)
245 } else {
246 continue;
247 }
248 } else {
249 continue;
250 }
251 }
252 } else {
253 // FIXME _expr contains invalid expression
254 continue; // ignore attribute
255 }
256 }
257 // No other attributes contain addresses or address offsets.
258 _ => match convert_unit.convert_attribute_value(unit, attr, &|_| None) {
259 Ok(value) => value,
260 Err(e) => {
261 // Invalid `FileIndex` was seen in #8884 and #8904. In general it's
262 // better to ignore invalid or unknown DWARF rather then failing outright.
263 dbi_log!(
264 "Ignoring entry {:x?} attribute {} = {:x?}: {e}",
265 entry.offset.to_unit_section_offset(&convert_unit.read_unit),
266 attr.name(),
267 attr_value,
268 );
269 continue;
270 }
271 },
272 };
273 let out_entry = convert_unit.unit.get_mut(out_entry_id);
274 out_entry.set(attr.name(), out_attr_value);
275 }
276 Ok(())
277 }
278
prepare_die_context<'a>( entry: &gimli::write::ConvertUnitEntry<Reader<'a>>, attr_context: &mut EntryAttributesContext, is_obj_ptr: &mut bool, ) -> Result<(), Error>279 fn prepare_die_context<'a>(
280 entry: &gimli::write::ConvertUnitEntry<Reader<'a>>,
281 attr_context: &mut EntryAttributesContext,
282 is_obj_ptr: &mut bool,
283 ) -> Result<(), Error> {
284 let subprograms = &mut attr_context.subprograms;
285
286 // Update the current context based on what kind of entry this is.
287 match entry.tag {
288 gimli::DW_TAG_subprogram | gimli::DW_TAG_inlined_subroutine | gimli::DW_TAG_entry_point => {
289 // Push the 'context' of there being no parameters (yet).
290 subprograms.push(
291 entry.depth,
292 SubprogramContext {
293 obj_ptr: UnitOffset { 0: 0 },
294 param_num: -1,
295 },
296 );
297 }
298 gimli::DW_TAG_formal_parameter => {
299 // Formal parameter tags can be parented by catch blocks
300 // and such - not just subprogram DIEs. So we need to check
301 // that this DIE is indeed a direct child of a subprogram.
302 if let Some(subprogram) = subprograms.top_with_depth_mut(entry.depth - 1) {
303 subprogram.param_num += 1;
304
305 if subprogram.obj_ptr == entry.offset
306 || is_obj_ptr_param(entry, subprogram.param_num)?
307 {
308 *is_obj_ptr = true;
309 }
310 }
311 }
312 _ => {}
313 }
314 Ok(())
315 }
316
is_obj_ptr_param( entry: &gimli::write::ConvertUnitEntry<Reader<'_>>, param_num: isize, ) -> Result<bool, Error>317 fn is_obj_ptr_param(
318 entry: &gimli::write::ConvertUnitEntry<Reader<'_>>,
319 param_num: isize,
320 ) -> Result<bool, Error> {
321 debug_assert_eq!(entry.tag, gimli::DW_TAG_formal_parameter);
322 let unit = entry.read_unit;
323
324 // This logic was taken loosely from LLDB. It is known
325 // that it is not fully correct (doesn't handle 'deduced
326 // this', for example).
327 // Q: DWARF includes DW_AT_object_pointer as we use it,
328 // why do we need this heuristic as well?
329 // A: Declarations do not include DW_AT_object_pointer.
330 if param_num == 0
331 && entry.attr_value(gimli::DW_AT_artificial) == Some(AttributeValue::Flag(true))
332 {
333 // Either this has no name (declarations omit them), or its explicitly "this".
334 let name = entry.attr_value(gimli::DW_AT_name);
335 if name.is_none() || unit.attr_string(name.unwrap())?.slice().eq(b"this") {
336 // Finally, a type check. We expect a pointer.
337 if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type) {
338 if let Some(type_die) = resolve_die_ref(unit, &type_attr)? {
339 return Ok(type_die.tag == gimli::DW_TAG_pointer_type);
340 }
341 }
342 }
343 };
344
345 return Ok(false);
346 }
347