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