Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/cranelift/src/debug/transform/attr.rs
3071 views
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
25
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
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
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
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
348