Skip to main content

gimli/write/
range.rs

1use alloc::vec::Vec;
2use core::ops::{Deref, DerefMut};
3
4use crate::common::{Encoding, RangeListsOffset, SectionId};
5use crate::write::{Address, BaseId, Error, FnvIndexSet, Result, Section, Sections, Writer};
6
7define_section!(
8    DebugRanges,
9    RangeListsOffset,
10    "A writable `.debug_ranges` section."
11);
12define_section!(
13    DebugRngLists,
14    RangeListsOffset,
15    "A writable `.debug_rnglists` section."
16);
17
18define_offsets!(
19    RangeListOffsets: RangeListId => RangeListsOffset,
20    "The section offsets of a series of range lists within the `.debug_ranges` or `.debug_rnglists` sections."
21);
22
23define_id!(
24    RangeListId,
25    "An identifier for a range list in a `RangeListTable`."
26);
27
28/// A table of range lists that will be stored in a `.debug_ranges` or `.debug_rnglists` section.
29#[derive(Debug, Default)]
30pub struct RangeListTable {
31    base_id: BaseId,
32    ranges: FnvIndexSet<RangeList>,
33}
34
35impl RangeListTable {
36    /// Add a range list to the table.
37    pub fn add(&mut self, range_list: RangeList) -> RangeListId {
38        let (index, _) = self.ranges.insert_full(range_list);
39        RangeListId::new(self.base_id, index)
40    }
41
42    /// Get a reference to a location list.
43    ///
44    /// # Panics
45    ///
46    /// Panics if `id` is invalid.
47    #[inline]
48    pub fn get(&self, id: RangeListId) -> &RangeList {
49        debug_assert_eq!(self.base_id, id.base_id);
50        &self.ranges[id.index]
51    }
52
53    /// Write the range list table to the appropriate section for the given DWARF version.
54    pub(crate) fn write<W: Writer>(
55        &self,
56        sections: &mut Sections<W>,
57        encoding: Encoding,
58    ) -> Result<RangeListOffsets> {
59        if self.ranges.is_empty() {
60            return Ok(RangeListOffsets::none());
61        }
62
63        match encoding.version {
64            2..=4 => self.write_ranges(&mut sections.debug_ranges, encoding.address_size),
65            5 => self.write_rnglists(&mut sections.debug_rnglists, encoding),
66            _ => Err(Error::UnsupportedVersion(encoding.version)),
67        }
68    }
69
70    /// Write the range list table to the `.debug_ranges` section.
71    fn write_ranges<W: Writer>(
72        &self,
73        w: &mut DebugRanges<W>,
74        address_size: u8,
75    ) -> Result<RangeListOffsets> {
76        let mut offsets = Vec::new();
77        for range_list in self.ranges.iter() {
78            offsets.push(w.offset());
79            for range in &range_list.0 {
80                // Note that we must ensure none of the ranges have both begin == 0 and end == 0.
81                // We do this by ensuring that begin != end, which is a bit more restrictive
82                // than required, but still seems reasonable.
83                match *range {
84                    Range::BaseAddress { address } => {
85                        let marker = !0 >> (64 - address_size * 8);
86                        w.write_udata(marker, address_size)?;
87                        w.write_address(address, address_size)?;
88                    }
89                    Range::OffsetPair { begin, end } => {
90                        if begin == end {
91                            return Err(Error::InvalidRange);
92                        }
93                        w.write_udata(begin, address_size)?;
94                        w.write_udata(end, address_size)?;
95                    }
96                    Range::StartEnd { begin, end } => {
97                        if begin == end {
98                            return Err(Error::InvalidRange);
99                        }
100                        w.write_address(begin, address_size)?;
101                        w.write_address(end, address_size)?;
102                    }
103                    Range::StartLength { begin, length } => {
104                        let end = match begin {
105                            Address::Constant(begin) => Address::Constant(begin + length),
106                            Address::Symbol { symbol, addend } => Address::Symbol {
107                                symbol,
108                                addend: addend + length as i64,
109                            },
110                        };
111                        if begin == end {
112                            return Err(Error::InvalidRange);
113                        }
114                        w.write_address(begin, address_size)?;
115                        w.write_address(end, address_size)?;
116                    }
117                }
118            }
119            w.write_udata(0, address_size)?;
120            w.write_udata(0, address_size)?;
121        }
122        Ok(RangeListOffsets {
123            base_id: self.base_id,
124            offsets,
125        })
126    }
127
128    /// Write the range list table to the `.debug_rnglists` section.
129    fn write_rnglists<W: Writer>(
130        &self,
131        w: &mut DebugRngLists<W>,
132        encoding: Encoding,
133    ) -> Result<RangeListOffsets> {
134        let mut offsets = Vec::new();
135
136        if encoding.version != 5 {
137            return Err(Error::NeedVersion(5));
138        }
139
140        let length_offset = w.write_initial_length(encoding.format)?;
141        let length_base = w.len();
142
143        w.write_u16(encoding.version)?;
144        w.write_u8(encoding.address_size)?;
145        w.write_u8(0)?; // segment_selector_size
146        w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28)
147        // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list
148
149        for range_list in self.ranges.iter() {
150            offsets.push(w.offset());
151            for range in &range_list.0 {
152                match *range {
153                    Range::BaseAddress { address } => {
154                        w.write_u8(crate::constants::DW_RLE_base_address.0)?;
155                        w.write_address(address, encoding.address_size)?;
156                    }
157                    Range::OffsetPair { begin, end } => {
158                        w.write_u8(crate::constants::DW_RLE_offset_pair.0)?;
159                        w.write_uleb128(begin)?;
160                        w.write_uleb128(end)?;
161                    }
162                    Range::StartEnd { begin, end } => {
163                        w.write_u8(crate::constants::DW_RLE_start_end.0)?;
164                        w.write_address(begin, encoding.address_size)?;
165                        w.write_address(end, encoding.address_size)?;
166                    }
167                    Range::StartLength { begin, length } => {
168                        w.write_u8(crate::constants::DW_RLE_start_length.0)?;
169                        w.write_address(begin, encoding.address_size)?;
170                        w.write_uleb128(length)?;
171                    }
172                }
173            }
174
175            w.write_u8(crate::constants::DW_RLE_end_of_list.0)?;
176        }
177
178        let length = (w.len() - length_base) as u64;
179        w.write_initial_length_at(length_offset, length, encoding.format)?;
180
181        Ok(RangeListOffsets {
182            base_id: self.base_id,
183            offsets,
184        })
185    }
186}
187
188/// A range list that will be stored in a `.debug_ranges` or `.debug_rnglists` section.
189#[derive(Clone, Debug, Eq, PartialEq, Hash)]
190pub struct RangeList(pub Vec<Range>);
191
192/// A single range.
193#[derive(Clone, Debug, Eq, PartialEq, Hash)]
194pub enum Range {
195    /// DW_RLE_base_address
196    BaseAddress {
197        /// Base address.
198        address: Address,
199    },
200    /// DW_RLE_offset_pair
201    OffsetPair {
202        /// Start of range relative to base address.
203        begin: u64,
204        /// End of range relative to base address.
205        end: u64,
206    },
207    /// DW_RLE_start_end
208    StartEnd {
209        /// Start of range.
210        begin: Address,
211        /// End of range.
212        end: Address,
213    },
214    /// DW_RLE_start_length
215    StartLength {
216        /// Start of range.
217        begin: Address,
218        /// Length of range.
219        length: u64,
220    },
221}
222
223#[cfg(feature = "read")]
224mod convert {
225    use super::*;
226
227    use crate::read::{self, Reader};
228    use crate::write::{ConvertError, ConvertResult};
229
230    impl RangeList {
231        /// Create a range list by reading the data from the give range list iter.
232        pub(crate) fn from<R: Reader<Offset = usize>>(
233            mut from: read::RawRngListIter<R>,
234            from_unit: read::UnitRef<'_, R>,
235            convert_address: &dyn Fn(u64) -> Option<Address>,
236        ) -> ConvertResult<Self> {
237            let convert_address = |x| convert_address(x).ok_or(ConvertError::InvalidAddress);
238            // The CU DW_AT_low_pc was parsed with `Reader::read_address`.
239            // We could pass it to `convert_address`, but we rely on `read_address`
240            // returning 0 if and only if it was an unrelocated 0 value.
241            // If it is 0, then DWARF v2-4 ranges are address pairs unless there is a
242            // base address entry, otherwise they must be offset pairs.
243            // We don't handle the possibility of this being a tombstone since I don't
244            // think that can occur.
245            let mut have_base_address = from_unit.low_pc != 0;
246            let mut ranges = Vec::new();
247            while let Some(from_range) = from.next()? {
248                let range = match from_range {
249                    read::RawRngListEntry::AddressOrOffsetPair { begin, end } => {
250                        // These were parsed with `Reader::read_address`, even if they are
251                        // offsets, so we need to apply the conversion function.
252                        // For executables, the converted values will be `Address::Constant`
253                        // for both offsets and addresses.
254                        // For relocatable objects, we expect offsets to be `Address::Constant`
255                        // and addresses to be `Address::Symbol`.
256                        let begin = convert_address(begin)?;
257                        let end = convert_address(end)?;
258                        // We must use the presence of a base address to disambiguate between
259                        // offsets and addresses for both executables and relocatable objects.
260                        // (This logic is also used in `LocationList::from`.)
261                        if have_base_address {
262                            let (Address::Constant(begin_offset), Address::Constant(end_offset)) =
263                                (begin, end)
264                            else {
265                                // We have a relocatable object file that uses both a base address
266                                // and an address pair.
267                                return Err(ConvertError::InvalidRangeRelativeAddress);
268                            };
269                            Range::OffsetPair {
270                                begin: begin_offset,
271                                end: end_offset,
272                            }
273                        } else {
274                            Range::StartEnd { begin, end }
275                        }
276                    }
277                    read::RawRngListEntry::BaseAddress { addr } => {
278                        have_base_address = true;
279                        let address = convert_address(addr)?;
280                        Range::BaseAddress { address }
281                    }
282                    read::RawRngListEntry::BaseAddressx { addr } => {
283                        have_base_address = true;
284                        let address = convert_address(from_unit.address(addr)?)?;
285                        Range::BaseAddress { address }
286                    }
287                    read::RawRngListEntry::StartxEndx { begin, end } => {
288                        let begin = convert_address(from_unit.address(begin)?)?;
289                        let end = convert_address(from_unit.address(end)?)?;
290                        Range::StartEnd { begin, end }
291                    }
292                    read::RawRngListEntry::StartxLength { begin, length } => {
293                        let begin = convert_address(from_unit.address(begin)?)?;
294                        Range::StartLength { begin, length }
295                    }
296                    read::RawRngListEntry::OffsetPair { begin, end } => {
297                        Range::OffsetPair { begin, end }
298                    }
299                    read::RawRngListEntry::StartEnd { begin, end } => {
300                        let begin = convert_address(begin)?;
301                        let end = convert_address(end)?;
302                        Range::StartEnd { begin, end }
303                    }
304                    read::RawRngListEntry::StartLength { begin, length } => {
305                        let begin = convert_address(begin)?;
306                        Range::StartLength { begin, length }
307                    }
308                };
309                // Filtering empty ranges out.
310                match range {
311                    Range::StartLength { length: 0, .. } => continue,
312                    Range::StartEnd { begin, end, .. } if begin == end => continue,
313                    Range::OffsetPair { begin, end, .. } if begin == end => continue,
314                    _ => (),
315                }
316                ranges.push(range);
317            }
318            Ok(RangeList(ranges))
319        }
320    }
321}
322
323#[cfg(test)]
324#[cfg(feature = "read")]
325mod tests {
326    use super::*;
327    use crate::LittleEndian;
328    use crate::common::{
329        DebugAbbrevOffset, DebugAddrBase, DebugLocListsBase, DebugRngListsBase,
330        DebugStrOffsetsBase, Format, UnitSectionOffset,
331    };
332    use crate::read;
333    use crate::write::{EndianVec, Range, RangeListTable};
334    use alloc::sync::Arc;
335
336    #[test]
337    fn test_range() {
338        for &version in &[2, 3, 4, 5] {
339            for &address_size in &[4, 8] {
340                for &format in &[Format::Dwarf32, Format::Dwarf64] {
341                    let encoding = Encoding {
342                        format,
343                        version,
344                        address_size,
345                    };
346
347                    let mut range_list = RangeList(vec![
348                        Range::StartLength {
349                            begin: Address::Constant(6666),
350                            length: 7777,
351                        },
352                        Range::StartEnd {
353                            begin: Address::Constant(4444),
354                            end: Address::Constant(5555),
355                        },
356                        Range::BaseAddress {
357                            address: Address::Constant(1111),
358                        },
359                        Range::OffsetPair {
360                            begin: 2222,
361                            end: 3333,
362                        },
363                    ]);
364
365                    let mut ranges = RangeListTable::default();
366                    let range_list_id = ranges.add(range_list.clone());
367
368                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
369                    let range_list_offsets = ranges.write(&mut sections, encoding).unwrap();
370
371                    let read_debug_ranges =
372                        read::DebugRanges::new(sections.debug_ranges.slice(), LittleEndian);
373                    let read_debug_rnglists =
374                        read::DebugRngLists::new(sections.debug_rnglists.slice(), LittleEndian);
375                    let read_ranges = read::RangeLists::new(read_debug_ranges, read_debug_rnglists);
376                    let offset = range_list_offsets.get(range_list_id);
377                    let read_range_list = read_ranges.raw_ranges(offset, encoding).unwrap();
378
379                    let dwarf = read::Dwarf {
380                        ranges: read_ranges,
381                        ..Default::default()
382                    };
383                    let unit = read::Unit {
384                        header: read::UnitHeader::new(
385                            encoding,
386                            0,
387                            read::UnitType::Compilation,
388                            DebugAbbrevOffset(0),
389                            SectionId::DebugInfo,
390                            UnitSectionOffset(0),
391                            read::EndianSlice::default(),
392                        ),
393                        abbreviations: Arc::new(read::Abbreviations::default()),
394                        name: None,
395                        comp_dir: None,
396                        low_pc: 0,
397                        str_offsets_base: DebugStrOffsetsBase(0),
398                        addr_base: DebugAddrBase(0),
399                        loclists_base: DebugLocListsBase(0),
400                        rnglists_base: DebugRngListsBase(0),
401                        line_program: None,
402                        dwo_id: None,
403                    };
404                    let convert_range_list =
405                        RangeList::from(read_range_list, unit.unit_ref(&dwarf), &|address| {
406                            Some(Address::Constant(address))
407                        })
408                        .unwrap();
409
410                    if version <= 4 {
411                        range_list.0[0] = Range::StartEnd {
412                            begin: Address::Constant(6666),
413                            end: Address::Constant(6666 + 7777),
414                        };
415                    }
416                    assert_eq!(range_list, convert_range_list);
417                }
418            }
419        }
420    }
421}