Skip to main content

gimli/write/
loc.rs

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