gimli/write/
loc.rs

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 = val.size(encoding, unit_offsets)? as u64;
299    if encoding.version <= 4 {
300        w.write_udata(size, 2)?;
301    } else {
302        w.write_uleb128(size)?;
303    }
304    val.write(w, 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}