Coverage Report

Created: 2026-05-30 06:13

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