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