/src/gimli/src/write/loc.rs
Line | Count | Source |
1 | | use alloc::vec::Vec; |
2 | | use core::ops::{Deref, DerefMut}; |
3 | | |
4 | | use crate::common::{Encoding, LocationListsOffset, SectionId}; |
5 | | use crate::write::{ |
6 | | Address, BaseId, DebugInfoFixup, Error, Expression, FnvIndexSet, Result, Section, Sections, |
7 | | UnitOffsets, Writer, |
8 | | }; |
9 | | |
10 | | define_section!( |
11 | | DebugLoc, |
12 | | LocationListsOffset, |
13 | | "A writable `.debug_loc` section." |
14 | | ); |
15 | | define_section!( |
16 | | DebugLocLists, |
17 | | LocationListsOffset, |
18 | | "A writable `.debug_loclists` section." |
19 | | ); |
20 | | |
21 | | define_offsets!( |
22 | | LocationListOffsets: LocationListId => LocationListsOffset, |
23 | | "The section offsets of a series of location lists within the `.debug_loc` or `.debug_loclists` sections." |
24 | | ); |
25 | | |
26 | | define_id!( |
27 | | LocationListId, |
28 | | "An identifier for a location list in a `LocationListTable`." |
29 | | ); |
30 | | |
31 | | /// A table of location lists that will be stored in a `.debug_loc` or `.debug_loclists` section. |
32 | | #[derive(Debug, Default)] |
33 | | pub struct LocationListTable { |
34 | | base_id: BaseId, |
35 | | locations: FnvIndexSet<LocationList>, |
36 | | } |
37 | | |
38 | | impl LocationListTable { |
39 | | /// Add a location list to the table. |
40 | 0 | pub fn add(&mut self, loc_list: LocationList) -> LocationListId { |
41 | 0 | let (index, _) = self.locations.insert_full(loc_list); |
42 | 0 | LocationListId::new(self.base_id, index) |
43 | 0 | } |
44 | | |
45 | | /// Get a reference to a location list. |
46 | | /// |
47 | | /// # Panics |
48 | | /// |
49 | | /// Panics if `id` is invalid. |
50 | | #[inline] |
51 | | pub fn get(&self, id: LocationListId) -> &LocationList { |
52 | | debug_assert_eq!(self.base_id, id.base_id); |
53 | | &self.locations[id.index] |
54 | | } |
55 | | |
56 | | /// Write the location list table to the appropriate section for the given DWARF version. |
57 | | pub(crate) fn write<W: Writer>( |
58 | | &self, |
59 | | sections: &mut Sections<W>, |
60 | | encoding: Encoding, |
61 | | have_base_address: bool, |
62 | | unit_offsets: Option<&UnitOffsets>, |
63 | | ) -> Result<LocationListOffsets> { |
64 | | if self.locations.is_empty() { |
65 | | return Ok(LocationListOffsets::none()); |
66 | | } |
67 | | |
68 | | match encoding.version { |
69 | | 2..=4 => self.write_loc( |
70 | | &mut sections.debug_loc, |
71 | | &mut sections.debug_loc_fixups, |
72 | | encoding, |
73 | | have_base_address, |
74 | | unit_offsets, |
75 | | ), |
76 | | 5 => self.write_loclists( |
77 | | &mut sections.debug_loclists, |
78 | | &mut sections.debug_loclists_fixups, |
79 | | encoding, |
80 | | unit_offsets, |
81 | | ), |
82 | | _ => Err(Error::UnsupportedVersion(encoding.version)), |
83 | | } |
84 | | } |
85 | | |
86 | | /// Write the location list table to the `.debug_loc` section. |
87 | | fn write_loc<W: Writer>( |
88 | | &self, |
89 | | w: &mut DebugLoc<W>, |
90 | | refs: &mut Vec<DebugInfoFixup>, |
91 | | encoding: Encoding, |
92 | | have_unit_base_address: bool, |
93 | | unit_offsets: Option<&UnitOffsets>, |
94 | | ) -> Result<LocationListOffsets> { |
95 | | let address_size = encoding.address_size; |
96 | | let mut offsets = Vec::new(); |
97 | | for loc_list in self.locations.iter() { |
98 | | let mut have_base_address = have_unit_base_address; |
99 | | offsets.push(w.offset()); |
100 | | for loc in &loc_list.0 { |
101 | | // Note that we must ensure none of the ranges have both begin == 0 and end == 0. |
102 | | // We do this by ensuring that begin != end, which is a bit more restrictive |
103 | | // than required, but still seems reasonable. |
104 | | match *loc { |
105 | | Location::BaseAddress { address } => { |
106 | | let marker = !0 >> (64 - address_size * 8); |
107 | | w.write_udata(marker, address_size)?; |
108 | | w.write_address(address, address_size)?; |
109 | | have_base_address = true; |
110 | | } |
111 | | Location::OffsetPair { |
112 | | begin, |
113 | | end, |
114 | | ref data, |
115 | | } => { |
116 | | if begin == end { |
117 | | return Err(Error::InvalidRange); |
118 | | } |
119 | | if !have_base_address { |
120 | | return Err(Error::MissingBaseAddress); |
121 | | } |
122 | | w.write_udata(begin, address_size)?; |
123 | | w.write_udata(end, address_size)?; |
124 | | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
125 | | } |
126 | | Location::StartEnd { |
127 | | begin, |
128 | | end, |
129 | | ref data, |
130 | | } => { |
131 | | if begin == end { |
132 | | return Err(Error::InvalidRange); |
133 | | } |
134 | | if have_base_address { |
135 | | return Err(Error::UnexpectedBaseAddress); |
136 | | } |
137 | | w.write_address(begin, address_size)?; |
138 | | w.write_address(end, address_size)?; |
139 | | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
140 | | } |
141 | | Location::StartLength { |
142 | | begin, |
143 | | length, |
144 | | ref data, |
145 | | } => { |
146 | | let end = match begin { |
147 | | Address::Constant(begin) => Address::Constant(begin + length), |
148 | | Address::Symbol { symbol, addend } => Address::Symbol { |
149 | | symbol, |
150 | | addend: addend + length as i64, |
151 | | }, |
152 | | }; |
153 | | if begin == end { |
154 | | return Err(Error::InvalidRange); |
155 | | } |
156 | | if have_base_address { |
157 | | return Err(Error::UnexpectedBaseAddress); |
158 | | } |
159 | | w.write_address(begin, address_size)?; |
160 | | w.write_address(end, address_size)?; |
161 | | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
162 | | } |
163 | | Location::DefaultLocation { .. } => { |
164 | | return Err(Error::InvalidRange); |
165 | | } |
166 | | } |
167 | | } |
168 | | w.write_udata(0, address_size)?; |
169 | | w.write_udata(0, address_size)?; |
170 | | } |
171 | | Ok(LocationListOffsets { |
172 | | base_id: self.base_id, |
173 | | offsets, |
174 | | }) |
175 | | } |
176 | | |
177 | | /// Write the location list table to the `.debug_loclists` section. |
178 | | fn write_loclists<W: Writer>( |
179 | | &self, |
180 | | w: &mut DebugLocLists<W>, |
181 | | refs: &mut Vec<DebugInfoFixup>, |
182 | | encoding: Encoding, |
183 | | unit_offsets: Option<&UnitOffsets>, |
184 | | ) -> Result<LocationListOffsets> { |
185 | | let mut offsets = Vec::new(); |
186 | | |
187 | | if encoding.version != 5 { |
188 | | return Err(Error::NeedVersion(5)); |
189 | | } |
190 | | |
191 | | let length_offset = w.write_initial_length(encoding.format)?; |
192 | | let length_base = w.len(); |
193 | | |
194 | | w.write_u16(encoding.version)?; |
195 | | w.write_u8(encoding.address_size)?; |
196 | | w.write_u8(0)?; // segment_selector_size |
197 | | w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_loclistx can't be used, see section 7.29) |
198 | | // FIXME implement DW_FORM_loclistx writing and implement the offset entry list |
199 | | |
200 | | for loc_list in self.locations.iter() { |
201 | | offsets.push(w.offset()); |
202 | | for loc in &loc_list.0 { |
203 | | match *loc { |
204 | | Location::BaseAddress { address } => { |
205 | | w.write_u8(crate::constants::DW_LLE_base_address.0)?; |
206 | | w.write_address(address, encoding.address_size)?; |
207 | | } |
208 | | Location::OffsetPair { |
209 | | begin, |
210 | | end, |
211 | | ref data, |
212 | | } => { |
213 | | w.write_u8(crate::constants::DW_LLE_offset_pair.0)?; |
214 | | w.write_uleb128(begin)?; |
215 | | w.write_uleb128(end)?; |
216 | | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
217 | | } |
218 | | Location::StartEnd { |
219 | | begin, |
220 | | end, |
221 | | ref data, |
222 | | } => { |
223 | | w.write_u8(crate::constants::DW_LLE_start_end.0)?; |
224 | | w.write_address(begin, encoding.address_size)?; |
225 | | w.write_address(end, encoding.address_size)?; |
226 | | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
227 | | } |
228 | | Location::StartLength { |
229 | | begin, |
230 | | length, |
231 | | ref data, |
232 | | } => { |
233 | | w.write_u8(crate::constants::DW_LLE_start_length.0)?; |
234 | | w.write_address(begin, encoding.address_size)?; |
235 | | w.write_uleb128(length)?; |
236 | | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
237 | | } |
238 | | Location::DefaultLocation { ref data } => { |
239 | | w.write_u8(crate::constants::DW_LLE_default_location.0)?; |
240 | | write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
241 | | } |
242 | | } |
243 | | } |
244 | | |
245 | | w.write_u8(crate::constants::DW_LLE_end_of_list.0)?; |
246 | | } |
247 | | |
248 | | let length = (w.len() - length_base) as u64; |
249 | | w.write_initial_length_at(length_offset, length, encoding.format)?; |
250 | | |
251 | | Ok(LocationListOffsets { |
252 | | base_id: self.base_id, |
253 | | offsets, |
254 | | }) |
255 | | } |
256 | | } |
257 | | |
258 | | /// A locations list that will be stored in a `.debug_loc` or `.debug_loclists` section. |
259 | | #[derive(Clone, Debug, Eq, PartialEq, Hash)] |
260 | | pub struct LocationList(pub Vec<Location>); |
261 | | |
262 | | /// A single location. |
263 | | #[derive(Clone, Debug, Eq, PartialEq, Hash)] |
264 | | pub enum Location { |
265 | | /// DW_LLE_base_address |
266 | | BaseAddress { |
267 | | /// Base address. |
268 | | address: Address, |
269 | | }, |
270 | | /// DW_LLE_offset_pair |
271 | | OffsetPair { |
272 | | /// Start of range relative to base address. |
273 | | begin: u64, |
274 | | /// End of range relative to base address. |
275 | | end: u64, |
276 | | /// Location description. |
277 | | data: Expression, |
278 | | }, |
279 | | /// DW_LLE_start_end |
280 | | StartEnd { |
281 | | /// Start of range. |
282 | | begin: Address, |
283 | | /// End of range. |
284 | | end: Address, |
285 | | /// Location description. |
286 | | data: Expression, |
287 | | }, |
288 | | /// DW_LLE_start_length |
289 | | StartLength { |
290 | | /// Start of range. |
291 | | begin: Address, |
292 | | /// Length of range. |
293 | | length: u64, |
294 | | /// Location description. |
295 | | data: Expression, |
296 | | }, |
297 | | /// DW_LLE_default_location |
298 | | DefaultLocation { |
299 | | /// Location description. |
300 | | data: Expression, |
301 | | }, |
302 | | } |
303 | | |
304 | | fn write_expression<W: Writer>( |
305 | | w: &mut W, |
306 | | refs: &mut Vec<DebugInfoFixup>, |
307 | | encoding: Encoding, |
308 | | unit_offsets: Option<&UnitOffsets>, |
309 | | val: &Expression, |
310 | | ) -> Result<()> { |
311 | | let size = val.size(encoding, unit_offsets)? as u64; |
312 | | if encoding.version <= 4 { |
313 | | w.write_udata(size, 2)?; |
314 | | } else { |
315 | | w.write_uleb128(size)?; |
316 | | } |
317 | | val.write(w, Some(refs), encoding, unit_offsets)?; |
318 | | Ok(()) |
319 | | } |
320 | | |
321 | | #[cfg(feature = "read")] |
322 | | mod convert { |
323 | | use super::*; |
324 | | |
325 | | use crate::read::{self, Reader}; |
326 | | use crate::write::{ConvertDebugInfoRef, ConvertError, ConvertResult}; |
327 | | |
328 | | impl LocationList { |
329 | | /// Create a location list by reading the data from the give location list iter. |
330 | | pub(crate) fn from<R: Reader<Offset = usize>>( |
331 | | mut from: read::RawLocListIter<R>, |
332 | | from_unit: read::UnitRef<'_, R>, |
333 | | convert_address: &dyn Fn(u64) -> Option<Address>, |
334 | | convert_debug_info_ref: &dyn ConvertDebugInfoRef, |
335 | | ) -> ConvertResult<Self> { |
336 | | let convert_expression = |x| { |
337 | | Expression::from( |
338 | | x, |
339 | | from_unit.encoding(), |
340 | | Some(from_unit), |
341 | | convert_address, |
342 | | convert_debug_info_ref, |
343 | | ) |
344 | | }; |
345 | | let convert_address = |x| convert_address(x).ok_or(ConvertError::InvalidAddress); |
346 | | let mut have_base_address = from_unit.low_pc != 0; |
347 | | let mut loc_list = Vec::new(); |
348 | | while let Some(from_loc) = from.next()? { |
349 | | let loc = match from_loc { |
350 | | read::RawLocListEntry::AddressOrOffsetPair { begin, end, data } => { |
351 | | // This matches the logic in `RangeList::from`. See the comments there. |
352 | | let begin = convert_address(begin)?; |
353 | | let end = convert_address(end)?; |
354 | | let data = convert_expression(data)?; |
355 | | if have_base_address { |
356 | | let (Address::Constant(begin_offset), Address::Constant(end_offset)) = |
357 | | (begin, end) |
358 | | else { |
359 | | return Err(ConvertError::InvalidRangeRelativeAddress); |
360 | | }; |
361 | | Location::OffsetPair { |
362 | | begin: begin_offset, |
363 | | end: end_offset, |
364 | | data, |
365 | | } |
366 | | } else { |
367 | | Location::StartEnd { begin, end, data } |
368 | | } |
369 | | } |
370 | | read::RawLocListEntry::BaseAddress { addr } => { |
371 | | have_base_address = true; |
372 | | let address = convert_address(addr)?; |
373 | | Location::BaseAddress { address } |
374 | | } |
375 | | read::RawLocListEntry::BaseAddressx { addr } => { |
376 | | have_base_address = true; |
377 | | let address = convert_address(from_unit.address(addr)?)?; |
378 | | Location::BaseAddress { address } |
379 | | } |
380 | | read::RawLocListEntry::StartxEndx { begin, end, data } => { |
381 | | let begin = convert_address(from_unit.address(begin)?)?; |
382 | | let end = convert_address(from_unit.address(end)?)?; |
383 | | let data = convert_expression(data)?; |
384 | | Location::StartEnd { begin, end, data } |
385 | | } |
386 | | read::RawLocListEntry::StartxLength { |
387 | | begin, |
388 | | length, |
389 | | data, |
390 | | } => { |
391 | | let begin = convert_address(from_unit.address(begin)?)?; |
392 | | let data = convert_expression(data)?; |
393 | | Location::StartLength { |
394 | | begin, |
395 | | length, |
396 | | data, |
397 | | } |
398 | | } |
399 | | read::RawLocListEntry::OffsetPair { begin, end, data } => { |
400 | | let data = convert_expression(data)?; |
401 | | Location::OffsetPair { begin, end, data } |
402 | | } |
403 | | read::RawLocListEntry::StartEnd { begin, end, data } => { |
404 | | let begin = convert_address(begin)?; |
405 | | let end = convert_address(end)?; |
406 | | let data = convert_expression(data)?; |
407 | | Location::StartEnd { begin, end, data } |
408 | | } |
409 | | read::RawLocListEntry::StartLength { |
410 | | begin, |
411 | | length, |
412 | | data, |
413 | | } => { |
414 | | let begin = convert_address(begin)?; |
415 | | let data = convert_expression(data)?; |
416 | | Location::StartLength { |
417 | | begin, |
418 | | length, |
419 | | data, |
420 | | } |
421 | | } |
422 | | read::RawLocListEntry::DefaultLocation { data } => { |
423 | | let data = convert_expression(data)?; |
424 | | Location::DefaultLocation { data } |
425 | | } |
426 | | }; |
427 | | // In some cases, existing data may contain begin == end, filtering |
428 | | // these out. |
429 | | match loc { |
430 | | Location::StartLength { length: 0, .. } => continue, |
431 | | Location::StartEnd { begin, end, .. } if begin == end => continue, |
432 | | Location::OffsetPair { begin, end, .. } if begin == end => continue, |
433 | | _ => (), |
434 | | } |
435 | | loc_list.push(loc); |
436 | | } |
437 | | Ok(LocationList(loc_list)) |
438 | | } |
439 | | } |
440 | | } |
441 | | |
442 | | #[cfg(test)] |
443 | | #[cfg(feature = "read")] |
444 | | mod tests { |
445 | | use super::*; |
446 | | use crate::LittleEndian; |
447 | | use crate::common::{ |
448 | | DebugAbbrevOffset, DebugAddrBase, DebugLocListsBase, DebugRngListsBase, |
449 | | DebugStrOffsetsBase, Format, UnitSectionOffset, |
450 | | }; |
451 | | use crate::write::{AttributeValue, DwarfUnit, EndianVec, NoConvertDebugInfoRef}; |
452 | | use crate::{constants, read}; |
453 | | use alloc::sync::Arc; |
454 | | |
455 | | #[test] |
456 | | fn test_loc_list() { |
457 | | let mut expression = Expression::new(); |
458 | | expression.op_constu(0); |
459 | | |
460 | | for &version in &[2, 3, 4, 5] { |
461 | | for &address_size in &[4, 8] { |
462 | | for &format in &[Format::Dwarf32, Format::Dwarf64] { |
463 | | let encoding = Encoding { |
464 | | format, |
465 | | version, |
466 | | address_size, |
467 | | }; |
468 | | |
469 | | let mut loc_list = LocationList(vec![ |
470 | | Location::StartLength { |
471 | | begin: Address::Constant(6666), |
472 | | length: 7777, |
473 | | data: expression.clone(), |
474 | | }, |
475 | | Location::StartEnd { |
476 | | begin: Address::Constant(4444), |
477 | | end: Address::Constant(5555), |
478 | | data: expression.clone(), |
479 | | }, |
480 | | Location::BaseAddress { |
481 | | address: Address::Constant(1111), |
482 | | }, |
483 | | Location::OffsetPair { |
484 | | begin: 2222, |
485 | | end: 3333, |
486 | | data: expression.clone(), |
487 | | }, |
488 | | ]); |
489 | | if version >= 5 { |
490 | | loc_list.0.push(Location::DefaultLocation { |
491 | | data: expression.clone(), |
492 | | }); |
493 | | } |
494 | | |
495 | | let mut locations = LocationListTable::default(); |
496 | | let loc_list_id = locations.add(loc_list.clone()); |
497 | | |
498 | | let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
499 | | let loc_list_offsets = locations |
500 | | .write(&mut sections, encoding, false, None) |
501 | | .unwrap(); |
502 | | assert!(sections.debug_loc_fixups.is_empty()); |
503 | | assert!(sections.debug_loclists_fixups.is_empty()); |
504 | | |
505 | | let read_debug_loc = |
506 | | read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); |
507 | | let read_debug_loclists = |
508 | | read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); |
509 | | let read_loc = read::LocationLists::new(read_debug_loc, read_debug_loclists); |
510 | | let offset = loc_list_offsets.get(loc_list_id); |
511 | | let read_loc_list = read_loc.raw_locations(offset, encoding).unwrap(); |
512 | | |
513 | | let dwarf = read::Dwarf { |
514 | | locations: read_loc, |
515 | | ..Default::default() |
516 | | }; |
517 | | let unit = read::Unit { |
518 | | header: read::UnitHeader::new( |
519 | | encoding, |
520 | | 0, |
521 | | read::UnitType::Compilation, |
522 | | DebugAbbrevOffset(0), |
523 | | SectionId::DebugInfo, |
524 | | UnitSectionOffset(0), |
525 | | read::EndianSlice::default(), |
526 | | ), |
527 | | abbreviations: Arc::new(read::Abbreviations::default()), |
528 | | name: None, |
529 | | comp_dir: None, |
530 | | low_pc: 0, |
531 | | str_offsets_base: DebugStrOffsetsBase(0), |
532 | | addr_base: DebugAddrBase(0), |
533 | | loclists_base: DebugLocListsBase(0), |
534 | | rnglists_base: DebugRngListsBase(0), |
535 | | line_program: None, |
536 | | dwo_id: None, |
537 | | }; |
538 | | let convert_loc_list = LocationList::from( |
539 | | read_loc_list, |
540 | | unit.unit_ref(&dwarf), |
541 | | &|address| Some(Address::Constant(address)), |
542 | | &NoConvertDebugInfoRef, |
543 | | ) |
544 | | .unwrap(); |
545 | | |
546 | | if version <= 4 { |
547 | | loc_list.0[0] = Location::StartEnd { |
548 | | begin: Address::Constant(6666), |
549 | | end: Address::Constant(6666 + 7777), |
550 | | data: expression.clone(), |
551 | | }; |
552 | | } |
553 | | assert_eq!(loc_list, convert_loc_list); |
554 | | } |
555 | | } |
556 | | } |
557 | | } |
558 | | |
559 | | #[test] |
560 | | fn test_loc_base_address_v4() { |
561 | | let encoding = Encoding { |
562 | | format: Format::Dwarf32, |
563 | | version: 4, |
564 | | address_size: 8, |
565 | | }; |
566 | | let location = [ |
567 | | Location::OffsetPair { |
568 | | begin: 0x1234, |
569 | | end: 0x2345, |
570 | | data: Expression::new(), |
571 | | }, |
572 | | Location::StartEnd { |
573 | | begin: Address::Constant(0x1234), |
574 | | end: Address::Constant(0x2345), |
575 | | data: Expression::new(), |
576 | | }, |
577 | | Location::StartLength { |
578 | | begin: Address::Constant(0x1234), |
579 | | length: 1, |
580 | | data: Expression::new(), |
581 | | }, |
582 | | ]; |
583 | | for (l, low_pc, err) in [ |
584 | | (0, None, Err(Error::MissingBaseAddress)), |
585 | | (0, Some(0), Err(Error::MissingBaseAddress)), |
586 | | (0, Some(1), Ok(())), |
587 | | (1, None, Ok(())), |
588 | | (1, Some(0), Ok(())), |
589 | | (1, Some(1), Err(Error::UnexpectedBaseAddress)), |
590 | | (2, None, Ok(())), |
591 | | (2, Some(0), Ok(())), |
592 | | (2, Some(1), Err(Error::UnexpectedBaseAddress)), |
593 | | ] { |
594 | | let mut dwarf = DwarfUnit::new(encoding); |
595 | | let location = dwarf |
596 | | .unit |
597 | | .locations |
598 | | .add(LocationList(vec![location[l].clone()])); |
599 | | |
600 | | let root = dwarf.unit.get_mut(dwarf.unit.root()); |
601 | | if let Some(low_pc) = low_pc { |
602 | | root.set( |
603 | | constants::DW_AT_low_pc, |
604 | | AttributeValue::Address(Address::Constant(low_pc)), |
605 | | ); |
606 | | } |
607 | | root.set( |
608 | | constants::DW_AT_location, |
609 | | AttributeValue::LocationListRef(location), |
610 | | ); |
611 | | |
612 | | let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
613 | | assert_eq!(dwarf.write(&mut sections), err); |
614 | | } |
615 | | } |
616 | | } |