/src/gimli/src/write/op.rs
Line | Count | Source |
1 | | use alloc::boxed::Box; |
2 | | use alloc::vec::Vec; |
3 | | |
4 | | use crate::common::{Encoding, Register}; |
5 | | use crate::constants::{self, DwOp}; |
6 | | use crate::leb128::write::{sleb128_size, uleb128_size}; |
7 | | use crate::write::{ |
8 | | Address, DebugInfoFixup, DebugInfoRef, Error, Result, UnitEntryId, UnitOffsets, Writer, |
9 | | }; |
10 | | |
11 | | /// The bytecode for a DWARF expression or location description. |
12 | | #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] |
13 | | pub struct Expression { |
14 | | operations: Vec<Operation>, |
15 | | } |
16 | | |
17 | | impl Expression { |
18 | | /// Create an empty expression. |
19 | | #[inline] |
20 | | pub fn new() -> Self { |
21 | | Self::default() |
22 | | } |
23 | | |
24 | | /// Create an expression from raw bytecode. |
25 | | /// |
26 | | /// This does not support operations that require references, such as `DW_OP_addr`. |
27 | | #[inline] |
28 | | pub fn raw(bytecode: Vec<u8>) -> Self { |
29 | | Expression { |
30 | | operations: vec![Operation::Raw(bytecode)], |
31 | | } |
32 | | } |
33 | | |
34 | | /// Add an operation to the expression. |
35 | | /// |
36 | | /// This should only be used for operations that have no explicit operands. |
37 | 0 | pub fn op(&mut self, opcode: DwOp) { |
38 | 0 | self.operations.push(Operation::Simple(opcode)); |
39 | 0 | } |
40 | | |
41 | | /// Add a `DW_OP_addr` operation to the expression. |
42 | 0 | pub fn op_addr(&mut self, address: Address) { |
43 | 0 | self.operations.push(Operation::Address(address)); |
44 | 0 | } |
45 | | |
46 | | /// Add a `DW_OP_constu` operation to the expression. |
47 | | /// |
48 | | /// This may be emitted as a smaller equivalent operation. |
49 | 0 | pub fn op_constu(&mut self, value: u64) { |
50 | 0 | self.operations.push(Operation::UnsignedConstant(value)); |
51 | 0 | } |
52 | | |
53 | | /// Add a `DW_OP_consts` operation to the expression. |
54 | | /// |
55 | | /// This may be emitted as a smaller equivalent operation. |
56 | 0 | pub fn op_consts(&mut self, value: i64) { |
57 | 0 | self.operations.push(Operation::SignedConstant(value)); |
58 | 0 | } |
59 | | |
60 | | /// Add a `DW_OP_const_type` or `DW_OP_GNU_const_type` operation to the expression. |
61 | 0 | pub fn op_const_type(&mut self, base: UnitEntryId, value: Box<[u8]>) { |
62 | 0 | self.operations.push(Operation::ConstantType(base, value)); |
63 | 0 | } |
64 | | |
65 | | /// Add a `DW_OP_fbreg` operation to the expression. |
66 | 0 | pub fn op_fbreg(&mut self, offset: i64) { |
67 | 0 | self.operations.push(Operation::FrameOffset(offset)); |
68 | 0 | } |
69 | | |
70 | | /// Add a `DW_OP_bregx` operation to the expression. |
71 | | /// |
72 | | /// This may be emitted as a smaller equivalent operation. |
73 | 0 | pub fn op_breg(&mut self, register: Register, offset: i64) { |
74 | 0 | self.operations |
75 | 0 | .push(Operation::RegisterOffset(register, offset)); |
76 | 0 | } |
77 | | |
78 | | /// Add a `DW_OP_regval_type` or `DW_OP_GNU_regval_type` operation to the expression. |
79 | | /// |
80 | | /// This may be emitted as a smaller equivalent operation. |
81 | 0 | pub fn op_regval_type(&mut self, register: Register, base: UnitEntryId) { |
82 | 0 | self.operations |
83 | 0 | .push(Operation::RegisterType(register, base)); |
84 | 0 | } |
85 | | |
86 | | /// Add a `DW_OP_pick` operation to the expression. |
87 | | /// |
88 | | /// This may be emitted as a `DW_OP_dup` or `DW_OP_over` operation. |
89 | 0 | pub fn op_pick(&mut self, index: u8) { |
90 | 0 | self.operations.push(Operation::Pick(index)); |
91 | 0 | } |
92 | | |
93 | | /// Add a `DW_OP_deref` operation to the expression. |
94 | 0 | pub fn op_deref(&mut self) { |
95 | 0 | self.operations.push(Operation::Deref { space: false }); |
96 | 0 | } |
97 | | |
98 | | /// Add a `DW_OP_xderef` operation to the expression. |
99 | 0 | pub fn op_xderef(&mut self) { |
100 | 0 | self.operations.push(Operation::Deref { space: true }); |
101 | 0 | } |
102 | | |
103 | | /// Add a `DW_OP_deref_size` operation to the expression. |
104 | 0 | pub fn op_deref_size(&mut self, size: u8) { |
105 | 0 | self.operations |
106 | 0 | .push(Operation::DerefSize { size, space: false }); |
107 | 0 | } |
108 | | |
109 | | /// Add a `DW_OP_xderef_size` operation to the expression. |
110 | 0 | pub fn op_xderef_size(&mut self, size: u8) { |
111 | 0 | self.operations |
112 | 0 | .push(Operation::DerefSize { size, space: true }); |
113 | 0 | } |
114 | | |
115 | | /// Add a `DW_OP_deref_type` or `DW_OP_GNU_deref_type` operation to the expression. |
116 | 0 | pub fn op_deref_type(&mut self, size: u8, base: UnitEntryId) { |
117 | 0 | self.operations.push(Operation::DerefType { |
118 | 0 | size, |
119 | 0 | base, |
120 | 0 | space: false, |
121 | 0 | }); |
122 | 0 | } |
123 | | |
124 | | /// Add a `DW_OP_xderef_type` operation to the expression. |
125 | 0 | pub fn op_xderef_type(&mut self, size: u8, base: UnitEntryId) { |
126 | 0 | self.operations.push(Operation::DerefType { |
127 | 0 | size, |
128 | 0 | base, |
129 | 0 | space: true, |
130 | 0 | }); |
131 | 0 | } |
132 | | |
133 | | /// Add a `DW_OP_plus_uconst` operation to the expression. |
134 | 0 | pub fn op_plus_uconst(&mut self, value: u64) { |
135 | 0 | self.operations.push(Operation::PlusConstant(value)); |
136 | 0 | } |
137 | | |
138 | | /// Add a `DW_OP_skip` operation to the expression. |
139 | | /// |
140 | | /// Returns the index of the operation. The caller must call `set_target` with |
141 | | /// this index to set the target of the branch. |
142 | 0 | pub fn op_skip(&mut self) -> usize { |
143 | 0 | let index = self.next_index(); |
144 | 0 | self.operations.push(Operation::Skip(!0)); |
145 | 0 | index |
146 | 0 | } |
147 | | |
148 | | /// Add a `DW_OP_bra` operation to the expression. |
149 | | /// |
150 | | /// Returns the index of the operation. The caller must call `set_target` with |
151 | | /// this index to set the target of the branch. |
152 | 0 | pub fn op_bra(&mut self) -> usize { |
153 | 0 | let index = self.next_index(); |
154 | 0 | self.operations.push(Operation::Branch(!0)); |
155 | 0 | index |
156 | 0 | } |
157 | | |
158 | | /// Return the index that will be assigned to the next operation. |
159 | | /// |
160 | | /// This can be passed to `set_target`. |
161 | | #[inline] |
162 | 0 | pub fn next_index(&self) -> usize { |
163 | 0 | self.operations.len() |
164 | 0 | } |
165 | | |
166 | | /// Set the target of a `DW_OP_skip` or `DW_OP_bra` operation . |
167 | 0 | pub fn set_target(&mut self, operation: usize, new_target: usize) { |
168 | 0 | debug_assert!(new_target <= self.next_index()); |
169 | 0 | debug_assert_ne!(operation, new_target); |
170 | 0 | match self.operations[operation] { |
171 | 0 | Operation::Skip(ref mut target) | Operation::Branch(ref mut target) => { |
172 | 0 | *target = new_target; |
173 | 0 | } |
174 | 0 | _ => unimplemented!(), |
175 | | } |
176 | 0 | } |
177 | | |
178 | | /// Add a `DW_OP_call4` operation to the expression. |
179 | 0 | pub fn op_call(&mut self, entry: UnitEntryId) { |
180 | 0 | self.operations.push(Operation::Call(entry)); |
181 | 0 | } |
182 | | |
183 | | /// Add a `DW_OP_call_ref` operation to the expression. |
184 | 0 | pub fn op_call_ref(&mut self, entry: DebugInfoRef) { |
185 | 0 | self.operations.push(Operation::CallRef(entry)); |
186 | 0 | } |
187 | | |
188 | | /// Add a `DW_OP_convert` or `DW_OP_GNU_convert` operation to the expression. |
189 | | /// |
190 | | /// `base` is the DIE of the base type, or `None` for the generic type. |
191 | 0 | pub fn op_convert(&mut self, base: Option<UnitEntryId>) { |
192 | 0 | self.operations.push(Operation::Convert(base)); |
193 | 0 | } |
194 | | |
195 | | /// Add a `DW_OP_reinterpret` or `DW_OP_GNU_reinterpret` operation to the expression. |
196 | | /// |
197 | | /// `base` is the DIE of the base type, or `None` for the generic type. |
198 | 0 | pub fn op_reinterpret(&mut self, base: Option<UnitEntryId>) { |
199 | 0 | self.operations.push(Operation::Reinterpret(base)); |
200 | 0 | } |
201 | | |
202 | | /// Add a `DW_OP_entry_value` or `DW_OP_GNU_entry_value` operation to the expression. |
203 | 0 | pub fn op_entry_value(&mut self, expression: Expression) { |
204 | 0 | self.operations.push(Operation::EntryValue(expression)); |
205 | 0 | } |
206 | | |
207 | | /// Add a `DW_OP_regx` operation to the expression. |
208 | | /// |
209 | | /// This may be emitted as a smaller equivalent operation. |
210 | 0 | pub fn op_reg(&mut self, register: Register) { |
211 | 0 | self.operations.push(Operation::Register(register)); |
212 | 0 | } |
213 | | |
214 | | /// Add a `DW_OP_implicit_value` operation to the expression. |
215 | 0 | pub fn op_implicit_value(&mut self, data: Box<[u8]>) { |
216 | 0 | self.operations.push(Operation::ImplicitValue(data)); |
217 | 0 | } |
218 | | |
219 | | /// Add a `DW_OP_implicit_pointer` or `DW_OP_GNU_implicit_pointer` operation to the expression. |
220 | 0 | pub fn op_implicit_pointer(&mut self, entry: DebugInfoRef, byte_offset: i64) { |
221 | 0 | self.operations |
222 | 0 | .push(Operation::ImplicitPointer { entry, byte_offset }); |
223 | 0 | } |
224 | | |
225 | | /// Add a `DW_OP_piece` operation to the expression. |
226 | 0 | pub fn op_piece(&mut self, size_in_bytes: u64) { |
227 | 0 | self.operations.push(Operation::Piece { size_in_bytes }); |
228 | 0 | } |
229 | | |
230 | | /// Add a `DW_OP_bit_piece` operation to the expression. |
231 | 0 | pub fn op_bit_piece(&mut self, size_in_bits: u64, bit_offset: u64) { |
232 | 0 | self.operations.push(Operation::BitPiece { |
233 | 0 | size_in_bits, |
234 | 0 | bit_offset, |
235 | 0 | }); |
236 | 0 | } |
237 | | |
238 | | /// Add a `DW_OP_GNU_parameter_ref` operation to the expression. |
239 | 0 | pub fn op_gnu_parameter_ref(&mut self, entry: UnitEntryId) { |
240 | 0 | self.operations.push(Operation::ParameterRef(entry)); |
241 | 0 | } |
242 | | |
243 | | /// Add a `DW_OP_WASM_location 0x0` operation to the expression. |
244 | 0 | pub fn op_wasm_local(&mut self, index: u32) { |
245 | 0 | self.operations.push(Operation::WasmLocal(index)); |
246 | 0 | } |
247 | | |
248 | | /// Add a `DW_OP_WASM_location 0x1` operation to the expression. |
249 | 0 | pub fn op_wasm_global(&mut self, index: u32) { |
250 | 0 | self.operations.push(Operation::WasmGlobal(index)); |
251 | 0 | } |
252 | | |
253 | | /// Add a `DW_OP_WASM_location 0x2` operation to the expression. |
254 | 0 | pub fn op_wasm_stack(&mut self, index: u32) { |
255 | 0 | self.operations.push(Operation::WasmStack(index)); |
256 | 0 | } |
257 | | |
258 | 0 | pub(crate) fn size( |
259 | 0 | &self, |
260 | 0 | encoding: Encoding, |
261 | 0 | unit_offsets: Option<&UnitOffsets>, |
262 | 0 | ) -> Result<usize> { |
263 | 0 | let mut size = 0; |
264 | 0 | for operation in &self.operations { |
265 | 0 | size += operation.size(encoding, unit_offsets)?; |
266 | | } |
267 | 0 | Ok(size) |
268 | 0 | } |
269 | | |
270 | | pub(crate) fn write<W: Writer>( |
271 | | &self, |
272 | | w: &mut W, |
273 | | mut refs: Option<&mut Vec<DebugInfoFixup>>, |
274 | | encoding: Encoding, |
275 | | unit_offsets: Option<&UnitOffsets>, |
276 | | ) -> Result<()> { |
277 | | // TODO: only calculate offsets if needed? |
278 | | let mut offsets = Vec::with_capacity(self.operations.len()); |
279 | | let mut offset = w.len(); |
280 | | for operation in &self.operations { |
281 | | offsets.push(offset); |
282 | | offset += operation.size(encoding, unit_offsets)?; |
283 | | } |
284 | | offsets.push(offset); |
285 | | for (operation, offset) in self.operations.iter().zip(offsets.iter().copied()) { |
286 | | debug_assert_eq!(w.len(), offset); |
287 | | operation.write(w, refs.as_deref_mut(), encoding, unit_offsets, &offsets)?; |
288 | | } |
289 | | debug_assert_eq!(w.len(), offset); |
290 | | Ok(()) |
291 | | } |
292 | | } |
293 | | |
294 | | /// A single DWARF operation. |
295 | | // |
296 | | // This type is intentionally not public so that we can change the |
297 | | // representation of expressions as needed. |
298 | | // |
299 | | // Variants are listed in the order they appear in Section 2.5. |
300 | | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
301 | | enum Operation { |
302 | | /// Raw bytecode. |
303 | | /// |
304 | | /// Does not support references. |
305 | | Raw(Vec<u8>), |
306 | | /// An operation that has no explicit operands. |
307 | | /// |
308 | | /// Represents: |
309 | | /// - `DW_OP_drop`, `DW_OP_swap`, `DW_OP_rot` |
310 | | /// - `DW_OP_push_object_address`, `DW_OP_form_tls_address`, `DW_OP_call_frame_cfa` |
311 | | /// - `DW_OP_abs`, `DW_OP_and`, `DW_OP_div`, `DW_OP_minus`, `DW_OP_mod`, `DW_OP_mul`, |
312 | | /// `DW_OP_neg`, `DW_OP_not`, `DW_OP_or`, `DW_OP_plus`, `DW_OP_shl`, `DW_OP_shr`, |
313 | | /// `DW_OP_shra`, `DW_OP_xor` |
314 | | /// - `DW_OP_le`, `DW_OP_ge`, `DW_OP_eq`, `DW_OP_lt`, `DW_OP_gt`, `DW_OP_ne` |
315 | | /// - `DW_OP_nop` |
316 | | /// - `DW_OP_stack_value` |
317 | | Simple(DwOp), |
318 | | /// Relocate the address if needed, and push it on the stack. |
319 | | /// |
320 | | /// Represents `DW_OP_addr`. |
321 | | Address(Address), |
322 | | /// Push an unsigned constant value on the stack. |
323 | | /// |
324 | | /// Represents `DW_OP_constu`. |
325 | | UnsignedConstant(u64), |
326 | | /// Push a signed constant value on the stack. |
327 | | /// |
328 | | /// Represents `DW_OP_consts`. |
329 | | SignedConstant(i64), |
330 | | /* TODO: requires .debug_addr write support |
331 | | /// Read the address at the given index in `.debug_addr, relocate the address if needed, |
332 | | /// and push it on the stack. |
333 | | /// |
334 | | /// Represents `DW_OP_addrx`. |
335 | | AddressIndex(DebugAddrIndex<Offset>), |
336 | | /// Read the address at the given index in `.debug_addr, and push it on the stack. |
337 | | /// Do not relocate the address. |
338 | | /// |
339 | | /// Represents `DW_OP_constx`. |
340 | | ConstantIndex(DebugAddrIndex<Offset>), |
341 | | */ |
342 | | /// Interpret the value bytes as a constant of a given type, and push it on the stack. |
343 | | /// |
344 | | /// Represents `DW_OP_const_type`. |
345 | | ConstantType(UnitEntryId, Box<[u8]>), |
346 | | /// Compute the frame base (using `DW_AT_frame_base`), add the |
347 | | /// given offset, and then push the resulting sum on the stack. |
348 | | /// |
349 | | /// Represents `DW_OP_fbreg`. |
350 | | FrameOffset(i64), |
351 | | /// Find the contents of the given register, add the offset, and then |
352 | | /// push the resulting sum on the stack. |
353 | | /// |
354 | | /// Represents `DW_OP_bregx`. |
355 | | RegisterOffset(Register, i64), |
356 | | /// Interpret the contents of the given register as a value of the given type, |
357 | | /// and push it on the stack. |
358 | | /// |
359 | | /// Represents `DW_OP_regval_type`. |
360 | | RegisterType(Register, UnitEntryId), |
361 | | /// Copy the item at a stack index and push it on top of the stack. |
362 | | /// |
363 | | /// Represents `DW_OP_pick`, `DW_OP_dup`, and `DW_OP_over`. |
364 | | Pick(u8), |
365 | | /// Pop the topmost value of the stack, dereference it, and push the |
366 | | /// resulting value. |
367 | | /// |
368 | | /// Represents `DW_OP_deref` and `DW_OP_xderef`. |
369 | | Deref { |
370 | | /// True if the dereference operation takes an address space |
371 | | /// argument from the stack; false otherwise. |
372 | | space: bool, |
373 | | }, |
374 | | /// Pop the topmost value of the stack, dereference it to obtain a value |
375 | | /// of the given size, and push the resulting value. |
376 | | /// |
377 | | /// Represents `DW_OP_deref_size` and `DW_OP_xderef_size`. |
378 | | DerefSize { |
379 | | /// True if the dereference operation takes an address space |
380 | | /// argument from the stack; false otherwise. |
381 | | space: bool, |
382 | | /// The size of the data to dereference. |
383 | | size: u8, |
384 | | }, |
385 | | /// Pop the topmost value of the stack, dereference it to obtain a value |
386 | | /// of the given type, and push the resulting value. |
387 | | /// |
388 | | /// Represents `DW_OP_deref_type` and `DW_OP_xderef_type`. |
389 | | DerefType { |
390 | | /// True if the dereference operation takes an address space |
391 | | /// argument from the stack; false otherwise. |
392 | | space: bool, |
393 | | /// The size of the data to dereference. |
394 | | size: u8, |
395 | | /// The DIE of the base type, or `None` for the generic type. |
396 | | base: UnitEntryId, |
397 | | }, |
398 | | /// Add an unsigned constant to the topmost value on the stack. |
399 | | /// |
400 | | /// Represents `DW_OP_plus_uconst`. |
401 | | PlusConstant(u64), |
402 | | /// Unconditional branch to the target location. |
403 | | /// |
404 | | /// The value is the index within the expression of the operation to branch to. |
405 | | /// This will be converted to a relative offset when writing. |
406 | | /// |
407 | | /// Represents `DW_OP_skip`. |
408 | | Skip(usize), |
409 | | /// Branch to the target location if the top of stack is nonzero. |
410 | | /// |
411 | | /// The value is the index within the expression of the operation to branch to. |
412 | | /// This will be converted to a relative offset when writing. |
413 | | /// |
414 | | /// Represents `DW_OP_bra`. |
415 | | Branch(usize), |
416 | | /// Evaluate a DWARF expression as a subroutine. |
417 | | /// |
418 | | /// The expression comes from the `DW_AT_location` attribute of the indicated DIE. |
419 | | /// |
420 | | /// Represents `DW_OP_call4`. |
421 | | Call(UnitEntryId), |
422 | | /// Evaluate an external DWARF expression as a subroutine. |
423 | | /// |
424 | | /// The expression comes from the `DW_AT_location` attribute of the indicated DIE, |
425 | | /// which may be in another compilation unit or shared object. |
426 | | /// |
427 | | /// Represents `DW_OP_call_ref`. |
428 | | CallRef(DebugInfoRef), |
429 | | /// Compute the value of a variable and push it on the stack. |
430 | | /// |
431 | | /// Represents `DW_OP_GNU_variable_value`. |
432 | | VariableValue(DebugInfoRef), |
433 | | /// Pop the top stack entry, convert it to a different type, and push it on the stack. |
434 | | /// |
435 | | /// Represents `DW_OP_convert`. |
436 | | Convert(Option<UnitEntryId>), |
437 | | /// Pop the top stack entry, reinterpret the bits in its value as a different type, |
438 | | /// and push it on the stack. |
439 | | /// |
440 | | /// Represents `DW_OP_reinterpret`. |
441 | | Reinterpret(Option<UnitEntryId>), |
442 | | /// Evaluate an expression at the entry to the current subprogram, and push it on the stack. |
443 | | /// |
444 | | /// Represents `DW_OP_entry_value`. |
445 | | EntryValue(Expression), |
446 | | // FIXME: EntryRegister |
447 | | /// Indicate that this piece's location is in the given register. |
448 | | /// |
449 | | /// Completes the piece or expression. |
450 | | /// |
451 | | /// Represents `DW_OP_regx`. |
452 | | Register(Register), |
453 | | /// The object has no location, but has a known constant value. |
454 | | /// |
455 | | /// Completes the piece or expression. |
456 | | /// |
457 | | /// Represents `DW_OP_implicit_value`. |
458 | | ImplicitValue(Box<[u8]>), |
459 | | /// The object is a pointer to a value which has no actual location, such as |
460 | | /// an implicit value or a stack value. |
461 | | /// |
462 | | /// Completes the piece or expression. |
463 | | /// |
464 | | /// Represents `DW_OP_implicit_pointer`. |
465 | | ImplicitPointer { |
466 | | /// The DIE of the value that this is an implicit pointer into. |
467 | | entry: DebugInfoRef, |
468 | | /// The byte offset into the value that the implicit pointer points to. |
469 | | byte_offset: i64, |
470 | | }, |
471 | | /// Terminate a piece. |
472 | | /// |
473 | | /// Represents `DW_OP_piece`. |
474 | | Piece { |
475 | | /// The size of this piece in bytes. |
476 | | size_in_bytes: u64, |
477 | | }, |
478 | | /// Terminate a piece with a size in bits. |
479 | | /// |
480 | | /// Represents `DW_OP_bit_piece`. |
481 | | BitPiece { |
482 | | /// The size of this piece in bits. |
483 | | size_in_bits: u64, |
484 | | /// The bit offset of this piece. |
485 | | bit_offset: u64, |
486 | | }, |
487 | | /// This represents a parameter that was optimized out. |
488 | | /// |
489 | | /// The entry is the definition of the parameter, and is matched to |
490 | | /// the `DW_TAG_GNU_call_site_parameter` in the caller that also |
491 | | /// points to the same definition of the parameter. |
492 | | /// |
493 | | /// Represents `DW_OP_GNU_parameter_ref`. |
494 | | ParameterRef(UnitEntryId), |
495 | | /// The index of a local in the currently executing function. |
496 | | /// |
497 | | /// Represents `DW_OP_WASM_location 0x00`. |
498 | | WasmLocal(u32), |
499 | | /// The index of a global. |
500 | | /// |
501 | | /// Represents `DW_OP_WASM_location 0x01`. |
502 | | WasmGlobal(u32), |
503 | | /// The index of an item on the operand stack. |
504 | | /// |
505 | | /// Represents `DW_OP_WASM_location 0x02`. |
506 | | WasmStack(u32), |
507 | | } |
508 | | |
509 | | impl Operation { |
510 | 0 | fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> Result<usize> { |
511 | 0 | let base_size = |entry| match unit_offsets { |
512 | 0 | Some(offsets) => offsets |
513 | 0 | .unit_offset(entry) |
514 | 0 | .map(uleb128_size) |
515 | 0 | .ok_or(Error::UnsupportedExpressionForwardReference), |
516 | 0 | None => Err(Error::UnsupportedCfiExpressionReference), |
517 | 0 | }; |
518 | 0 | Ok(1 + match *self { |
519 | 0 | Operation::Raw(ref bytecode) => return Ok(bytecode.len()), |
520 | 0 | Operation::Simple(_) => 0, |
521 | 0 | Operation::Address(_) => encoding.address_size as usize, |
522 | 0 | Operation::UnsignedConstant(value) => { |
523 | 0 | if value < 32 { |
524 | 0 | 0 |
525 | | } else { |
526 | 0 | uleb128_size(value) |
527 | | } |
528 | | } |
529 | 0 | Operation::SignedConstant(value) => sleb128_size(value), |
530 | 0 | Operation::ConstantType(base, ref value) => base_size(base)? + 1 + value.len(), |
531 | 0 | Operation::FrameOffset(offset) => sleb128_size(offset), |
532 | 0 | Operation::RegisterOffset(register, offset) => { |
533 | 0 | if register.0 < 32 { |
534 | 0 | sleb128_size(offset) |
535 | | } else { |
536 | 0 | uleb128_size(register.0.into()) + sleb128_size(offset) |
537 | | } |
538 | | } |
539 | 0 | Operation::RegisterType(register, base) => { |
540 | 0 | uleb128_size(register.0.into()) + base_size(base)? |
541 | | } |
542 | 0 | Operation::Pick(index) => { |
543 | 0 | if index > 1 { |
544 | 0 | 1 |
545 | | } else { |
546 | 0 | 0 |
547 | | } |
548 | | } |
549 | 0 | Operation::Deref { .. } => 0, |
550 | 0 | Operation::DerefSize { .. } => 1, |
551 | 0 | Operation::DerefType { base, .. } => 1 + base_size(base)?, |
552 | 0 | Operation::PlusConstant(value) => uleb128_size(value), |
553 | 0 | Operation::Skip(_) => 2, |
554 | 0 | Operation::Branch(_) => 2, |
555 | 0 | Operation::Call(_) => 4, |
556 | 0 | Operation::CallRef(_) => encoding.format.word_size() as usize, |
557 | 0 | Operation::VariableValue(_) => encoding.format.word_size() as usize, |
558 | 0 | Operation::Convert(base) => match base { |
559 | 0 | Some(base) => base_size(base)?, |
560 | 0 | None => 1, |
561 | | }, |
562 | 0 | Operation::Reinterpret(base) => match base { |
563 | 0 | Some(base) => base_size(base)?, |
564 | 0 | None => 1, |
565 | | }, |
566 | 0 | Operation::EntryValue(ref expression) => { |
567 | 0 | let length = expression.size(encoding, unit_offsets)?; |
568 | 0 | uleb128_size(length as u64) + length |
569 | | } |
570 | 0 | Operation::Register(register) => { |
571 | 0 | if register.0 < 32 { |
572 | 0 | 0 |
573 | | } else { |
574 | 0 | uleb128_size(register.0.into()) |
575 | | } |
576 | | } |
577 | 0 | Operation::ImplicitValue(ref data) => uleb128_size(data.len() as u64) + data.len(), |
578 | 0 | Operation::ImplicitPointer { byte_offset, .. } => { |
579 | 0 | let size = if encoding.version == 2 { |
580 | 0 | encoding.address_size |
581 | | } else { |
582 | 0 | encoding.format.word_size() |
583 | | }; |
584 | 0 | size as usize + sleb128_size(byte_offset) |
585 | | } |
586 | 0 | Operation::Piece { size_in_bytes } => uleb128_size(size_in_bytes), |
587 | | Operation::BitPiece { |
588 | 0 | size_in_bits, |
589 | 0 | bit_offset, |
590 | 0 | } => uleb128_size(size_in_bits) + uleb128_size(bit_offset), |
591 | 0 | Operation::ParameterRef(_) => 4, |
592 | 0 | Operation::WasmLocal(index) |
593 | 0 | | Operation::WasmGlobal(index) |
594 | 0 | | Operation::WasmStack(index) => 1 + uleb128_size(index.into()), |
595 | | }) |
596 | 0 | } |
597 | | |
598 | | pub(crate) fn write<W: Writer>( |
599 | | &self, |
600 | | w: &mut W, |
601 | | refs: Option<&mut Vec<DebugInfoFixup>>, |
602 | | encoding: Encoding, |
603 | | unit_offsets: Option<&UnitOffsets>, |
604 | | offsets: &[usize], |
605 | | ) -> Result<()> { |
606 | | let entry_offset = |entry| match unit_offsets { |
607 | | Some(offsets) => offsets |
608 | | .unit_offset(entry) |
609 | | .ok_or(Error::UnsupportedExpressionForwardReference), |
610 | | None => Err(Error::UnsupportedCfiExpressionReference), |
611 | | }; |
612 | | match *self { |
613 | | Operation::Raw(ref bytecode) => w.write(bytecode)?, |
614 | | Operation::Simple(opcode) => w.write_u8(opcode.0)?, |
615 | | Operation::Address(address) => { |
616 | | w.write_u8(constants::DW_OP_addr.0)?; |
617 | | w.write_address(address, encoding.address_size)?; |
618 | | } |
619 | | Operation::UnsignedConstant(value) => { |
620 | | if value < 32 { |
621 | | w.write_u8(constants::DW_OP_lit0.0 + value as u8)?; |
622 | | } else { |
623 | | w.write_u8(constants::DW_OP_constu.0)?; |
624 | | w.write_uleb128(value)?; |
625 | | } |
626 | | } |
627 | | Operation::SignedConstant(value) => { |
628 | | w.write_u8(constants::DW_OP_consts.0)?; |
629 | | w.write_sleb128(value)?; |
630 | | } |
631 | | Operation::ConstantType(base, ref value) => { |
632 | | if encoding.version >= 5 { |
633 | | w.write_u8(constants::DW_OP_const_type.0)?; |
634 | | } else { |
635 | | w.write_u8(constants::DW_OP_GNU_const_type.0)?; |
636 | | } |
637 | | w.write_uleb128(entry_offset(base)?)?; |
638 | | w.write_udata(value.len() as u64, 1)?; |
639 | | w.write(value)?; |
640 | | } |
641 | | Operation::FrameOffset(offset) => { |
642 | | w.write_u8(constants::DW_OP_fbreg.0)?; |
643 | | w.write_sleb128(offset)?; |
644 | | } |
645 | | Operation::RegisterOffset(register, offset) => { |
646 | | if register.0 < 32 { |
647 | | w.write_u8(constants::DW_OP_breg0.0 + register.0 as u8)?; |
648 | | } else { |
649 | | w.write_u8(constants::DW_OP_bregx.0)?; |
650 | | w.write_uleb128(register.0.into())?; |
651 | | } |
652 | | w.write_sleb128(offset)?; |
653 | | } |
654 | | Operation::RegisterType(register, base) => { |
655 | | if encoding.version >= 5 { |
656 | | w.write_u8(constants::DW_OP_regval_type.0)?; |
657 | | } else { |
658 | | w.write_u8(constants::DW_OP_GNU_regval_type.0)?; |
659 | | } |
660 | | w.write_uleb128(register.0.into())?; |
661 | | w.write_uleb128(entry_offset(base)?)?; |
662 | | } |
663 | | Operation::Pick(index) => match index { |
664 | | 0 => w.write_u8(constants::DW_OP_dup.0)?, |
665 | | 1 => w.write_u8(constants::DW_OP_over.0)?, |
666 | | _ => { |
667 | | w.write_u8(constants::DW_OP_pick.0)?; |
668 | | w.write_u8(index)?; |
669 | | } |
670 | | }, |
671 | | Operation::Deref { space } => { |
672 | | if space { |
673 | | w.write_u8(constants::DW_OP_xderef.0)?; |
674 | | } else { |
675 | | w.write_u8(constants::DW_OP_deref.0)?; |
676 | | } |
677 | | } |
678 | | Operation::DerefSize { space, size } => { |
679 | | if space { |
680 | | w.write_u8(constants::DW_OP_xderef_size.0)?; |
681 | | } else { |
682 | | w.write_u8(constants::DW_OP_deref_size.0)?; |
683 | | } |
684 | | w.write_u8(size)?; |
685 | | } |
686 | | Operation::DerefType { space, size, base } => { |
687 | | if space { |
688 | | w.write_u8(constants::DW_OP_xderef_type.0)?; |
689 | | } else { |
690 | | if encoding.version >= 5 { |
691 | | w.write_u8(constants::DW_OP_deref_type.0)?; |
692 | | } else { |
693 | | w.write_u8(constants::DW_OP_GNU_deref_type.0)?; |
694 | | } |
695 | | } |
696 | | w.write_u8(size)?; |
697 | | w.write_uleb128(entry_offset(base)?)?; |
698 | | } |
699 | | Operation::PlusConstant(value) => { |
700 | | w.write_u8(constants::DW_OP_plus_uconst.0)?; |
701 | | w.write_uleb128(value)?; |
702 | | } |
703 | | Operation::Skip(target) => { |
704 | | w.write_u8(constants::DW_OP_skip.0)?; |
705 | | let offset = offsets[target] as i64 - (w.len() as i64 + 2); |
706 | | w.write_sdata(offset, 2)?; |
707 | | } |
708 | | Operation::Branch(target) => { |
709 | | w.write_u8(constants::DW_OP_bra.0)?; |
710 | | let offset = offsets[target] as i64 - (w.len() as i64 + 2); |
711 | | w.write_sdata(offset, 2)?; |
712 | | } |
713 | | Operation::Call(entry) => { |
714 | | w.write_u8(constants::DW_OP_call4.0)?; |
715 | | // TODO: this probably won't work in practice, because we may |
716 | | // only know the offsets of base type DIEs at this point. |
717 | | w.write_udata(entry_offset(entry)?, 4)?; |
718 | | } |
719 | | Operation::CallRef(entry) => { |
720 | | w.write_u8(constants::DW_OP_call_ref.0)?; |
721 | | let size = encoding.format.word_size(); |
722 | | match entry { |
723 | | DebugInfoRef::Symbol(symbol) => w.write_reference(symbol, size)?, |
724 | | DebugInfoRef::Entry(unit, entry) => { |
725 | | let refs = refs.ok_or(Error::InvalidReference)?; |
726 | | refs.push(DebugInfoFixup { |
727 | | offset: w.len(), |
728 | | unit, |
729 | | entry, |
730 | | size, |
731 | | }); |
732 | | w.write_udata(0, size)?; |
733 | | } |
734 | | } |
735 | | } |
736 | | Operation::VariableValue(entry) => { |
737 | | w.write_u8(constants::DW_OP_GNU_variable_value.0)?; |
738 | | let size = encoding.format.word_size(); |
739 | | match entry { |
740 | | DebugInfoRef::Symbol(symbol) => w.write_reference(symbol, size)?, |
741 | | DebugInfoRef::Entry(unit, entry) => { |
742 | | let refs = refs.ok_or(Error::InvalidReference)?; |
743 | | refs.push(DebugInfoFixup { |
744 | | offset: w.len(), |
745 | | unit, |
746 | | entry, |
747 | | size, |
748 | | }); |
749 | | w.write_udata(0, size)?; |
750 | | } |
751 | | } |
752 | | } |
753 | | Operation::Convert(base) => { |
754 | | if encoding.version >= 5 { |
755 | | w.write_u8(constants::DW_OP_convert.0)?; |
756 | | } else { |
757 | | w.write_u8(constants::DW_OP_GNU_convert.0)?; |
758 | | } |
759 | | match base { |
760 | | Some(base) => w.write_uleb128(entry_offset(base)?)?, |
761 | | None => w.write_u8(0)?, |
762 | | } |
763 | | } |
764 | | Operation::Reinterpret(base) => { |
765 | | if encoding.version >= 5 { |
766 | | w.write_u8(constants::DW_OP_reinterpret.0)?; |
767 | | } else { |
768 | | w.write_u8(constants::DW_OP_GNU_reinterpret.0)?; |
769 | | } |
770 | | match base { |
771 | | Some(base) => w.write_uleb128(entry_offset(base)?)?, |
772 | | None => w.write_u8(0)?, |
773 | | } |
774 | | } |
775 | | Operation::EntryValue(ref expression) => { |
776 | | if encoding.version >= 5 { |
777 | | w.write_u8(constants::DW_OP_entry_value.0)?; |
778 | | } else { |
779 | | w.write_u8(constants::DW_OP_GNU_entry_value.0)?; |
780 | | } |
781 | | let length = expression.size(encoding, unit_offsets)?; |
782 | | w.write_uleb128(length as u64)?; |
783 | | expression.write(w, refs, encoding, unit_offsets)?; |
784 | | } |
785 | | Operation::Register(register) => { |
786 | | if register.0 < 32 { |
787 | | w.write_u8(constants::DW_OP_reg0.0 + register.0 as u8)?; |
788 | | } else { |
789 | | w.write_u8(constants::DW_OP_regx.0)?; |
790 | | w.write_uleb128(register.0.into())?; |
791 | | } |
792 | | } |
793 | | Operation::ImplicitValue(ref data) => { |
794 | | w.write_u8(constants::DW_OP_implicit_value.0)?; |
795 | | w.write_uleb128(data.len() as u64)?; |
796 | | w.write(data)?; |
797 | | } |
798 | | Operation::ImplicitPointer { entry, byte_offset } => { |
799 | | if encoding.version >= 5 { |
800 | | w.write_u8(constants::DW_OP_implicit_pointer.0)?; |
801 | | } else { |
802 | | w.write_u8(constants::DW_OP_GNU_implicit_pointer.0)?; |
803 | | } |
804 | | let size = if encoding.version == 2 { |
805 | | encoding.address_size |
806 | | } else { |
807 | | encoding.format.word_size() |
808 | | }; |
809 | | match entry { |
810 | | DebugInfoRef::Symbol(symbol) => { |
811 | | w.write_reference(symbol, size)?; |
812 | | } |
813 | | DebugInfoRef::Entry(unit, entry) => { |
814 | | let refs = refs.ok_or(Error::InvalidReference)?; |
815 | | refs.push(DebugInfoFixup { |
816 | | offset: w.len(), |
817 | | unit, |
818 | | entry, |
819 | | size, |
820 | | }); |
821 | | w.write_udata(0, size)?; |
822 | | } |
823 | | } |
824 | | w.write_sleb128(byte_offset)?; |
825 | | } |
826 | | Operation::Piece { size_in_bytes } => { |
827 | | w.write_u8(constants::DW_OP_piece.0)?; |
828 | | w.write_uleb128(size_in_bytes)?; |
829 | | } |
830 | | Operation::BitPiece { |
831 | | size_in_bits, |
832 | | bit_offset, |
833 | | } => { |
834 | | w.write_u8(constants::DW_OP_bit_piece.0)?; |
835 | | w.write_uleb128(size_in_bits)?; |
836 | | w.write_uleb128(bit_offset)?; |
837 | | } |
838 | | Operation::ParameterRef(entry) => { |
839 | | w.write_u8(constants::DW_OP_GNU_parameter_ref.0)?; |
840 | | w.write_udata(entry_offset(entry)?, 4)?; |
841 | | } |
842 | | Operation::WasmLocal(index) => { |
843 | | w.write(&[constants::DW_OP_WASM_location.0, 0])?; |
844 | | w.write_uleb128(index.into())?; |
845 | | } |
846 | | Operation::WasmGlobal(index) => { |
847 | | w.write(&[constants::DW_OP_WASM_location.0, 1])?; |
848 | | w.write_uleb128(index.into())?; |
849 | | } |
850 | | Operation::WasmStack(index) => { |
851 | | w.write(&[constants::DW_OP_WASM_location.0, 2])?; |
852 | | w.write_uleb128(index.into())?; |
853 | | } |
854 | | } |
855 | | Ok(()) |
856 | | } |
857 | | } |
858 | | |
859 | | #[cfg(feature = "read")] |
860 | | pub(crate) mod convert { |
861 | | use super::*; |
862 | | use crate::read::{self, Reader}; |
863 | | use crate::write::{ConvertDebugInfoRef, ConvertError, ConvertResult}; |
864 | | |
865 | | impl Expression { |
866 | | /// Create an expression from the input expression. |
867 | | pub(crate) fn from<R: Reader<Offset = usize>>( |
868 | | from_expression: read::Expression<R>, |
869 | | encoding: Encoding, |
870 | | unit: Option<read::UnitRef<'_, R>>, |
871 | | convert_address: &dyn Fn(u64) -> Option<Address>, |
872 | | refs: &dyn ConvertDebugInfoRef, |
873 | | ) -> ConvertResult<Expression> { |
874 | | // Calculate offsets for use in branch/skip operations. |
875 | | let mut offsets = Vec::new(); |
876 | | let mut offset = 0; |
877 | | let mut from_operations = from_expression.clone().operations(encoding); |
878 | | while from_operations.next()?.is_some() { |
879 | | offsets.push(offset); |
880 | | offset = from_operations.offset_from(&from_expression); |
881 | | } |
882 | | offsets.push(from_expression.0.len()); |
883 | | |
884 | | let mut from_operations = from_expression.clone().operations(encoding); |
885 | | let mut operations = Vec::new(); |
886 | | while let Some(from_operation) = from_operations.next()? { |
887 | | let operation = match from_operation { |
888 | | read::Operation::Deref { |
889 | | base_type, |
890 | | size, |
891 | | space, |
892 | | } => { |
893 | | if base_type.0 != 0 { |
894 | | let base = refs.convert_unit_ref(base_type)?; |
895 | | Operation::DerefType { space, size, base } |
896 | | } else if size != encoding.address_size { |
897 | | Operation::DerefSize { space, size } |
898 | | } else { |
899 | | Operation::Deref { space } |
900 | | } |
901 | | } |
902 | | read::Operation::Drop => Operation::Simple(constants::DW_OP_drop), |
903 | | read::Operation::Pick { index } => Operation::Pick(index), |
904 | | read::Operation::Swap => Operation::Simple(constants::DW_OP_swap), |
905 | | read::Operation::Rot => Operation::Simple(constants::DW_OP_rot), |
906 | | read::Operation::Abs => Operation::Simple(constants::DW_OP_abs), |
907 | | read::Operation::And => Operation::Simple(constants::DW_OP_and), |
908 | | read::Operation::Div => Operation::Simple(constants::DW_OP_div), |
909 | | read::Operation::Minus => Operation::Simple(constants::DW_OP_minus), |
910 | | read::Operation::Mod => Operation::Simple(constants::DW_OP_mod), |
911 | | read::Operation::Mul => Operation::Simple(constants::DW_OP_mul), |
912 | | read::Operation::Neg => Operation::Simple(constants::DW_OP_neg), |
913 | | read::Operation::Not => Operation::Simple(constants::DW_OP_not), |
914 | | read::Operation::Or => Operation::Simple(constants::DW_OP_or), |
915 | | read::Operation::Plus => Operation::Simple(constants::DW_OP_plus), |
916 | | read::Operation::PlusConstant { value } => Operation::PlusConstant(value), |
917 | | read::Operation::Shl => Operation::Simple(constants::DW_OP_shl), |
918 | | read::Operation::Shr => Operation::Simple(constants::DW_OP_shr), |
919 | | read::Operation::Shra => Operation::Simple(constants::DW_OP_shra), |
920 | | read::Operation::Xor => Operation::Simple(constants::DW_OP_xor), |
921 | | read::Operation::Eq => Operation::Simple(constants::DW_OP_eq), |
922 | | read::Operation::Ge => Operation::Simple(constants::DW_OP_ge), |
923 | | read::Operation::Gt => Operation::Simple(constants::DW_OP_gt), |
924 | | read::Operation::Le => Operation::Simple(constants::DW_OP_le), |
925 | | read::Operation::Lt => Operation::Simple(constants::DW_OP_lt), |
926 | | read::Operation::Ne => Operation::Simple(constants::DW_OP_ne), |
927 | | read::Operation::Bra { target } => { |
928 | | let offset = from_operations |
929 | | .offset_from(&from_expression) |
930 | | .wrapping_add(i64::from(target) as usize); |
931 | | let index = offsets |
932 | | .binary_search(&offset) |
933 | | .map_err(|_| ConvertError::InvalidBranchTarget)?; |
934 | | Operation::Branch(index) |
935 | | } |
936 | | read::Operation::Skip { target } => { |
937 | | let offset = from_operations |
938 | | .offset_from(&from_expression) |
939 | | .wrapping_add(i64::from(target) as usize); |
940 | | let index = offsets |
941 | | .binary_search(&offset) |
942 | | .map_err(|_| ConvertError::InvalidBranchTarget)?; |
943 | | Operation::Skip(index) |
944 | | } |
945 | | read::Operation::UnsignedConstant { value } => { |
946 | | Operation::UnsignedConstant(value) |
947 | | } |
948 | | read::Operation::SignedConstant { value } => Operation::SignedConstant(value), |
949 | | read::Operation::Register { register } => Operation::Register(register), |
950 | | read::Operation::RegisterOffset { |
951 | | register, |
952 | | offset, |
953 | | base_type, |
954 | | } => { |
955 | | if base_type.0 != 0 { |
956 | | Operation::RegisterType(register, refs.convert_unit_ref(base_type)?) |
957 | | } else { |
958 | | Operation::RegisterOffset(register, offset) |
959 | | } |
960 | | } |
961 | | read::Operation::FrameOffset { offset } => Operation::FrameOffset(offset), |
962 | | read::Operation::Nop => Operation::Simple(constants::DW_OP_nop), |
963 | | read::Operation::PushObjectAddress => { |
964 | | Operation::Simple(constants::DW_OP_push_object_address) |
965 | | } |
966 | | read::Operation::Call { offset } => match offset { |
967 | | read::DieReference::UnitRef(offset) => { |
968 | | Operation::Call(refs.convert_unit_ref(offset)?) |
969 | | } |
970 | | read::DieReference::DebugInfoRef(offset) => { |
971 | | Operation::CallRef(refs.convert_debug_info_ref(offset)?) |
972 | | } |
973 | | }, |
974 | | read::Operation::VariableValue { offset } => { |
975 | | Operation::VariableValue(refs.convert_debug_info_ref(offset)?) |
976 | | } |
977 | | read::Operation::TLS => Operation::Simple(constants::DW_OP_form_tls_address), |
978 | | read::Operation::CallFrameCFA => { |
979 | | Operation::Simple(constants::DW_OP_call_frame_cfa) |
980 | | } |
981 | | read::Operation::Piece { |
982 | | size_in_bits, |
983 | | bit_offset: None, |
984 | | } => Operation::Piece { |
985 | | size_in_bytes: size_in_bits / 8, |
986 | | }, |
987 | | read::Operation::Piece { |
988 | | size_in_bits, |
989 | | bit_offset: Some(bit_offset), |
990 | | } => Operation::BitPiece { |
991 | | size_in_bits, |
992 | | bit_offset, |
993 | | }, |
994 | | read::Operation::ImplicitValue { data } => { |
995 | | Operation::ImplicitValue(data.to_slice()?.into_owned().into()) |
996 | | } |
997 | | read::Operation::StackValue => Operation::Simple(constants::DW_OP_stack_value), |
998 | | read::Operation::ImplicitPointer { value, byte_offset } => { |
999 | | let entry = refs.convert_debug_info_ref(value)?; |
1000 | | Operation::ImplicitPointer { entry, byte_offset } |
1001 | | } |
1002 | | read::Operation::EntryValue { expression } => { |
1003 | | let expression = Expression::from( |
1004 | | read::Expression(expression), |
1005 | | encoding, |
1006 | | unit, |
1007 | | convert_address, |
1008 | | refs, |
1009 | | )?; |
1010 | | Operation::EntryValue(expression) |
1011 | | } |
1012 | | read::Operation::ParameterRef { offset } => { |
1013 | | let entry = refs.convert_unit_ref(offset)?; |
1014 | | Operation::ParameterRef(entry) |
1015 | | } |
1016 | | read::Operation::Address { address } => { |
1017 | | let address = |
1018 | | convert_address(address).ok_or(ConvertError::InvalidAddress)?; |
1019 | | Operation::Address(address) |
1020 | | } |
1021 | | read::Operation::AddressIndex { index } => { |
1022 | | let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; |
1023 | | let val = unit.address(index)?; |
1024 | | let address = convert_address(val).ok_or(ConvertError::InvalidAddress)?; |
1025 | | Operation::Address(address) |
1026 | | } |
1027 | | read::Operation::ConstantIndex { index } => { |
1028 | | let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; |
1029 | | let val = unit.address(index)?; |
1030 | | Operation::UnsignedConstant(val) |
1031 | | } |
1032 | | read::Operation::TypedLiteral { base_type, value } => { |
1033 | | let entry = refs.convert_unit_ref(base_type)?; |
1034 | | Operation::ConstantType(entry, value.to_slice()?.into_owned().into()) |
1035 | | } |
1036 | | read::Operation::Convert { base_type } => { |
1037 | | if base_type.0 == 0 { |
1038 | | Operation::Convert(None) |
1039 | | } else { |
1040 | | let entry = refs.convert_unit_ref(base_type)?; |
1041 | | Operation::Convert(Some(entry)) |
1042 | | } |
1043 | | } |
1044 | | read::Operation::Reinterpret { base_type } => { |
1045 | | if base_type.0 == 0 { |
1046 | | Operation::Reinterpret(None) |
1047 | | } else { |
1048 | | let entry = refs.convert_unit_ref(base_type)?; |
1049 | | Operation::Reinterpret(Some(entry)) |
1050 | | } |
1051 | | } |
1052 | | read::Operation::Uninitialized => { |
1053 | | Operation::Simple(constants::DW_OP_GNU_uninit) |
1054 | | } |
1055 | | read::Operation::WasmLocal { index } => Operation::WasmLocal(index), |
1056 | | read::Operation::WasmGlobal { index } => Operation::WasmGlobal(index), |
1057 | | read::Operation::WasmStack { index } => Operation::WasmStack(index), |
1058 | | }; |
1059 | | operations.push(operation); |
1060 | | } |
1061 | | Ok(Expression { operations }) |
1062 | | } |
1063 | | } |
1064 | | } |
1065 | | |
1066 | | #[cfg(test)] |
1067 | | #[cfg(feature = "read")] |
1068 | | mod tests { |
1069 | | use super::*; |
1070 | | use crate::LittleEndian; |
1071 | | use crate::common::{DebugInfoOffset, Format}; |
1072 | | use crate::read; |
1073 | | use crate::write::{AttributeValue, Dwarf, EndianVec, LineProgram, Sections, Unit}; |
1074 | | |
1075 | | #[test] |
1076 | | #[allow(clippy::type_complexity)] |
1077 | | fn test_operation() { |
1078 | | for version in [2, 3, 4, 5] { |
1079 | | for address_size in [4, 8] { |
1080 | | for format in [Format::Dwarf32, Format::Dwarf64] { |
1081 | | let encoding = Encoding { |
1082 | | format, |
1083 | | version, |
1084 | | address_size, |
1085 | | }; |
1086 | | |
1087 | | let mut dwarf = Dwarf::new(); |
1088 | | let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); |
1089 | | let unit = dwarf.units.get_mut(unit_id); |
1090 | | |
1091 | | // Create an entry that can be referenced by the expression. |
1092 | | let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type); |
1093 | | let reference = DebugInfoRef::Entry(unit_id, entry_id); |
1094 | | |
1095 | | // The offsets for the above entry when reading back the expression. |
1096 | | struct ReadState { |
1097 | | debug_info_offset: DebugInfoOffset, |
1098 | | entry_offset: read::UnitOffset, |
1099 | | } |
1100 | | |
1101 | | let mut reg_expression = Expression::new(); |
1102 | | reg_expression.op_reg(Register(23)); |
1103 | | |
1104 | | let operations: &[( |
1105 | | &dyn Fn(&mut Expression), |
1106 | | Operation, |
1107 | | &dyn Fn(&ReadState) -> read::Operation<_>, |
1108 | | )] = &[ |
1109 | | ( |
1110 | | &|x| x.op_deref(), |
1111 | | Operation::Deref { space: false }, |
1112 | | &|_| read::Operation::Deref { |
1113 | | base_type: read::UnitOffset(0), |
1114 | | size: address_size, |
1115 | | space: false, |
1116 | | }, |
1117 | | ), |
1118 | | ( |
1119 | | &|x| x.op_xderef(), |
1120 | | Operation::Deref { space: true }, |
1121 | | &|_| read::Operation::Deref { |
1122 | | base_type: read::UnitOffset(0), |
1123 | | size: address_size, |
1124 | | space: true, |
1125 | | }, |
1126 | | ), |
1127 | | ( |
1128 | | &|x| x.op_deref_size(2), |
1129 | | Operation::DerefSize { |
1130 | | space: false, |
1131 | | size: 2, |
1132 | | }, |
1133 | | &|_| read::Operation::Deref { |
1134 | | base_type: read::UnitOffset(0), |
1135 | | size: 2, |
1136 | | space: false, |
1137 | | }, |
1138 | | ), |
1139 | | ( |
1140 | | &|x| x.op_xderef_size(2), |
1141 | | Operation::DerefSize { |
1142 | | space: true, |
1143 | | size: 2, |
1144 | | }, |
1145 | | &|_| read::Operation::Deref { |
1146 | | base_type: read::UnitOffset(0), |
1147 | | size: 2, |
1148 | | space: true, |
1149 | | }, |
1150 | | ), |
1151 | | ( |
1152 | | &|x| x.op_deref_type(2, entry_id), |
1153 | | Operation::DerefType { |
1154 | | space: false, |
1155 | | size: 2, |
1156 | | base: entry_id, |
1157 | | }, |
1158 | | &|x| read::Operation::Deref { |
1159 | | base_type: x.entry_offset, |
1160 | | size: 2, |
1161 | | space: false, |
1162 | | }, |
1163 | | ), |
1164 | | ( |
1165 | | &|x| x.op_xderef_type(2, entry_id), |
1166 | | Operation::DerefType { |
1167 | | space: true, |
1168 | | size: 2, |
1169 | | base: entry_id, |
1170 | | }, |
1171 | | &|x| read::Operation::Deref { |
1172 | | base_type: x.entry_offset, |
1173 | | size: 2, |
1174 | | space: true, |
1175 | | }, |
1176 | | ), |
1177 | | ( |
1178 | | &|x| x.op(constants::DW_OP_drop), |
1179 | | Operation::Simple(constants::DW_OP_drop), |
1180 | | &|_| read::Operation::Drop, |
1181 | | ), |
1182 | | (&|x| x.op_pick(0), Operation::Pick(0), &|_| { |
1183 | | read::Operation::Pick { index: 0 } |
1184 | | }), |
1185 | | (&|x| x.op_pick(1), Operation::Pick(1), &|_| { |
1186 | | read::Operation::Pick { index: 1 } |
1187 | | }), |
1188 | | (&|x| x.op_pick(2), Operation::Pick(2), &|_| { |
1189 | | read::Operation::Pick { index: 2 } |
1190 | | }), |
1191 | | ( |
1192 | | &|x| x.op(constants::DW_OP_swap), |
1193 | | Operation::Simple(constants::DW_OP_swap), |
1194 | | &|_| read::Operation::Swap, |
1195 | | ), |
1196 | | ( |
1197 | | &|x| x.op(constants::DW_OP_rot), |
1198 | | Operation::Simple(constants::DW_OP_rot), |
1199 | | &|_| read::Operation::Rot, |
1200 | | ), |
1201 | | ( |
1202 | | &|x| x.op(constants::DW_OP_abs), |
1203 | | Operation::Simple(constants::DW_OP_abs), |
1204 | | &|_| read::Operation::Abs, |
1205 | | ), |
1206 | | ( |
1207 | | &|x| x.op(constants::DW_OP_and), |
1208 | | Operation::Simple(constants::DW_OP_and), |
1209 | | &|_| read::Operation::And, |
1210 | | ), |
1211 | | ( |
1212 | | &|x| x.op(constants::DW_OP_div), |
1213 | | Operation::Simple(constants::DW_OP_div), |
1214 | | &|_| read::Operation::Div, |
1215 | | ), |
1216 | | ( |
1217 | | &|x| x.op(constants::DW_OP_minus), |
1218 | | Operation::Simple(constants::DW_OP_minus), |
1219 | | &|_| read::Operation::Minus, |
1220 | | ), |
1221 | | ( |
1222 | | &|x| x.op(constants::DW_OP_mod), |
1223 | | Operation::Simple(constants::DW_OP_mod), |
1224 | | &|_| read::Operation::Mod, |
1225 | | ), |
1226 | | ( |
1227 | | &|x| x.op(constants::DW_OP_mul), |
1228 | | Operation::Simple(constants::DW_OP_mul), |
1229 | | &|_| read::Operation::Mul, |
1230 | | ), |
1231 | | ( |
1232 | | &|x| x.op(constants::DW_OP_neg), |
1233 | | Operation::Simple(constants::DW_OP_neg), |
1234 | | &|_| read::Operation::Neg, |
1235 | | ), |
1236 | | ( |
1237 | | &|x| x.op(constants::DW_OP_not), |
1238 | | Operation::Simple(constants::DW_OP_not), |
1239 | | &|_| read::Operation::Not, |
1240 | | ), |
1241 | | ( |
1242 | | &|x| x.op(constants::DW_OP_or), |
1243 | | Operation::Simple(constants::DW_OP_or), |
1244 | | &|_| read::Operation::Or, |
1245 | | ), |
1246 | | ( |
1247 | | &|x| x.op(constants::DW_OP_plus), |
1248 | | Operation::Simple(constants::DW_OP_plus), |
1249 | | &|_| read::Operation::Plus, |
1250 | | ), |
1251 | | ( |
1252 | | &|x| x.op_plus_uconst(23), |
1253 | | Operation::PlusConstant(23), |
1254 | | &|_| read::Operation::PlusConstant { value: 23 }, |
1255 | | ), |
1256 | | ( |
1257 | | &|x| x.op(constants::DW_OP_shl), |
1258 | | Operation::Simple(constants::DW_OP_shl), |
1259 | | &|_| read::Operation::Shl, |
1260 | | ), |
1261 | | ( |
1262 | | &|x| x.op(constants::DW_OP_shr), |
1263 | | Operation::Simple(constants::DW_OP_shr), |
1264 | | &|_| read::Operation::Shr, |
1265 | | ), |
1266 | | ( |
1267 | | &|x| x.op(constants::DW_OP_shra), |
1268 | | Operation::Simple(constants::DW_OP_shra), |
1269 | | &|_| read::Operation::Shra, |
1270 | | ), |
1271 | | ( |
1272 | | &|x| x.op(constants::DW_OP_xor), |
1273 | | Operation::Simple(constants::DW_OP_xor), |
1274 | | &|_| read::Operation::Xor, |
1275 | | ), |
1276 | | ( |
1277 | | &|x| x.op(constants::DW_OP_eq), |
1278 | | Operation::Simple(constants::DW_OP_eq), |
1279 | | &|_| read::Operation::Eq, |
1280 | | ), |
1281 | | ( |
1282 | | &|x| x.op(constants::DW_OP_ge), |
1283 | | Operation::Simple(constants::DW_OP_ge), |
1284 | | &|_| read::Operation::Ge, |
1285 | | ), |
1286 | | ( |
1287 | | &|x| x.op(constants::DW_OP_gt), |
1288 | | Operation::Simple(constants::DW_OP_gt), |
1289 | | &|_| read::Operation::Gt, |
1290 | | ), |
1291 | | ( |
1292 | | &|x| x.op(constants::DW_OP_le), |
1293 | | Operation::Simple(constants::DW_OP_le), |
1294 | | &|_| read::Operation::Le, |
1295 | | ), |
1296 | | ( |
1297 | | &|x| x.op(constants::DW_OP_lt), |
1298 | | Operation::Simple(constants::DW_OP_lt), |
1299 | | &|_| read::Operation::Lt, |
1300 | | ), |
1301 | | ( |
1302 | | &|x| x.op(constants::DW_OP_ne), |
1303 | | Operation::Simple(constants::DW_OP_ne), |
1304 | | &|_| read::Operation::Ne, |
1305 | | ), |
1306 | | ( |
1307 | | &|x| x.op_constu(23), |
1308 | | Operation::UnsignedConstant(23), |
1309 | | &|_| read::Operation::UnsignedConstant { value: 23 }, |
1310 | | ), |
1311 | | ( |
1312 | | &|x| x.op_consts(-23), |
1313 | | Operation::SignedConstant(-23), |
1314 | | &|_| read::Operation::SignedConstant { value: -23 }, |
1315 | | ), |
1316 | | ( |
1317 | | &|x| x.op_reg(Register(23)), |
1318 | | Operation::Register(Register(23)), |
1319 | | &|_| read::Operation::Register { |
1320 | | register: Register(23), |
1321 | | }, |
1322 | | ), |
1323 | | ( |
1324 | | &|x| x.op_reg(Register(123)), |
1325 | | Operation::Register(Register(123)), |
1326 | | &|_| read::Operation::Register { |
1327 | | register: Register(123), |
1328 | | }, |
1329 | | ), |
1330 | | ( |
1331 | | &|x| x.op_breg(Register(23), 34), |
1332 | | Operation::RegisterOffset(Register(23), 34), |
1333 | | &|_| read::Operation::RegisterOffset { |
1334 | | register: Register(23), |
1335 | | offset: 34, |
1336 | | base_type: read::UnitOffset(0), |
1337 | | }, |
1338 | | ), |
1339 | | ( |
1340 | | &|x| x.op_breg(Register(123), 34), |
1341 | | Operation::RegisterOffset(Register(123), 34), |
1342 | | &|_| read::Operation::RegisterOffset { |
1343 | | register: Register(123), |
1344 | | offset: 34, |
1345 | | base_type: read::UnitOffset(0), |
1346 | | }, |
1347 | | ), |
1348 | | ( |
1349 | | &|x| x.op_regval_type(Register(23), entry_id), |
1350 | | Operation::RegisterType(Register(23), entry_id), |
1351 | | &|x| read::Operation::RegisterOffset { |
1352 | | register: Register(23), |
1353 | | offset: 0, |
1354 | | base_type: x.entry_offset, |
1355 | | }, |
1356 | | ), |
1357 | | (&|x| x.op_fbreg(34), Operation::FrameOffset(34), &|_| { |
1358 | | read::Operation::FrameOffset { offset: 34 } |
1359 | | }), |
1360 | | ( |
1361 | | &|x| x.op(constants::DW_OP_nop), |
1362 | | Operation::Simple(constants::DW_OP_nop), |
1363 | | &|_| read::Operation::Nop, |
1364 | | ), |
1365 | | ( |
1366 | | &|x| x.op(constants::DW_OP_push_object_address), |
1367 | | Operation::Simple(constants::DW_OP_push_object_address), |
1368 | | &|_| read::Operation::PushObjectAddress, |
1369 | | ), |
1370 | | (&|x| x.op_call(entry_id), Operation::Call(entry_id), &|x| { |
1371 | | read::Operation::Call { |
1372 | | offset: read::DieReference::UnitRef(x.entry_offset), |
1373 | | } |
1374 | | }), |
1375 | | ( |
1376 | | &|x| x.op_call_ref(reference), |
1377 | | Operation::CallRef(reference), |
1378 | | &|x| read::Operation::Call { |
1379 | | offset: read::DieReference::DebugInfoRef(x.debug_info_offset), |
1380 | | }, |
1381 | | ), |
1382 | | ( |
1383 | | &|x| x.op(constants::DW_OP_form_tls_address), |
1384 | | Operation::Simple(constants::DW_OP_form_tls_address), |
1385 | | &|_| read::Operation::TLS, |
1386 | | ), |
1387 | | ( |
1388 | | &|x| x.op(constants::DW_OP_call_frame_cfa), |
1389 | | Operation::Simple(constants::DW_OP_call_frame_cfa), |
1390 | | &|_| read::Operation::CallFrameCFA, |
1391 | | ), |
1392 | | ( |
1393 | | &|x| x.op_piece(23), |
1394 | | Operation::Piece { size_in_bytes: 23 }, |
1395 | | &|_| read::Operation::Piece { |
1396 | | size_in_bits: 23 * 8, |
1397 | | bit_offset: None, |
1398 | | }, |
1399 | | ), |
1400 | | ( |
1401 | | &|x| x.op_bit_piece(23, 34), |
1402 | | Operation::BitPiece { |
1403 | | size_in_bits: 23, |
1404 | | bit_offset: 34, |
1405 | | }, |
1406 | | &|_| read::Operation::Piece { |
1407 | | size_in_bits: 23, |
1408 | | bit_offset: Some(34), |
1409 | | }, |
1410 | | ), |
1411 | | ( |
1412 | | &|x| x.op_implicit_value(vec![23].into()), |
1413 | | Operation::ImplicitValue(vec![23].into()), |
1414 | | &|_| read::Operation::ImplicitValue { |
1415 | | data: read::EndianSlice::new(&[23], LittleEndian), |
1416 | | }, |
1417 | | ), |
1418 | | ( |
1419 | | &|x| x.op(constants::DW_OP_stack_value), |
1420 | | Operation::Simple(constants::DW_OP_stack_value), |
1421 | | &|_| read::Operation::StackValue, |
1422 | | ), |
1423 | | ( |
1424 | | &|x| x.op_implicit_pointer(reference, 23), |
1425 | | Operation::ImplicitPointer { |
1426 | | entry: reference, |
1427 | | byte_offset: 23, |
1428 | | }, |
1429 | | &|x| read::Operation::ImplicitPointer { |
1430 | | value: x.debug_info_offset, |
1431 | | byte_offset: 23, |
1432 | | }, |
1433 | | ), |
1434 | | ( |
1435 | | &|x| x.op_entry_value(reg_expression.clone()), |
1436 | | Operation::EntryValue(reg_expression.clone()), |
1437 | | &|_| read::Operation::EntryValue { |
1438 | | expression: read::EndianSlice::new( |
1439 | | &[constants::DW_OP_reg23.0], |
1440 | | LittleEndian, |
1441 | | ), |
1442 | | }, |
1443 | | ), |
1444 | | ( |
1445 | | &|x| x.op_gnu_parameter_ref(entry_id), |
1446 | | Operation::ParameterRef(entry_id), |
1447 | | &|x| read::Operation::ParameterRef { |
1448 | | offset: x.entry_offset, |
1449 | | }, |
1450 | | ), |
1451 | | ( |
1452 | | &|x| x.op_addr(Address::Constant(23)), |
1453 | | Operation::Address(Address::Constant(23)), |
1454 | | &|_| read::Operation::Address { address: 23 }, |
1455 | | ), |
1456 | | ( |
1457 | | &|x| x.op_const_type(entry_id, vec![23].into()), |
1458 | | Operation::ConstantType(entry_id, vec![23].into()), |
1459 | | &|x| read::Operation::TypedLiteral { |
1460 | | base_type: x.entry_offset, |
1461 | | value: read::EndianSlice::new(&[23], LittleEndian), |
1462 | | }, |
1463 | | ), |
1464 | | (&|x| x.op_convert(None), Operation::Convert(None), &|_| { |
1465 | | read::Operation::Convert { |
1466 | | base_type: read::UnitOffset(0), |
1467 | | } |
1468 | | }), |
1469 | | ( |
1470 | | &|x| x.op_convert(Some(entry_id)), |
1471 | | Operation::Convert(Some(entry_id)), |
1472 | | &|x| read::Operation::Convert { |
1473 | | base_type: x.entry_offset, |
1474 | | }, |
1475 | | ), |
1476 | | ( |
1477 | | &|x| x.op_reinterpret(None), |
1478 | | Operation::Reinterpret(None), |
1479 | | &|_| read::Operation::Reinterpret { |
1480 | | base_type: read::UnitOffset(0), |
1481 | | }, |
1482 | | ), |
1483 | | ( |
1484 | | &|x| x.op_reinterpret(Some(entry_id)), |
1485 | | Operation::Reinterpret(Some(entry_id)), |
1486 | | &|x| read::Operation::Reinterpret { |
1487 | | base_type: x.entry_offset, |
1488 | | }, |
1489 | | ), |
1490 | | ( |
1491 | | &|x| x.op_wasm_local(1000), |
1492 | | Operation::WasmLocal(1000), |
1493 | | &|_| read::Operation::WasmLocal { index: 1000 }, |
1494 | | ), |
1495 | | ( |
1496 | | &|x| x.op_wasm_global(1000), |
1497 | | Operation::WasmGlobal(1000), |
1498 | | &|_| read::Operation::WasmGlobal { index: 1000 }, |
1499 | | ), |
1500 | | ( |
1501 | | &|x| x.op_wasm_stack(1000), |
1502 | | Operation::WasmStack(1000), |
1503 | | &|_| read::Operation::WasmStack { index: 1000 }, |
1504 | | ), |
1505 | | ]; |
1506 | | |
1507 | | // Create a single expression containing all operations. |
1508 | | let mut expression = Expression::new(); |
1509 | | let start_index = expression.next_index(); |
1510 | | for (f, o, _) in operations { |
1511 | | f(&mut expression); |
1512 | | assert_eq!(expression.operations.last(), Some(o)); |
1513 | | } |
1514 | | |
1515 | | let bra_index = expression.op_bra(); |
1516 | | let skip_index = expression.op_skip(); |
1517 | | expression.op(constants::DW_OP_nop); |
1518 | | let end_index = expression.next_index(); |
1519 | | expression.set_target(bra_index, start_index); |
1520 | | expression.set_target(skip_index, end_index); |
1521 | | |
1522 | | // Create an entry containing the expression. |
1523 | | let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); |
1524 | | let subprogram = unit.get_mut(subprogram_id); |
1525 | | subprogram.set( |
1526 | | constants::DW_AT_location, |
1527 | | AttributeValue::Exprloc(expression), |
1528 | | ); |
1529 | | |
1530 | | // Write the DWARF, then parse it. |
1531 | | let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
1532 | | dwarf.write(&mut sections).unwrap(); |
1533 | | |
1534 | | let read_dwarf = sections.read(LittleEndian); |
1535 | | let mut read_units = read_dwarf.units(); |
1536 | | let read_unit_header = read_units.next().unwrap().unwrap(); |
1537 | | let read_unit = read_dwarf.unit(read_unit_header).unwrap(); |
1538 | | let mut read_entries = read_unit.entries(); |
1539 | | let read_entry = read_entries.next_dfs().unwrap().unwrap(); |
1540 | | assert_eq!(read_entry.tag(), constants::DW_TAG_compile_unit); |
1541 | | |
1542 | | // Determine the offset of the entry that can be referenced by the expression. |
1543 | | let read_entry = read_entries.next_dfs().unwrap().unwrap(); |
1544 | | assert_eq!(read_entry.tag(), constants::DW_TAG_base_type); |
1545 | | let read_state = ReadState { |
1546 | | debug_info_offset: read_entry |
1547 | | .offset() |
1548 | | .to_debug_info_offset(&read_unit.header) |
1549 | | .unwrap(), |
1550 | | entry_offset: read_entry.offset(), |
1551 | | }; |
1552 | | |
1553 | | // Get the expression. |
1554 | | let read_entry = read_entries.next_dfs().unwrap().unwrap(); |
1555 | | assert_eq!(read_entry.tag(), constants::DW_TAG_subprogram); |
1556 | | let read_attr = read_entry.attr_value(constants::DW_AT_location).unwrap(); |
1557 | | let read_expression = read_attr.exprloc_value().unwrap(); |
1558 | | let mut read_operations = read_expression.operations(encoding); |
1559 | | for (_, _, operation) in operations { |
1560 | | assert_eq!(read_operations.next(), Ok(Some(operation(&read_state)))); |
1561 | | } |
1562 | | |
1563 | | // 4 = DW_OP_skip + i16 + DW_OP_nop |
1564 | | assert_eq!( |
1565 | | read_operations.next(), |
1566 | | Ok(Some(read::Operation::Bra { |
1567 | | target: -(read_expression.0.len() as i16) + 4 |
1568 | | })) |
1569 | | ); |
1570 | | // 1 = DW_OP_nop |
1571 | | assert_eq!( |
1572 | | read_operations.next(), |
1573 | | Ok(Some(read::Operation::Skip { target: 1 })) |
1574 | | ); |
1575 | | assert_eq!(read_operations.next(), Ok(Some(read::Operation::Nop))); |
1576 | | assert_eq!(read_operations.next(), Ok(None)); |
1577 | | |
1578 | | let convert_dwarf = |
1579 | | Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))) |
1580 | | .unwrap(); |
1581 | | let (convert_unit_id, convert_unit) = |
1582 | | convert_dwarf.units.iter().next().unwrap(); |
1583 | | let convert_root = convert_unit.get(convert_unit.root()); |
1584 | | let mut convert_entries = convert_root.children(); |
1585 | | let convert_entry_id = convert_entries.next().unwrap(); |
1586 | | let convert_subprogram = convert_unit.get(*convert_entries.next().unwrap()); |
1587 | | let convert_attr = convert_subprogram.get(constants::DW_AT_location).unwrap(); |
1588 | | let AttributeValue::Exprloc(convert_expression) = convert_attr else { |
1589 | | panic!("unexpected {:?}", convert_attr); |
1590 | | }; |
1591 | | let mut convert_operations = convert_expression.operations.iter(); |
1592 | | for (_, operation, _) in operations { |
1593 | | let mut operation = operation.clone(); |
1594 | | match &mut operation { |
1595 | | Operation::ConstantType(entry, _) |
1596 | | | Operation::RegisterType(_, entry) |
1597 | | | Operation::DerefType { base: entry, .. } |
1598 | | | Operation::Call(entry) |
1599 | | | Operation::Convert(Some(entry)) |
1600 | | | Operation::Reinterpret(Some(entry)) |
1601 | | | Operation::ParameterRef(entry) => { |
1602 | | *entry = *convert_entry_id; |
1603 | | } |
1604 | | Operation::CallRef(entry) |
1605 | | | Operation::ImplicitPointer { entry, .. } => { |
1606 | | *entry = DebugInfoRef::Entry(convert_unit_id, *convert_entry_id); |
1607 | | } |
1608 | | _ => {} |
1609 | | } |
1610 | | assert_eq!(convert_operations.next(), Some(&operation)); |
1611 | | } |
1612 | | assert_eq!( |
1613 | | convert_operations.next(), |
1614 | | Some(&Operation::Branch(start_index)) |
1615 | | ); |
1616 | | assert_eq!(convert_operations.next(), Some(&Operation::Skip(end_index))); |
1617 | | assert_eq!( |
1618 | | convert_operations.next(), |
1619 | | Some(&Operation::Simple(constants::DW_OP_nop)) |
1620 | | ); |
1621 | | } |
1622 | | } |
1623 | | } |
1624 | | } |
1625 | | |
1626 | | #[test] |
1627 | | #[allow(clippy::type_complexity)] |
1628 | | fn test_expression_raw() { |
1629 | | for version in [2, 3, 4, 5] { |
1630 | | for address_size in [4, 8] { |
1631 | | for format in [Format::Dwarf32, Format::Dwarf64] { |
1632 | | let encoding = Encoding { |
1633 | | format, |
1634 | | version, |
1635 | | address_size, |
1636 | | }; |
1637 | | |
1638 | | let mut dwarf = Dwarf::new(); |
1639 | | let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); |
1640 | | let unit = dwarf.units.get_mut(unit_id); |
1641 | | let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); |
1642 | | let subprogram = unit.get_mut(subprogram_id); |
1643 | | let expression = Expression::raw(vec![constants::DW_OP_constu.0, 23]); |
1644 | | subprogram.set( |
1645 | | constants::DW_AT_location, |
1646 | | AttributeValue::Exprloc(expression), |
1647 | | ); |
1648 | | |
1649 | | // Write the DWARF, then parse it. |
1650 | | let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
1651 | | dwarf.write(&mut sections).unwrap(); |
1652 | | |
1653 | | let read_dwarf = sections.read(LittleEndian); |
1654 | | let mut read_units = read_dwarf.units(); |
1655 | | let read_unit_header = read_units.next().unwrap().unwrap(); |
1656 | | let read_unit = read_dwarf.unit(read_unit_header).unwrap(); |
1657 | | let mut read_entries = read_unit.entries(); |
1658 | | let read_entry = read_entries.next_dfs().unwrap().unwrap(); |
1659 | | assert_eq!(read_entry.tag(), constants::DW_TAG_compile_unit); |
1660 | | let read_entry = read_entries.next_dfs().unwrap().unwrap(); |
1661 | | assert_eq!(read_entry.tag(), constants::DW_TAG_subprogram); |
1662 | | let read_attr = read_entry.attr_value(constants::DW_AT_location).unwrap(); |
1663 | | let read_expression = read_attr.exprloc_value().unwrap(); |
1664 | | let mut read_operations = read_expression.operations(encoding); |
1665 | | assert_eq!( |
1666 | | read_operations.next(), |
1667 | | Ok(Some(read::Operation::UnsignedConstant { value: 23 })) |
1668 | | ); |
1669 | | assert_eq!(read_operations.next(), Ok(None)); |
1670 | | } |
1671 | | } |
1672 | | } |
1673 | | } |
1674 | | |
1675 | | #[test] |
1676 | | fn test_forward_ref() { |
1677 | | let encoding = Encoding { |
1678 | | format: Format::Dwarf32, |
1679 | | version: 5, |
1680 | | address_size: 8, |
1681 | | }; |
1682 | | |
1683 | | let mut dwarf = Dwarf::new(); |
1684 | | let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); |
1685 | | let unit = dwarf.units.get_mut(unit_id); |
1686 | | |
1687 | | // First, create an entry that will contain the expression. |
1688 | | let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); |
1689 | | |
1690 | | // Now create the entry to be referenced by the expression. |
1691 | | let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); |
1692 | | |
1693 | | // Create an expression containing the reference. |
1694 | | let mut expression = Expression::new(); |
1695 | | expression.op_deref_type(2, entry_id); |
1696 | | |
1697 | | // Add the expression to the subprogram. |
1698 | | unit.get_mut(subprogram_id).set( |
1699 | | constants::DW_AT_location, |
1700 | | AttributeValue::Exprloc(expression), |
1701 | | ); |
1702 | | |
1703 | | // Writing the DWARF should fail. |
1704 | | let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
1705 | | assert_eq!( |
1706 | | dwarf.write(&mut sections), |
1707 | | Err(Error::UnsupportedExpressionForwardReference) |
1708 | | ); |
1709 | | } |
1710 | | |
1711 | | #[test] |
1712 | | fn test_missing_unit_ref() { |
1713 | | let encoding = Encoding { |
1714 | | format: Format::Dwarf32, |
1715 | | version: 5, |
1716 | | address_size: 8, |
1717 | | }; |
1718 | | |
1719 | | let mut dwarf = Dwarf::new(); |
1720 | | let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); |
1721 | | let unit = dwarf.units.get_mut(unit_id); |
1722 | | |
1723 | | // Create the entry to be referenced by the expression. |
1724 | | let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); |
1725 | | // And delete it so that it is not available when writing the expression. |
1726 | | unit.get_mut(unit.root()).delete_child(entry_id); |
1727 | | |
1728 | | // Create an expression containing the reference. |
1729 | | let mut expression = Expression::new(); |
1730 | | expression.op_deref_type(2, entry_id); |
1731 | | |
1732 | | // Create an entry containing the expression. |
1733 | | let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); |
1734 | | unit.get_mut(subprogram_id).set( |
1735 | | constants::DW_AT_location, |
1736 | | AttributeValue::Exprloc(expression), |
1737 | | ); |
1738 | | |
1739 | | // Writing the DWARF should fail. |
1740 | | let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
1741 | | assert_eq!( |
1742 | | dwarf.write(&mut sections), |
1743 | | Err(Error::UnsupportedExpressionForwardReference) |
1744 | | ); |
1745 | | } |
1746 | | |
1747 | | #[test] |
1748 | | fn test_missing_debuginfo_ref() { |
1749 | | let encoding = Encoding { |
1750 | | format: Format::Dwarf32, |
1751 | | version: 5, |
1752 | | address_size: 8, |
1753 | | }; |
1754 | | |
1755 | | let mut dwarf = Dwarf::new(); |
1756 | | let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); |
1757 | | let unit = dwarf.units.get_mut(unit_id); |
1758 | | |
1759 | | // Create the entry to be referenced by the expression. |
1760 | | let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); |
1761 | | // And delete it so that it is not available when writing the expression. |
1762 | | unit.get_mut(unit.root()).delete_child(entry_id); |
1763 | | |
1764 | | // Create an expression containing the reference. |
1765 | | let mut expression = Expression::new(); |
1766 | | expression.op_call_ref(DebugInfoRef::Entry(unit_id, entry_id)); |
1767 | | |
1768 | | // Create an entry containing the expression. |
1769 | | let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); |
1770 | | unit.get_mut(subprogram_id).set( |
1771 | | constants::DW_AT_location, |
1772 | | AttributeValue::Exprloc(expression), |
1773 | | ); |
1774 | | |
1775 | | // Writing the DWARF should fail. |
1776 | | let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
1777 | | assert_eq!(dwarf.write(&mut sections), Err(Error::InvalidReference)); |
1778 | | } |
1779 | | } |