Coverage Report

Created: 2026-08-13 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gimli/src/write/line.rs
Line
Count
Source
1
use alloc::vec::Vec;
2
use core::ops::{Deref, DerefMut};
3
4
use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId};
5
use crate::constants;
6
use crate::leb128::write::Leb128;
7
use crate::write::{
8
    Address, Error, FnvIndexMap, FnvIndexSet, LineStringId, LineStringTable, Result, Section,
9
    StringId, StringTable, Writer,
10
};
11
12
/// The number assigned to the first special opcode.
13
//
14
// We output all instructions for all DWARF versions, since readers
15
// should be able to ignore instructions they don't support.
16
const OPCODE_BASE: u8 = 13;
17
18
/// A line number program.
19
#[derive(Debug, Clone)]
20
pub struct LineProgram {
21
    /// True if this line program was created with `LineProgram::none()`.
22
    none: bool,
23
    encoding: Encoding,
24
    line_encoding: LineEncoding,
25
26
    /// A list of source directory path names.
27
    ///
28
    /// If a path is relative, then the directory is located relative to the working
29
    /// directory of the compilation unit.
30
    ///
31
    /// The first entry is for the working directory of the compilation unit.
32
    directories: FnvIndexSet<LineString>,
33
34
    /// A list of source file entries.
35
    ///
36
    /// Each entry has a path name and a directory.
37
    ///
38
    /// If a path is a relative, then the file is located relative to the
39
    /// directory. Otherwise the directory is meaningless.
40
    ///
41
    /// Does not include comp_file, even for version >= 5.
42
    files: FnvIndexMap<(LineString, DirectoryId), FileInfo>,
43
44
    /// True if the file entries may have valid timestamps.
45
    ///
46
    /// Entries may still have a timestamp of 0 even if this is set.
47
    /// For version <= 4, this is ignored.
48
    /// For version 5, this controls whether to emit `DW_LNCT_timestamp`.
49
    pub file_has_timestamp: bool,
50
51
    /// True if the file entries may have valid sizes.
52
    ///
53
    /// Entries may still have a size of 0 even if this is set.
54
    /// For version <= 4, this is ignored.
55
    /// For version 5, this controls whether to emit `DW_LNCT_size`.
56
    pub file_has_size: bool,
57
58
    /// True if the file entries have valid MD5 checksums.
59
    ///
60
    /// For version <= 4, this is ignored.
61
    /// For version 5, this controls whether to emit `DW_LNCT_MD5`.
62
    pub file_has_md5: bool,
63
64
    /// True if the file entries have embedded source code.
65
    ///
66
    /// For version <= 4, this is ignored.
67
    /// For version 5, this controls whether to emit `DW_LNCT_LLVM_source`.
68
    pub file_has_source: bool,
69
70
    prev_row: LineRow,
71
    row: LineRow,
72
    // TODO: this probably should be either rows or sequences instead
73
    instructions: Vec<LineInstruction>,
74
    in_sequence: bool,
75
}
76
77
impl LineProgram {
78
    /// Create a new `LineProgram`.
79
    ///
80
    /// `working_dir` defines the working directory of the compilation unit.
81
    ///
82
    /// `source_dir`, `source_file` and `source_file_info` define the first
83
    /// file entry. `source_dir` may be relative to `working_dir`, and may be
84
    /// `None` if `source_file` is in `working_dir`. The first file entry
85
    /// is usually the primary source file.
86
    ///
87
    /// The standard specifies that `working_dir` should be the same as the
88
    /// `DW_AT_comp_dir` attribute of the compilation unit DIE, and the
89
    /// combination of `source_dir` and `source_file` should be the same
90
    /// as the `DW_AT_name` attribute of the compilation unit DIE.
91
    /// However, neither of these are enforced by this library.
92
    ///
93
    /// # Panics
94
    ///
95
    /// Panics if `line_encoding.line_base` > 0.
96
    ///
97
    /// Panics if `line_encoding.line_base` + `line_encoding.line_range` <= 0.
98
    ///
99
    /// Panics if `working_dir`, `source_dir`, or `source_file` are empty or
100
    /// contain a null byte.
101
0
    pub fn new(
102
0
        encoding: Encoding,
103
0
        line_encoding: LineEncoding,
104
0
        working_dir: LineString,
105
0
        source_dir: Option<LineString>,
106
0
        source_file: LineString,
107
0
        source_file_info: Option<FileInfo>,
108
0
    ) -> LineProgram {
109
        // We require a special opcode for a line advance of 0.
110
        // See the debug_asserts in generate_row().
111
0
        assert!(line_encoding.line_base <= 0);
112
0
        assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0);
113
0
        let mut program = LineProgram {
114
0
            none: false,
115
0
            encoding,
116
0
            line_encoding,
117
0
            directories: FnvIndexSet::default(),
118
0
            files: FnvIndexMap::default(),
119
0
            prev_row: LineRow::initial_state(encoding, line_encoding),
120
0
            row: LineRow::initial_state(encoding, line_encoding),
121
0
            instructions: Vec::new(),
122
0
            in_sequence: false,
123
0
            file_has_timestamp: false,
124
0
            file_has_size: false,
125
0
            file_has_md5: false,
126
0
            file_has_source: false,
127
0
        };
128
        // For all DWARF versions, directory index 0 is working_dir.
129
        // For version <= 4, the entry is implicit. We still add
130
        // it here so that we use it, but we don't emit it.
131
0
        let working_dir_id = program.add_directory(working_dir);
132
        // For DWARF version >= 5, file index 0 is source_file and must exist.
133
0
        if encoding.version >= 5 {
134
0
            let source_dir_id = match source_dir {
135
0
                Some(source_dir) => program.add_directory(source_dir),
136
0
                None => working_dir_id,
137
            };
138
0
            program.add_file(source_file, source_dir_id, source_file_info);
139
0
        }
140
0
        program
141
0
    }
142
143
    /// Create a new `LineProgram` with no fields set.
144
    ///
145
    /// This can be used when the `LineProgram` will not be used.
146
    ///
147
    /// You should not attempt to add files or line instructions to
148
    /// this line program, or write it to the `.debug_line` section.
149
0
    pub fn none() -> Self {
150
0
        let encoding = Encoding {
151
0
            format: Format::Dwarf32,
152
0
            version: 2,
153
0
            address_size: 0,
154
0
        };
155
0
        let line_encoding = LineEncoding::default();
156
0
        LineProgram {
157
0
            none: true,
158
0
            encoding,
159
0
            line_encoding,
160
0
            directories: FnvIndexSet::default(),
161
0
            files: FnvIndexMap::default(),
162
0
            prev_row: LineRow::initial_state(encoding, line_encoding),
163
0
            row: LineRow::initial_state(encoding, line_encoding),
164
0
            instructions: Vec::new(),
165
0
            in_sequence: false,
166
0
            file_has_timestamp: false,
167
0
            file_has_size: false,
168
0
            file_has_md5: false,
169
0
            file_has_source: false,
170
0
        }
171
0
    }
172
173
    /// Return true if this line program was created with `LineProgram::none()`.
174
    #[inline]
175
0
    pub fn is_none(&self) -> bool {
176
0
        self.none
177
0
    }
178
179
    /// Return the encoding parameters for this line program.
180
    #[inline]
181
    pub fn encoding(&self) -> Encoding {
182
        self.encoding
183
    }
184
185
    /// Return the DWARF version for this line program.
186
    #[inline]
187
    pub fn version(&self) -> u16 {
188
        self.encoding.version
189
    }
190
191
    /// Return the address size in bytes for this line program.
192
    #[inline]
193
    pub fn address_size(&self) -> u8 {
194
        self.encoding.address_size
195
    }
196
197
    /// Return the DWARF format for this line program.
198
    #[inline]
199
    pub fn format(&self) -> Format {
200
        self.encoding.format
201
    }
202
203
    /// Return the id for the working directory of the compilation unit.
204
    #[inline]
205
    pub fn default_directory(&self) -> DirectoryId {
206
        DirectoryId(0)
207
    }
208
209
    /// Add a directory entry and return its id.
210
    ///
211
    /// If the directory already exists, then return the id of the existing entry.
212
    ///
213
    /// If the path is relative, then the directory is located relative to the working
214
    /// directory of the compilation unit.
215
    ///
216
    /// # Panics
217
    ///
218
    /// Panics if `directory` is empty or contains a null byte.
219
0
    pub fn add_directory(&mut self, directory: LineString) -> DirectoryId {
220
0
        if let LineString::String(ref val) = directory {
221
            // For DWARF version <= 4, directories must not be empty.
222
            // The first directory isn't emitted so skip the check for it.
223
0
            if self.encoding.version <= 4 && !self.directories.is_empty() {
224
0
                assert!(!val.is_empty());
225
0
            }
226
0
            assert!(!val.contains(&0));
227
0
        }
228
0
        let (index, _) = self.directories.insert_full(directory);
229
0
        DirectoryId(index)
230
0
    }
231
232
    /// Get a reference to a directory entry.
233
    ///
234
    /// # Panics
235
    ///
236
    /// Panics if `id` is invalid.
237
0
    pub fn get_directory(&self, id: DirectoryId) -> &LineString {
238
0
        self.directories.get_index(id.0).unwrap()
239
0
    }
240
241
    /// Add a file entry and return its id.
242
    ///
243
    /// If the file already exists, then return the id of the existing entry.
244
    ///
245
    /// If the file path is relative, then the file is located relative
246
    /// to the directory. Otherwise the directory is meaningless, but it
247
    /// is still used as a key for file entries.
248
    ///
249
    /// If `info` is `None`, then new entries are assigned
250
    /// default information, and existing entries are unmodified.
251
    ///
252
    /// If `info` is not `None`, then it is always assigned to the
253
    /// entry, even if the entry already exists.
254
    ///
255
    /// # Panics
256
    ///
257
    /// Panics if 'file' is empty or contains a null byte.
258
0
    pub fn add_file(
259
0
        &mut self,
260
0
        file: LineString,
261
0
        directory: DirectoryId,
262
0
        info: Option<FileInfo>,
263
0
    ) -> FileId {
264
0
        if let LineString::String(ref val) = file {
265
0
            if self.encoding.version <= 4 {
266
0
                assert!(!val.is_empty());
267
0
            }
268
0
            assert!(!val.contains(&0));
269
0
        }
270
271
0
        let key = (file, directory);
272
0
        let index = if let Some(info) = info {
273
0
            let (index, _) = self.files.insert_full(key, info);
274
0
            index
275
        } else {
276
0
            let entry = self.files.entry(key);
277
0
            let index = entry.index();
278
0
            entry.or_default();
279
0
            index
280
        };
281
0
        FileId::new(index)
282
0
    }
283
284
    /// Get an iterator for the files.
285
0
    pub fn files(&self) -> impl Iterator<Item = (FileId, &LineString, DirectoryId)> {
286
0
        self.files
287
0
            .iter()
288
0
            .enumerate()
289
0
            .map(move |(index, entry)| (FileId::new(index), &(entry.0).0, (entry.0).1))
290
0
    }
291
292
    /// Get a reference to a file entry.
293
    ///
294
    /// # Panics
295
    ///
296
    /// Panics if `id` is invalid.
297
0
    pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) {
298
0
        self.files
299
0
            .get_index(id.index())
300
0
            .map(|entry| (&(entry.0).0, (entry.0).1))
301
0
            .unwrap()
302
0
    }
303
304
    /// Get a reference to the info for a file entry.
305
    ///
306
    /// # Panics
307
    ///
308
    /// Panics if `id` is invalid.
309
0
    pub fn get_file_info(&self, id: FileId) -> &FileInfo {
310
0
        self.files
311
0
            .get_index(id.index())
312
0
            .map(|entry| entry.1)
313
0
            .unwrap()
314
0
    }
315
316
    /// Get a mutable reference to the info for a file entry.
317
    ///
318
    /// # Panics
319
    ///
320
    /// Panics if `id` is invalid.
321
0
    pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo {
322
0
        self.files
323
0
            .get_index_mut(id.index())
324
0
            .map(|entry| entry.1)
325
0
            .unwrap()
326
0
    }
327
328
    /// Begin a new sequence and set its base address.
329
    ///
330
    /// It is not necessary to call this method, since all of the other methods
331
    /// will automatically begin a sequence if one has not already begun.
332
    /// However, it may be useful in rare cases where you need to begin a sequence
333
    /// while optionally setting the address.
334
    ///
335
    /// # Panics
336
    ///
337
    /// Panics if a sequence has already begun.
338
0
    pub fn begin_sequence(&mut self, address: Option<Address>) {
339
0
        assert!(!self.in_sequence);
340
0
        self.in_sequence = true;
341
0
        if let Some(address) = address {
342
0
            self.instructions.push(LineInstruction::SetAddress(address));
343
0
        }
344
0
    }
345
346
    /// Set the address for the next row.
347
    ///
348
    /// This begins a sequence if one has not already begun.
349
    ///
350
    /// This does not begin a new sequence if one has already begun. Hence, the caller must
351
    /// ensure that this address is greater than or equal to the address of the previous row.
352
0
    pub fn set_address(&mut self, address: Address) {
353
0
        self.in_sequence = true;
354
0
        self.instructions.push(LineInstruction::SetAddress(address));
355
0
    }
356
357
    /// End the sequence, and reset the row to its default values.
358
    ///
359
    /// Only the `address_offset` and op_index` fields of the current row are used.
360
    ///
361
    /// This will emit an `EndSequence` instruction even if a sequence has not begun.
362
0
    pub fn end_sequence(&mut self, address_offset: u64) {
363
0
        self.in_sequence = false;
364
0
        self.row.address_offset = address_offset;
365
0
        let op_advance = self.op_advance();
366
0
        if op_advance != 0 {
367
0
            self.instructions
368
0
                .push(LineInstruction::AdvancePc(op_advance));
369
0
        }
370
0
        self.instructions.push(LineInstruction::EndSequence);
371
0
        self.prev_row = LineRow::initial_state(self.encoding, self.line_encoding);
372
0
        self.row = LineRow::initial_state(self.encoding, self.line_encoding);
373
0
    }
374
375
    /// Return true if a sequence has begun.
376
    #[inline]
377
    pub fn in_sequence(&self) -> bool {
378
        self.in_sequence
379
    }
380
381
    /// Returns a reference to the data for the current row.
382
    #[inline]
383
    pub fn row(&mut self) -> &mut LineRow {
384
        &mut self.row
385
    }
386
387
    /// Generates the line number information instructions for the current row.
388
    ///
389
    /// After the instructions are generated, it sets `discriminator` to 0, and sets
390
    /// `basic_block`, `prologue_end`, and `epilogue_begin` to false.
391
    ///
392
    /// This begins a sequence if one has not already begun.
393
    ///
394
    /// # Panics
395
    ///
396
    /// Panics if the address_offset decreases.
397
0
    pub fn generate_row(&mut self) {
398
0
        self.in_sequence = true;
399
        // Output fields that are reset on every row.
400
0
        if self.row.discriminator != 0 {
401
0
            self.instructions
402
0
                .push(LineInstruction::SetDiscriminator(self.row.discriminator));
403
0
            self.row.discriminator = 0;
404
0
        }
405
0
        if self.row.basic_block {
406
0
            self.instructions.push(LineInstruction::SetBasicBlock);
407
0
            self.row.basic_block = false;
408
0
        }
409
0
        if self.row.prologue_end {
410
0
            self.instructions.push(LineInstruction::SetPrologueEnd);
411
0
            self.row.prologue_end = false;
412
0
        }
413
0
        if self.row.epilogue_begin {
414
0
            self.instructions.push(LineInstruction::SetEpilogueBegin);
415
0
            self.row.epilogue_begin = false;
416
0
        }
417
418
        // Output fields that are not reset on every row.
419
0
        if self.row.is_statement != self.prev_row.is_statement {
420
0
            self.instructions.push(LineInstruction::NegateStatement);
421
0
        }
422
0
        if self.row.file != self.prev_row.file {
423
0
            self.instructions
424
0
                .push(LineInstruction::SetFile(self.row.file));
425
0
        }
426
0
        if self.row.column != self.prev_row.column {
427
0
            self.instructions
428
0
                .push(LineInstruction::SetColumn(self.row.column));
429
0
        }
430
0
        if self.row.isa != self.prev_row.isa {
431
0
            self.instructions
432
0
                .push(LineInstruction::SetIsa(self.row.isa));
433
0
        }
434
435
        // Advance the line, address, and operation index.
436
0
        let line_base = i64::from(self.line_encoding.line_base) as u64;
437
0
        let line_range = u64::from(self.line_encoding.line_range);
438
0
        let line_advance = self.row.line as i64 - self.prev_row.line as i64;
439
0
        let op_advance = self.op_advance();
440
441
        // Default to special advances of 0.
442
0
        let special_base = u64::from(OPCODE_BASE);
443
        // TODO: handle lack of special opcodes for 0 line advance
444
0
        debug_assert!(self.line_encoding.line_base <= 0);
445
0
        debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0);
446
0
        let special_default = special_base.wrapping_sub(line_base);
447
0
        let mut special = special_default;
448
0
        let mut use_special = false;
449
450
0
        if line_advance != 0 {
451
0
            let special_line = (line_advance as u64).wrapping_sub(line_base);
452
0
            if special_line < line_range {
453
0
                special = special_base + special_line;
454
0
                use_special = true;
455
0
            } else {
456
0
                self.instructions
457
0
                    .push(LineInstruction::AdvanceLine(line_advance));
458
0
            }
459
0
        }
460
461
0
        if op_advance != 0 {
462
            // Using ConstAddPc can save a byte.
463
0
            let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 {
464
0
                (op_advance, false)
465
            } else {
466
0
                let op_range = (255 - special_base) / line_range;
467
0
                (op_advance - op_range, true)
468
            };
469
470
0
            let special_op = special_op_advance * line_range;
471
0
            if special + special_op <= 255 {
472
0
                special += special_op;
473
0
                use_special = true;
474
0
                if const_add_pc {
475
0
                    self.instructions.push(LineInstruction::ConstAddPc);
476
0
                }
477
0
            } else {
478
0
                self.instructions
479
0
                    .push(LineInstruction::AdvancePc(op_advance));
480
0
            }
481
0
        }
482
483
0
        if use_special && special != special_default {
484
0
            debug_assert!(special >= special_base);
485
0
            debug_assert!(special <= 255);
486
0
            self.instructions
487
0
                .push(LineInstruction::Special(special as u8));
488
0
        } else {
489
0
            self.instructions.push(LineInstruction::Copy);
490
0
        }
491
492
0
        self.prev_row = self.row;
493
0
    }
494
495
0
    fn op_advance(&self) -> u64 {
496
0
        debug_assert!(self.row.address_offset >= self.prev_row.address_offset);
497
0
        let mut address_advance = self.row.address_offset - self.prev_row.address_offset;
498
0
        if self.line_encoding.minimum_instruction_length != 1 {
499
0
            debug_assert_eq!(
500
0
                self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length),
501
                0
502
            );
503
0
            address_advance /= u64::from(self.line_encoding.minimum_instruction_length);
504
0
        }
505
0
        address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction)
506
0
            + self.row.op_index
507
0
            - self.prev_row.op_index
508
0
    }
509
510
    /// Returns true if the line number program has no instructions.
511
    ///
512
    /// Does not check the file or directory entries.
513
    #[inline]
514
0
    pub fn is_empty(&self) -> bool {
515
0
        self.instructions.is_empty()
516
0
    }
517
518
    /// Write the line number program to the given section.
519
    ///
520
    /// `encoding` contains the encoding parameters of the associated compilation unit.
521
    /// This is not used for writing the line number program, but is used to check for
522
    /// compatibility with [`Self::encoding`].
523
    ///
524
    /// # Panics
525
    ///
526
    /// Panics if `self.is_none()`.
527
    pub fn write<W: Writer>(
528
        &self,
529
        w: &mut DebugLine<W>,
530
        encoding: Encoding,
531
        line_strings: &mut LineStringTable,
532
        strings: &mut StringTable,
533
    ) -> Result<DebugLineOffset> {
534
        assert!(!self.is_none());
535
536
        // `.debug_line` and `.debug_info` may use different versions, but version 5
537
        // allows a file index of 0, which is not allowed in earlier versions.
538
        //
539
        // Also check that address size matches since there is no reason for it to differ.
540
        if encoding.version < 5 && self.version() >= 5
541
            || encoding.address_size != self.address_size()
542
        {
543
            return Err(Error::IncompatibleLineProgramEncoding);
544
        }
545
546
        let offset = w.offset();
547
548
        let length_offset = w.write_initial_length(self.format())?;
549
        let length_base = w.len();
550
551
        if self.version() < 2 || self.version() > 5 {
552
            return Err(Error::UnsupportedVersion(self.version()));
553
        }
554
        w.write_u16(self.version())?;
555
556
        if self.version() >= 5 {
557
            w.write_u8(self.address_size())?;
558
            // Segment selector size.
559
            w.write_u8(0)?;
560
        }
561
562
        let header_length_offset = w.len();
563
        w.write_udata(0, self.format().word_size())?;
564
        let header_length_base = w.len();
565
566
        w.write_u8(self.line_encoding.minimum_instruction_length)?;
567
        if self.version() >= 4 {
568
            w.write_u8(self.line_encoding.maximum_operations_per_instruction)?;
569
        } else if self.line_encoding.maximum_operations_per_instruction != 1 {
570
            return Err(Error::NeedVersion(4));
571
        };
572
        w.write_u8(if self.line_encoding.default_is_stmt {
573
            1
574
        } else {
575
            0
576
        })?;
577
        w.write_u8(self.line_encoding.line_base as u8)?;
578
        w.write_u8(self.line_encoding.line_range)?;
579
        w.write_u8(OPCODE_BASE)?;
580
        w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?;
581
582
        if self.version() <= 4 {
583
            // The first directory is stored as DW_AT_comp_dir.
584
            for dir in self.directories.iter().skip(1) {
585
                dir.write(
586
                    w,
587
                    constants::DW_FORM_string,
588
                    self.encoding,
589
                    line_strings,
590
                    strings,
591
                )?;
592
            }
593
            w.write_u8(0)?;
594
595
            for ((file, dir), info) in self.files.iter() {
596
                file.write(
597
                    w,
598
                    constants::DW_FORM_string,
599
                    self.encoding,
600
                    line_strings,
601
                    strings,
602
                )?;
603
                w.write_uleb128(dir.0 as u64)?;
604
                w.write_uleb128(info.timestamp)?;
605
                w.write_uleb128(info.size)?;
606
            }
607
            w.write_u8(0)?;
608
        } else {
609
            // Directory entry formats (only ever 1).
610
            w.write_u8(1)?;
611
            w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
612
            let dir_form = self.directories.get_index(0).unwrap().form();
613
            w.write_uleb128(dir_form.0.into())?;
614
615
            // Directory entries.
616
            w.write_uleb128(self.directories.len() as u64)?;
617
            for dir in self.directories.iter() {
618
                dir.write(w, dir_form, self.encoding, line_strings, strings)?;
619
            }
620
621
            // File name entry formats.
622
            let count = 2
623
                + if self.file_has_timestamp { 1 } else { 0 }
624
                + if self.file_has_size { 1 } else { 0 }
625
                + if self.file_has_md5 { 1 } else { 0 }
626
                + if self.file_has_source { 1 } else { 0 };
627
            w.write_u8(count)?;
628
            w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
629
            let file_form = (self.files.get_index(0).unwrap().0).0.form();
630
            w.write_uleb128(file_form.0.into())?;
631
            w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?;
632
            w.write_uleb128(constants::DW_FORM_udata.0.into())?;
633
            if self.file_has_timestamp {
634
                w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?;
635
                w.write_uleb128(constants::DW_FORM_udata.0.into())?;
636
            }
637
            if self.file_has_size {
638
                w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?;
639
                w.write_uleb128(constants::DW_FORM_udata.0.into())?;
640
            }
641
            if self.file_has_md5 {
642
                w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?;
643
                w.write_uleb128(constants::DW_FORM_data16.0.into())?;
644
            }
645
            let file_source_form = self
646
                .files
647
                .iter()
648
                .find_map(|file| file.1.source.as_ref().map(LineString::form))
649
                .unwrap_or(constants::DW_FORM_string);
650
            if self.file_has_source {
651
                w.write_uleb128(u64::from(constants::DW_LNCT_LLVM_source.0))?;
652
                w.write_uleb128(file_source_form.0.into())?;
653
            }
654
655
            // File name entries.
656
            w.write_uleb128(self.files.len() as u64)?;
657
            let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| {
658
                file.write(w, file_form, self.encoding, line_strings, strings)?;
659
                w.write_uleb128(dir.0 as u64)?;
660
                if self.file_has_timestamp {
661
                    w.write_uleb128(info.timestamp)?;
662
                }
663
                if self.file_has_size {
664
                    w.write_uleb128(info.size)?;
665
                }
666
                if self.file_has_md5 {
667
                    w.write(&info.md5)?;
668
                }
669
                if self.file_has_source {
670
                    if let Some(source) = info.source.as_ref() {
671
                        source.write(w, file_source_form, self.encoding, line_strings, strings)?;
672
                    } else {
673
                        // Create a string to use for files with no source.
674
                        // Note: An empty DW_LNCT_LLVM_source is interpreted as missing
675
                        // source code. Included source code should always be
676
                        // terminated by a "\n" line ending.
677
                        let source = match file_source_form {
678
                            constants::DW_FORM_line_strp => {
679
                                LineString::LineStringRef(line_strings.add([]))
680
                            }
681
                            constants::DW_FORM_strp => LineString::StringRef(strings.add([])),
682
                            _ => LineString::String(Vec::new()),
683
                        };
684
                        source.write(w, file_source_form, self.encoding, line_strings, strings)?;
685
                    }
686
                }
687
                Ok(())
688
            };
689
            for ((file, dir), info) in self.files.iter() {
690
                write_file(file, *dir, info)?;
691
            }
692
        }
693
694
        let header_length = (w.len() - header_length_base) as u64;
695
        w.write_udata_at(
696
            header_length_offset,
697
            header_length,
698
            self.format().word_size(),
699
        )?;
700
701
        for instruction in &self.instructions {
702
            instruction.write(w, self.encoding)?;
703
        }
704
705
        let length = (w.len() - length_base) as u64;
706
        w.write_initial_length_at(length_offset, length, self.format())?;
707
708
        Ok(offset)
709
    }
710
}
711
712
/// A row in the line number table that corresponds to a machine instruction.
713
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
714
pub struct LineRow {
715
    /// The offset of the instruction from the start address of the sequence.
716
    pub address_offset: u64,
717
    /// The index of an operation within a VLIW instruction.
718
    ///
719
    /// The index of the first operation is 0.
720
    /// Set to 0 for non-VLIW instructions.
721
    pub op_index: u64,
722
723
    /// The source file corresponding to the instruction.
724
    pub file: FileId,
725
    /// The line number within the source file.
726
    ///
727
    /// Lines are numbered beginning at 1. Set to 0 if there is no source line.
728
    pub line: u64,
729
    /// The column number within the source line.
730
    ///
731
    /// Columns are numbered beginning at 1. Set to 0 for the "left edge" of the line.
732
    pub column: u64,
733
    /// An additional discriminator used to distinguish between source locations.
734
    /// This value is assigned arbitrarily by the DWARF producer.
735
    pub discriminator: u64,
736
737
    /// Set to true if the instruction is a recommended breakpoint for a statement.
738
    pub is_statement: bool,
739
    /// Set to true if the instruction is the beginning of a basic block.
740
    pub basic_block: bool,
741
    /// Set to true if the instruction is a recommended breakpoint at the entry of a
742
    /// function.
743
    pub prologue_end: bool,
744
    /// Set to true if the instruction is a recommended breakpoint prior to the exit of
745
    /// a function.
746
    pub epilogue_begin: bool,
747
748
    /// The instruction set architecture of the instruction.
749
    ///
750
    /// Set to 0 for the default ISA. Other values are defined by the architecture ABI.
751
    pub isa: u64,
752
}
753
754
impl LineRow {
755
    /// Return the initial state as specified in the DWARF standard.
756
0
    fn initial_state(encoding: Encoding, line_encoding: LineEncoding) -> Self {
757
0
        LineRow {
758
0
            address_offset: 0,
759
0
            op_index: 0,
760
0
761
0
            file: FileId::initial_state(encoding.version),
762
0
            line: 1,
763
0
            column: 0,
764
0
            discriminator: 0,
765
0
766
0
            is_statement: line_encoding.default_is_stmt,
767
0
            basic_block: false,
768
0
            prologue_end: false,
769
0
            epilogue_begin: false,
770
0
771
0
            isa: 0,
772
0
        }
773
0
    }
774
}
775
776
/// An instruction in a line number program.
777
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
778
enum LineInstruction {
779
    // Special opcodes
780
    Special(u8),
781
782
    // Standard opcodes
783
    Copy,
784
    AdvancePc(u64),
785
    AdvanceLine(i64),
786
    SetFile(FileId),
787
    SetColumn(u64),
788
    NegateStatement,
789
    SetBasicBlock,
790
    ConstAddPc,
791
    // DW_LNS_fixed_advance_pc is not supported.
792
    SetPrologueEnd,
793
    SetEpilogueBegin,
794
    SetIsa(u64),
795
796
    // Extended opcodes
797
    EndSequence,
798
    // TODO: this doubles the size of this enum.
799
    SetAddress(Address),
800
    // DW_LNE_define_file is not supported.
801
    SetDiscriminator(u64),
802
}
803
804
impl LineInstruction {
805
    /// Write the line number instruction to the given section.
806
    fn write<W: Writer>(self, w: &mut DebugLine<W>, encoding: Encoding) -> Result<()> {
807
        use self::LineInstruction::*;
808
        match self {
809
            Special(val) => w.write_u8(val)?,
810
            Copy => w.write_u8(constants::DW_LNS_copy.0)?,
811
            AdvancePc(val) => {
812
                w.write_u8(constants::DW_LNS_advance_pc.0)?;
813
                w.write_uleb128(val)?;
814
            }
815
            AdvanceLine(val) => {
816
                w.write_u8(constants::DW_LNS_advance_line.0)?;
817
                w.write_sleb128(val)?;
818
            }
819
            SetFile(val) => {
820
                w.write_u8(constants::DW_LNS_set_file.0)?;
821
                w.write_uleb128(val.raw(encoding.version))?;
822
            }
823
            SetColumn(val) => {
824
                w.write_u8(constants::DW_LNS_set_column.0)?;
825
                w.write_uleb128(val)?;
826
            }
827
            NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?,
828
            SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?,
829
            ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?,
830
            SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?,
831
            SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?,
832
            SetIsa(val) => {
833
                w.write_u8(constants::DW_LNS_set_isa.0)?;
834
                w.write_uleb128(val)?;
835
            }
836
            EndSequence => {
837
                w.write_u8(0)?;
838
                w.write_uleb128(1)?;
839
                w.write_u8(constants::DW_LNE_end_sequence.0)?;
840
            }
841
            SetAddress(address) => {
842
                w.write_u8(0)?;
843
                w.write_uleb128(1 + u64::from(encoding.address_size))?;
844
                w.write_u8(constants::DW_LNE_set_address.0)?;
845
                w.write_address(address, encoding.address_size)?;
846
            }
847
            SetDiscriminator(val) => {
848
                let val = Leb128::unsigned(val);
849
                w.write_u8(0)?;
850
                w.write_uleb128(1 + val.len() as u64)?;
851
                w.write_u8(constants::DW_LNE_set_discriminator.0)?;
852
                w.write(val.bytes())?;
853
            }
854
        }
855
        Ok(())
856
    }
857
}
858
859
/// A string value for use in defining paths in line number programs.
860
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
861
pub enum LineString {
862
    /// A slice of bytes representing a string. Must not include null bytes.
863
    /// Not guaranteed to be UTF-8 or anything like that.
864
    String(Vec<u8>),
865
866
    /// A reference to a string in the `.debug_str` section.
867
    StringRef(StringId),
868
869
    /// A reference to a string in the `.debug_line_str` section.
870
    LineStringRef(LineStringId),
871
}
872
873
impl LineString {
874
    /// Create a `LineString` using the normal form for the given encoding.
875
    pub fn new<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self
876
    where
877
        T: Into<Vec<u8>>,
878
    {
879
        let val = val.into();
880
        if encoding.version <= 4 {
881
            LineString::String(val)
882
        } else {
883
            LineString::LineStringRef(line_strings.add(val))
884
        }
885
    }
886
887
    /// Get a reference to the string data.
888
0
    pub fn get<'a>(
889
0
        &'a self,
890
0
        strings: &'a StringTable,
891
0
        line_strings: &'a LineStringTable,
892
0
    ) -> &'a [u8] {
893
0
        match self {
894
0
            LineString::String(val) => val,
895
0
            LineString::StringRef(val) => strings.get(*val),
896
0
            LineString::LineStringRef(val) => line_strings.get(*val),
897
        }
898
0
    }
899
900
0
    fn form(&self) -> constants::DwForm {
901
0
        match *self {
902
0
            LineString::String(..) => constants::DW_FORM_string,
903
0
            LineString::StringRef(..) => constants::DW_FORM_strp,
904
0
            LineString::LineStringRef(..) => constants::DW_FORM_line_strp,
905
        }
906
0
    }
907
908
    fn write<W: Writer>(
909
        &self,
910
        w: &mut DebugLine<W>,
911
        form: constants::DwForm,
912
        encoding: Encoding,
913
        line_strings: &LineStringTable,
914
        strings: &StringTable,
915
    ) -> Result<()> {
916
        if form != self.form() {
917
            return Err(Error::LineStringFormMismatch);
918
        }
919
920
        match *self {
921
            LineString::String(ref val) => {
922
                if encoding.version <= 4 {
923
                    debug_assert!(!val.is_empty());
924
                }
925
                w.write(val)?;
926
                w.write_u8(0)?;
927
            }
928
            LineString::StringRef(val) => {
929
                if encoding.version < 5 {
930
                    return Err(Error::NeedVersion(5));
931
                }
932
                w.write_offset(
933
                    strings.offset(val).0,
934
                    SectionId::DebugStr,
935
                    encoding.format.word_size(),
936
                )?;
937
            }
938
            LineString::LineStringRef(val) => {
939
                if encoding.version < 5 {
940
                    return Err(Error::NeedVersion(5));
941
                }
942
                w.write_offset(
943
                    line_strings.offset(val).0,
944
                    SectionId::DebugLineStr,
945
                    encoding.format.word_size(),
946
                )?;
947
            }
948
        }
949
        Ok(())
950
    }
951
}
952
953
/// An identifier for a directory in a `LineProgram`.
954
///
955
/// Defaults to the working directory of the compilation unit.
956
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
957
pub struct DirectoryId(usize);
958
959
// Force FileId access via the methods.
960
mod id {
961
    /// An identifier for a file in a `LineProgram`.
962
    //
963
    // We internally use a 0-based index for all versions, but
964
    // emit a 1-based index for DWARF version <= 4.
965
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
966
    pub struct FileId(usize);
967
968
    impl FileId {
969
        /// Create a `FileId` given a 0-based index into `LineProgram::files`.
970
0
        pub(crate) fn new(index: usize) -> Self {
971
0
            FileId(index)
972
0
        }
973
974
        /// The 0-based index of the file in `LineProgram::files`.
975
0
        pub(super) fn index(self) -> usize {
976
0
            self.0
977
0
        }
978
979
        /// The initial state of the file register.
980
0
        pub(super) fn initial_state(version: u16) -> Self {
981
0
            if version == 5 {
982
                // For version 5, the files are 0-based and the default file is 1,
983
                // which is a 0-based index of 1.
984
0
                FileId(1)
985
            } else {
986
                // For version <= 4, the files are 1-based and the default file is 1,
987
                // which is a 0-based index of 0.
988
                // For version >= 6, the files are 0-based and the default file is 0,
989
                // which is a 0-based index of 0.
990
0
                FileId(0)
991
            }
992
0
        }
993
994
        /// Convert to a raw value used for writing.
995
        ///
996
        /// This converts to a 1-based index for DWARF version <= 4.
997
0
        pub(crate) fn raw(self, version: u16) -> u64 {
998
0
            if version <= 4 {
999
0
                self.0 as u64 + 1
1000
            } else {
1001
0
                self.0 as u64
1002
            }
1003
0
        }
1004
    }
1005
}
1006
pub use self::id::*;
1007
1008
/// Extra information for file in a `LineProgram`.
1009
#[derive(Debug, Default, Clone, PartialEq, Eq)]
1010
pub struct FileInfo {
1011
    /// The implementation defined timestamp of the last modification of the file,
1012
    /// or 0 if not available.
1013
    pub timestamp: u64,
1014
1015
    /// The size of the file in bytes, or 0 if not available.
1016
    pub size: u64,
1017
1018
    /// A 16-byte MD5 digest of the file contents.
1019
    ///
1020
    /// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`.
1021
    pub md5: [u8; 16],
1022
1023
    /// Optionally some embedded sourcecode.
1024
    ///
1025
    /// Only used if version >= 5 and `LineProgram::file_has_source` is `true`.
1026
    pub source: Option<LineString>,
1027
}
1028
1029
define_section!(
1030
    DebugLine,
1031
    DebugLineOffset,
1032
    "A writable `.debug_line` section."
1033
);
1034
1035
#[cfg(feature = "read")]
1036
pub use convert::*;
1037
#[cfg(feature = "read")]
1038
mod convert {
1039
    use super::*;
1040
    use crate::read::{self, Reader};
1041
    use crate::write::{self, ConvertError, ConvertResult};
1042
1043
    /// The result of [`ConvertLineProgram::read_row`].
1044
    #[derive(Debug, PartialEq, Eq)]
1045
    pub enum ConvertLineRow {
1046
        /// The address from a `DW_LNE_set_address` instruction.
1047
        ///
1048
        /// All subsequent rows in the sequence will have their `address_offset`
1049
        /// field set to an offset from this address.
1050
        SetAddress(u64),
1051
        /// A row produced by the line number program.
1052
        Row(LineRow),
1053
        /// The address offset of the end of the sequence.
1054
        EndSequence(u64),
1055
    }
1056
1057
    /// The result of [`ConvertLineProgram::read_sequence`].
1058
    #[derive(Debug, PartialEq, Eq)]
1059
    pub struct ConvertLineSequence {
1060
        /// The address of the first instruction in the given rows.
1061
        ///
1062
        /// This will be `None` if there was no `DW_LNE_set_address` instruction.
1063
        pub start: Option<u64>,
1064
        /// The location of the next instruction after the given rows.
1065
        pub end: ConvertLineSequenceEnd,
1066
        /// The rows in the sequence.
1067
        ///
1068
        /// The `LineRow::address` fields are set to an offset from `Self::start`.
1069
        pub rows: Vec<LineRow>,
1070
    }
1071
1072
    /// The location of the next instruction after a sequence of rows.
1073
    #[derive(Debug, PartialEq, Eq)]
1074
    pub enum ConvertLineSequenceEnd {
1075
        /// An offset in bytes from [`ConvertLineSequence::start`].
1076
        Length(u64),
1077
        /// An address set by a `DW_LNE_set_address` instruction.
1078
        Address(u64),
1079
    }
1080
1081
    /// The next action to take in the conversion of a line number program.
1082
    #[derive(Debug)]
1083
    enum ConvertLineState {
1084
        /// Read and evaluate instructions for the next row.
1085
        ReadRow,
1086
        /// Return `self.address` then switch to `ConvertRow`.
1087
        SetAddress,
1088
        /// Convert `self.from_row` to a `LineRow`.
1089
        ///
1090
        /// If this state occurred after a `SetAddress`, `self.address` is still the
1091
        /// address that was set.
1092
        ConvertRow,
1093
    }
1094
1095
    /// The state for the conversion of a line number program.
1096
    ///
1097
    /// After calling [`Dwarf::read_line_program`](crate::write::Dwarf::read_line_program),
1098
    /// or [`ConvertUnit::read_line_program`](write::ConvertUnit::read_line_program),
1099
    /// you may call either [`ConvertLineProgram::read_row`] to read the next row, or
1100
    /// [`ConvertLineProgram::read_sequence`] to read a sequence of rows.
1101
    ///
1102
    /// If desired, you may transform the rows that are read. For example, it may be useful
1103
    /// to modify the addresses of the rows to match a corresponding transformation of the
1104
    /// machine instructions.
1105
    ///
1106
    /// Next, you may call [`ConvertLineProgram::set_address`], [`ConvertLineProgram::generate_row`],
1107
    /// [`ConvertLineProgram::end_sequence`] to update the converted line number program.
1108
    ///
1109
    /// Once all rows have been converted, you may call [`ConvertLineProgram::program`] to
1110
    /// obtain the converted program and a mapping for file indices.
1111
    ///
1112
    /// ## Example Usage
1113
    ///
1114
    /// Convert a line program using [`ConvertLineProgram::read_row`].
1115
    ///
1116
    /// ```rust,no_run
1117
    /// use gimli::write::{Address, ConvertLineProgram, ConvertLineRow};
1118
    /// # fn example() -> Result<(), gimli::write::ConvertError> {
1119
    /// #    type Reader = gimli::read::EndianSlice<'static, gimli::LittleEndian>;
1120
    /// #    let from_program: gimli::read::IncompleteLineProgram<Reader> = unimplemented!();
1121
    /// #    let from_dwarf: gimli::read::Dwarf<_> = unimplemented!();
1122
    /// let mut dwarf = gimli::write::Dwarf::new();
1123
    /// // Start the conversion. This will convert the header, directories and files.
1124
    /// let mut convert = dwarf.read_line_program(
1125
    ///     &from_dwarf,
1126
    ///     from_program,
1127
    ///     // Use the original encodings.
1128
    ///     None,
1129
    ///     None,
1130
    /// )?;
1131
    /// // Read and convert each row in the program.
1132
    /// while let Some(row) = convert.read_row()? {
1133
    ///     match row {
1134
    ///         ConvertLineRow::SetAddress(address) => {
1135
    ///             convert.set_address(Address::Constant(address));
1136
    ///         }
1137
    ///         ConvertLineRow::Row(row) => {
1138
    ///             convert.generate_row(row);
1139
    ///         }
1140
    ///         ConvertLineRow::EndSequence(length) => {
1141
    ///             convert.end_sequence(length);
1142
    ///         }
1143
    ///     }
1144
    /// }
1145
    /// if convert.in_sequence() {
1146
    ///    // The sequence was never ended. This is invalid DWARF.
1147
    ///    return Err(gimli::write::ConvertError::MissingLineEndSequence);
1148
    /// }
1149
    /// let (program, files) = convert.program();
1150
    /// # Ok(())
1151
    /// # }
1152
    /// ```
1153
    #[derive(Debug)]
1154
    pub struct ConvertLineProgram<'a, R: Reader> {
1155
        from_dwarf: &'a read::Dwarf<R>,
1156
        from_program: read::IncompleteLineProgram<R>,
1157
        from_row: read::LineRow,
1158
        from_instructions: read::LineInstructions<R>,
1159
        files: Vec<FileId>,
1160
        dirs: Vec<DirectoryId>,
1161
        program: LineProgram,
1162
        line_strings: &'a mut write::LineStringTable,
1163
        #[allow(unused)] // May need LineString::StringRef in future.
1164
        strings: &'a mut write::StringTable,
1165
        address: Option<u64>,
1166
        state: ConvertLineState,
1167
    }
1168
1169
    impl<'a, R: Reader + 'a> ConvertLineProgram<'a, R> {
1170
        pub(crate) fn new(
1171
            from_dwarf: &'a read::Dwarf<R>,
1172
            from_program: read::IncompleteLineProgram<R>,
1173
            mut from_comp_name: Option<R>,
1174
            encoding: Option<Encoding>,
1175
            line_encoding: Option<LineEncoding>,
1176
            line_strings: &'a mut write::LineStringTable,
1177
            strings: &'a mut write::StringTable,
1178
        ) -> ConvertResult<Self> {
1179
            let encoding = encoding.unwrap_or(from_program.header().encoding());
1180
            let line_encoding = line_encoding.unwrap_or(from_program.header().line_encoding());
1181
1182
            // Create mappings in case the source has duplicate files or directories.
1183
            let mut dirs = Vec::new();
1184
            let mut files = Vec::new();
1185
1186
            let from_header = from_program.header();
1187
1188
            let working_dir = if let Some(from_working_dir) = from_header.directory(0) {
1189
                Self::convert_string(from_working_dir, from_dwarf, encoding, line_strings)?
1190
            } else if encoding.version <= 4 {
1191
                // This value won't be emitted, but we need to provide something.
1192
                LineString::String(Vec::new())
1193
            } else {
1194
                return Err(ConvertError::MissingCompilationDirectory);
1195
            };
1196
1197
            // `from_comp_name` is only needed for split DWARF version 4.
1198
            if from_header.version() >= 5 {
1199
                from_comp_name = None;
1200
            }
1201
            let (source_dir, source_file) = if let Some(from_comp_name) = from_comp_name {
1202
                (
1203
                    None,
1204
                    LineString::new(from_comp_name.to_slice()?, encoding, line_strings),
1205
                )
1206
            } else if let Some(from_source_file) = from_header.file(0) {
1207
                let dir_index = from_source_file.directory_index();
1208
                let source_dir = if dir_index != 0 {
1209
                    match from_header.directory(dir_index) {
1210
                        Some(from_source_dir) => Some(Self::convert_string(
1211
                            from_source_dir,
1212
                            from_dwarf,
1213
                            encoding,
1214
                            line_strings,
1215
                        )?),
1216
                        None => return Err(ConvertError::InvalidDirectoryIndex),
1217
                    }
1218
                } else {
1219
                    // Source dir matches working dir.
1220
                    None
1221
                };
1222
                let source_file = Self::convert_string(
1223
                    from_source_file.path_name(),
1224
                    from_dwarf,
1225
                    encoding,
1226
                    line_strings,
1227
                )?;
1228
                (source_dir, source_file)
1229
            } else if encoding.version <= 4 {
1230
                // This value won't be emitted, but we need to provide something.
1231
                (None, LineString::String(Vec::new()))
1232
            } else {
1233
                return Err(ConvertError::MissingCompilationName);
1234
            };
1235
1236
            if from_header.line_base() > 0 {
1237
                return Err(ConvertError::InvalidLineBase);
1238
            }
1239
            let mut program = LineProgram::new(
1240
                encoding,
1241
                line_encoding,
1242
                working_dir,
1243
                source_dir,
1244
                source_file,
1245
                None, // We'll set this later if needed when we add the file again.
1246
            );
1247
1248
            if from_header.version() <= 4 {
1249
                // The first directory is implicit.
1250
                dirs.push(DirectoryId(0));
1251
                // A file index of 0 is invalid for version <= 4, but putting
1252
                // something there makes the indexing easier.
1253
                files.push(FileId::new(0));
1254
            }
1255
1256
            for from_attr in from_header.include_directories() {
1257
                let from_dir =
1258
                    Self::convert_string(from_attr.clone(), from_dwarf, encoding, line_strings)?;
1259
                dirs.push(program.add_directory(from_dir));
1260
            }
1261
1262
            program.file_has_timestamp = from_header.file_has_timestamp();
1263
            program.file_has_size = from_header.file_has_size();
1264
            program.file_has_md5 = from_header.file_has_md5();
1265
            program.file_has_source = from_header.file_has_source();
1266
            for from_file in from_header.file_names() {
1267
                let (from_name, from_dir, from_info) =
1268
                    Self::convert_file(from_file, from_dwarf, &dirs, encoding, line_strings)?;
1269
                files.push(program.add_file(from_name, from_dir, from_info));
1270
            }
1271
1272
            // We can't use the `from_program.rows()` because that wouldn't let
1273
            // us preserve address relocations.
1274
            let from_row = read::LineRow::new(from_program.header());
1275
            let from_instructions = from_program.header().instructions();
1276
            Ok(ConvertLineProgram {
1277
                from_dwarf,
1278
                from_program,
1279
                from_row,
1280
                from_instructions,
1281
                files,
1282
                dirs,
1283
                program,
1284
                line_strings,
1285
                strings,
1286
                address: None,
1287
                state: ConvertLineState::ReadRow,
1288
            })
1289
        }
1290
1291
        fn convert_string(
1292
            from_attr: read::AttributeValue<R>,
1293
            from_dwarf: &read::Dwarf<R>,
1294
            encoding: Encoding,
1295
            line_strings: &mut write::LineStringTable,
1296
        ) -> ConvertResult<LineString> {
1297
            let r = from_dwarf.attr_line_string(from_attr)?;
1298
            Ok(LineString::new(r.to_slice()?, encoding, line_strings))
1299
        }
1300
1301
        fn convert_file(
1302
            from_file: &read::FileEntry<R>,
1303
            from_dwarf: &read::Dwarf<R>,
1304
            dirs: &[DirectoryId],
1305
            encoding: Encoding,
1306
            line_strings: &mut write::LineStringTable,
1307
        ) -> ConvertResult<(LineString, DirectoryId, Option<FileInfo>)> {
1308
            let from_name =
1309
                Self::convert_string(from_file.path_name(), from_dwarf, encoding, line_strings)?;
1310
            let from_dir = from_file.directory_index();
1311
            if from_dir >= dirs.len() as u64 {
1312
                return Err(ConvertError::InvalidDirectoryIndex);
1313
            }
1314
            let from_dir = dirs[from_dir as usize];
1315
            let from_info = Some(FileInfo {
1316
                timestamp: from_file.timestamp(),
1317
                size: from_file.size(),
1318
                md5: *from_file.md5(),
1319
                source: match from_file.source() {
1320
                    Some(source) => Some(Self::convert_string(
1321
                        source,
1322
                        from_dwarf,
1323
                        encoding,
1324
                        line_strings,
1325
                    )?),
1326
                    None => None,
1327
                },
1328
            });
1329
            Ok((from_name, from_dir, from_info))
1330
        }
1331
1332
        /// Read the next row from the source program.
1333
        ///
1334
        /// See [`ConvertLineProgram`] for an example of how to add the row to the converted program.
1335
        pub fn read_row(&mut self) -> ConvertResult<Option<ConvertLineRow>> {
1336
            match self.state {
1337
                ConvertLineState::ReadRow => {}
1338
                ConvertLineState::SetAddress => {
1339
                    if let Some(address) = self.address.take() {
1340
                        self.state = ConvertLineState::ConvertRow;
1341
                        return Ok(Some(ConvertLineRow::SetAddress(address)));
1342
                    }
1343
                    self.state = ConvertLineState::ReadRow;
1344
                    return Ok(Some(ConvertLineRow::Row(self.convert_row()?)));
1345
                }
1346
                ConvertLineState::ConvertRow => {
1347
                    self.state = ConvertLineState::ReadRow;
1348
                    return Ok(Some(ConvertLineRow::Row(self.convert_row()?)));
1349
                }
1350
            }
1351
            let mut tombstone = false;
1352
            self.address = None;
1353
            self.from_row.reset(self.from_program.header());
1354
            while let Some(instruction) = self
1355
                .from_instructions
1356
                .next_instruction(self.from_program.header())?
1357
            {
1358
                match instruction {
1359
                    read::LineInstruction::SetAddress(val) => {
1360
                        // Use address 0 so that all addresses are offsets.
1361
                        self.from_row.execute(
1362
                            read::LineInstruction::SetAddress(0),
1363
                            &mut self.from_program,
1364
                        )?;
1365
                        // Handle tombstones the same way that `from_row.execute` would have.
1366
                        let tombstone_address =
1367
                            !0 >> (64 - self.from_program.header().encoding().address_size * 8);
1368
                        tombstone = val == tombstone_address;
1369
                        if !tombstone {
1370
                            self.address = Some(val);
1371
                        }
1372
                        continue;
1373
                    }
1374
                    read::LineInstruction::DefineFile(ref from_file) => {
1375
                        let (from_name, from_dir, from_info) = Self::convert_file(
1376
                            from_file,
1377
                            self.from_dwarf,
1378
                            &self.dirs,
1379
                            self.program.encoding(),
1380
                            self.line_strings,
1381
                        )?;
1382
                        self.files
1383
                            .push(self.program.add_file(from_name, from_dir, from_info));
1384
                        continue;
1385
                    }
1386
                    _ => {}
1387
                }
1388
                if !self.from_row.execute(instruction, &mut self.from_program)? {
1389
                    // This instruction didn't generate a new row.
1390
                    continue;
1391
                }
1392
                if tombstone {
1393
                    // Perform any reset that was required for the tombstone row.
1394
                    // Normally this is done when `read_row` is called again, but for
1395
                    // tombstones we loop immediately.
1396
                    if self.from_row.end_sequence() {
1397
                        tombstone = false;
1398
                        self.address = None;
1399
                    }
1400
                    self.from_row.reset(self.from_program.header());
1401
                    continue;
1402
                }
1403
                if self.from_row.end_sequence() {
1404
                    return Ok(Some(ConvertLineRow::EndSequence(self.from_row.address())));
1405
                }
1406
                if let Some(address) = self.address.take() {
1407
                    self.state = ConvertLineState::ConvertRow;
1408
                    return Ok(Some(ConvertLineRow::SetAddress(address)));
1409
                } else {
1410
                    self.state = ConvertLineState::ReadRow;
1411
                    return Ok(Some(ConvertLineRow::Row(self.convert_row()?)));
1412
                }
1413
            }
1414
            Ok(None)
1415
        }
1416
1417
        fn convert_row(&self) -> ConvertResult<LineRow> {
1418
            Ok(LineRow {
1419
                address_offset: self.from_row.address(),
1420
                op_index: self.from_row.op_index(),
1421
                file: {
1422
                    let file = self.from_row.file_index();
1423
                    if file >= self.files.len() as u64 {
1424
                        return Err(ConvertError::InvalidFileIndex);
1425
                    }
1426
                    if file == 0 && self.from_program.header().version() <= 4 {
1427
                        return Err(ConvertError::InvalidFileIndex);
1428
                    }
1429
                    self.files[file as usize]
1430
                },
1431
                line: match self.from_row.line() {
1432
                    Some(line) => line.get(),
1433
                    None => 0,
1434
                },
1435
                column: match self.from_row.column() {
1436
                    read::ColumnType::LeftEdge => 0,
1437
                    read::ColumnType::Column(val) => val.get(),
1438
                },
1439
                discriminator: self.from_row.discriminator(),
1440
                is_statement: self.from_row.is_stmt(),
1441
                basic_block: self.from_row.basic_block(),
1442
                prologue_end: self.from_row.prologue_end(),
1443
                epilogue_begin: self.from_row.epilogue_begin(),
1444
                isa: self.from_row.isa(),
1445
            })
1446
        }
1447
1448
        /// Read the next sequence from the source program.
1449
        ///
1450
        /// This will read rows in the sequence up to either the end of the sequence,
1451
        /// or the next `DW_LNE_set_address` instruction.
1452
        ///
1453
        /// ## Example Usage
1454
        ///
1455
        /// ```rust,no_run
1456
        /// # use gimli::write::{Address, ConvertLineProgram, ConvertLineSequenceEnd};
1457
        /// # fn example<R: gimli::Reader>(mut convert: ConvertLineProgram<R>) -> Result<(), gimli::write::ConvertError> {
1458
        /// // Read and convert each sequence in the program.
1459
        /// while let Some(sequence) = convert.read_sequence()? {
1460
        ///     if let Some(start) = sequence.start {
1461
        ///         convert.set_address(Address::Constant(start));
1462
        ///     }
1463
        ///     for row in sequence.rows {
1464
        ///         convert.generate_row(row);
1465
        ///     }
1466
        ///     if let ConvertLineSequenceEnd::Length(length) = sequence.end {
1467
        ///         convert.end_sequence(length);
1468
        ///     }
1469
        /// }
1470
        /// # Ok(())
1471
        /// # }
1472
        /// ```
1473
        pub fn read_sequence(&mut self) -> ConvertResult<Option<ConvertLineSequence>> {
1474
            let mut start = None;
1475
            let mut rows = Vec::new();
1476
            match self.state {
1477
                ConvertLineState::ReadRow => {}
1478
                ConvertLineState::SetAddress | ConvertLineState::ConvertRow => {
1479
                    start = self.address;
1480
                    rows.push(self.convert_row()?);
1481
                    self.state = ConvertLineState::ReadRow;
1482
                }
1483
            }
1484
            while let Some(row) = self.read_row()? {
1485
                match row {
1486
                    ConvertLineRow::SetAddress(address) => {
1487
                        if !rows.is_empty() {
1488
                            self.address = Some(address);
1489
                            self.state = ConvertLineState::SetAddress;
1490
                            return Ok(Some(ConvertLineSequence {
1491
                                start,
1492
                                end: ConvertLineSequenceEnd::Address(address),
1493
                                rows,
1494
                            }));
1495
                        }
1496
                        start = Some(address);
1497
                    }
1498
                    ConvertLineRow::Row(row) => {
1499
                        rows.push(row);
1500
                    }
1501
                    ConvertLineRow::EndSequence(length) => {
1502
                        return Ok(Some(ConvertLineSequence {
1503
                            start,
1504
                            end: ConvertLineSequenceEnd::Length(length),
1505
                            rows,
1506
                        }));
1507
                    }
1508
                }
1509
            }
1510
            if !rows.is_empty() {
1511
                return Err(ConvertError::MissingLineEndSequence);
1512
            }
1513
            Ok(None)
1514
        }
1515
1516
        /// Call [`LineProgram::begin_sequence`] for the converted program.
1517
        pub fn begin_sequence(&mut self, address: Option<Address>) {
1518
            self.program.begin_sequence(address);
1519
        }
1520
1521
        /// Call [`LineProgram::set_address`] for the converted program.
1522
        pub fn set_address(&mut self, address: Address) {
1523
            self.program.set_address(address);
1524
        }
1525
1526
        /// Call [`LineProgram::end_sequence`] for the converted program.
1527
        pub fn end_sequence(&mut self, address_offset: u64) {
1528
            self.program.end_sequence(address_offset);
1529
        }
1530
1531
        /// Return [`LineProgram::in_sequence`] for the converted program.
1532
        pub fn in_sequence(&self) -> bool {
1533
            self.program.in_sequence()
1534
        }
1535
1536
        /// Set the next row and call [`LineProgram::generate_row`] for the converted program.
1537
        pub fn generate_row(&mut self, row: LineRow) {
1538
            *self.program.row() = row;
1539
            self.program.generate_row();
1540
        }
1541
1542
        /// Return the program and a mapping from source file index to `FileId`.
1543
        ///
1544
        /// The file index mapping is 0 based, regardless of the DWARF version.
1545
        /// For DWARF version <= 4, the entry at index 0 should not be used.
1546
        pub fn program(self) -> (LineProgram, Vec<FileId>) {
1547
            (self.program, self.files)
1548
        }
1549
1550
        /// Convert the entire program.
1551
        ///
1552
        /// Returns the program and a mapping from source file index to `FileId`,
1553
        /// as for [`ConvertLineProgram::program`].
1554
        ///
1555
        /// See [`Dwarf::from`](crate::write::Dwarf::from) for the meaning of `convert_address`.
1556
        pub fn convert(
1557
            mut self,
1558
            convert_address: &dyn Fn(u64) -> Option<Address>,
1559
        ) -> ConvertResult<(LineProgram, Vec<FileId>)> {
1560
            while let Some(row) = self.read_row()? {
1561
                match row {
1562
                    ConvertLineRow::SetAddress(address) => {
1563
                        let address =
1564
                            convert_address(address).ok_or(ConvertError::InvalidAddress)?;
1565
                        self.set_address(address);
1566
                    }
1567
                    ConvertLineRow::Row(row) => {
1568
                        self.generate_row(row);
1569
                    }
1570
                    ConvertLineRow::EndSequence(length) => {
1571
                        self.end_sequence(length);
1572
                    }
1573
                }
1574
            }
1575
            if self.in_sequence() {
1576
                return Err(ConvertError::MissingLineEndSequence);
1577
            }
1578
            Ok(self.program())
1579
        }
1580
    }
1581
}
1582
1583
#[cfg(test)]
1584
#[cfg(feature = "read")]
1585
mod tests {
1586
    use super::*;
1587
    use crate::LittleEndian;
1588
    use crate::read;
1589
    use crate::write::{AttributeValue, Dwarf, EndianVec, Sections, Unit};
1590
1591
    #[test]
1592
    fn test_line_program() {
1593
        let dir1 = LineString::String(b"dir1".to_vec());
1594
        let file1 = LineString::String(b"file1".to_vec());
1595
        let dir2 = LineString::String(b"dir2".to_vec());
1596
        let file2 = LineString::String(b"file2".to_vec());
1597
1598
        let mut dwarf = Dwarf::new();
1599
1600
        for &version in &[2, 3, 4, 5] {
1601
            for &address_size in &[4, 8] {
1602
                for &format in &[Format::Dwarf32, Format::Dwarf64] {
1603
                    let encoding = Encoding {
1604
                        format,
1605
                        version,
1606
                        address_size,
1607
                    };
1608
                    let mut program = LineProgram::new(
1609
                        encoding,
1610
                        LineEncoding::default(),
1611
                        dir1.clone(),
1612
                        None,
1613
                        file1.clone(),
1614
                        None,
1615
                    );
1616
1617
                    assert_eq!(&dir1, program.get_directory(program.default_directory()));
1618
                    program.file_has_timestamp = true;
1619
                    program.file_has_size = true;
1620
                    if encoding.version >= 5 {
1621
                        program.file_has_md5 = true;
1622
                    }
1623
1624
                    // Note: Embedded source code is an accepted extension
1625
                    // that will become part of DWARF v6. We're using the LLVM extension
1626
                    // here for v5.
1627
                    if encoding.version >= 5 {
1628
                        program.file_has_source = true;
1629
                    }
1630
1631
                    let dir_id = program.add_directory(dir2.clone());
1632
                    assert_eq!(&dir2, program.get_directory(dir_id));
1633
                    assert_eq!(dir_id, program.add_directory(dir2.clone()));
1634
1635
                    let file_info = FileInfo {
1636
                        timestamp: 1,
1637
                        size: 2,
1638
                        md5: if encoding.version >= 5 {
1639
                            [3; 16]
1640
                        } else {
1641
                            [0; 16]
1642
                        },
1643
                        source: (encoding.version >= 5)
1644
                            .then(|| LineString::String(b"the source code\n".to_vec())),
1645
                    };
1646
                    let file_id = program.add_file(file2.clone(), dir_id, Some(file_info.clone()));
1647
                    assert_eq!((&file2, dir_id), program.get_file(file_id));
1648
                    assert_eq!(file_info, *program.get_file_info(file_id));
1649
1650
                    program.get_file_info_mut(file_id).size = 3;
1651
                    assert_ne!(file_info, *program.get_file_info(file_id));
1652
                    assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None));
1653
                    assert_ne!(file_info, *program.get_file_info(file_id));
1654
                    assert_eq!(
1655
                        file_id,
1656
                        program.add_file(file2.clone(), dir_id, Some(file_info.clone()))
1657
                    );
1658
                    assert_eq!(file_info, *program.get_file_info(file_id));
1659
1660
                    let mut unit = Unit::new(encoding, program);
1661
                    let root = unit.get_mut(unit.root());
1662
                    root.set(
1663
                        constants::DW_AT_comp_dir,
1664
                        AttributeValue::String(b"dir1".to_vec()),
1665
                    );
1666
                    root.set(
1667
                        constants::DW_AT_name,
1668
                        AttributeValue::String(b"file1".to_vec()),
1669
                    );
1670
                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1671
                    root.set(
1672
                        constants::DW_AT_decl_file,
1673
                        AttributeValue::FileIndex(Some(file_id)),
1674
                    );
1675
1676
                    dwarf.units.add(unit);
1677
                }
1678
            }
1679
        }
1680
1681
        let mut sections = Sections::new(EndianVec::new(LittleEndian));
1682
        dwarf.write(&mut sections).unwrap();
1683
        let read_dwarf = sections.read(LittleEndian);
1684
        let convert_dwarf =
1685
            Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap();
1686
1687
        let mut convert_units = convert_dwarf.units.iter();
1688
        for (_, unit) in dwarf.units.iter() {
1689
            let program = &unit.line_program;
1690
            let root = unit.get(unit.root());
1691
            let Some(AttributeValue::FileIndex(Some(file_id))) =
1692
                root.get(constants::DW_AT_decl_file)
1693
            else {
1694
                panic!("missing DW_AT_decl_file");
1695
            };
1696
1697
            let (_, convert_unit) = convert_units.next().unwrap();
1698
            let convert_program = &convert_unit.line_program;
1699
            let convert_root = convert_unit.get(convert_unit.root());
1700
            let Some(AttributeValue::FileIndex(Some(convert_file_id))) =
1701
                convert_root.get(constants::DW_AT_decl_file)
1702
            else {
1703
                panic!("missing DW_AT_decl_file");
1704
            };
1705
1706
            assert_eq!(convert_program.version(), program.version());
1707
            assert_eq!(convert_program.address_size(), program.address_size());
1708
            assert_eq!(convert_program.format(), program.format());
1709
1710
            let (file, dir) = program.get_file(*file_id);
1711
            let (convert_file, convert_dir) = convert_program.get_file(*convert_file_id);
1712
            assert_eq!(
1713
                dwarf.get_line_string(file),
1714
                convert_dwarf.get_line_string(convert_file)
1715
            );
1716
            assert_eq!(
1717
                dwarf.get_line_string(program.get_directory(dir)),
1718
                convert_dwarf.get_line_string(convert_program.get_directory(convert_dir)),
1719
            );
1720
            let info = program.get_file_info(*file_id);
1721
            let convert_info = convert_program.get_file_info(*convert_file_id);
1722
            assert_eq!(info.timestamp, convert_info.timestamp);
1723
            assert_eq!(info.size, convert_info.size);
1724
            assert_eq!(info.md5, convert_info.md5);
1725
            assert_eq!(
1726
                info.source.as_ref().map(|s| dwarf.get_line_string(s)),
1727
                convert_info
1728
                    .source
1729
                    .as_ref()
1730
                    .map(|s| convert_dwarf.get_line_string(s)),
1731
            );
1732
        }
1733
    }
1734
1735
    #[test]
1736
    fn test_line_sequence() {
1737
        let dir1 = &b"dir1"[..];
1738
        let file1 = &b"file1"[..];
1739
        let file2 = &b"file2"[..];
1740
1741
        for &version in &[2, 3, 4, 5] {
1742
            for &address_size in &[4, 8] {
1743
                for &format in &[Format::Dwarf32, Format::Dwarf64] {
1744
                    let encoding = Encoding {
1745
                        format,
1746
                        version,
1747
                        address_size,
1748
                    };
1749
                    let mut program = LineProgram::new(
1750
                        encoding,
1751
                        LineEncoding::default(),
1752
                        LineString::String(dir1.to_vec()),
1753
                        None,
1754
                        LineString::String(file1.to_vec()),
1755
                        None,
1756
                    );
1757
                    let dir_id = program.default_directory();
1758
                    program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1759
                    // Ensure the default file index is valid.
1760
                    program.add_file(LineString::String(file2.to_vec()), dir_id, None);
1761
1762
                    // Test instructions added by sequence related methods.
1763
                    {
1764
                        let mut program = program.clone();
1765
                        let address = Address::Constant(0x12);
1766
                        program.begin_sequence(Some(address));
1767
                        assert_eq!(
1768
                            program.instructions,
1769
                            vec![LineInstruction::SetAddress(address)]
1770
                        );
1771
                    }
1772
1773
                    {
1774
                        let mut program = program.clone();
1775
                        program.begin_sequence(None);
1776
                        assert_eq!(program.instructions, Vec::new());
1777
                    }
1778
1779
                    {
1780
                        let mut program = program.clone();
1781
                        let address = Address::Constant(0x12);
1782
                        program.set_address(address);
1783
                        assert_eq!(
1784
                            program.instructions,
1785
                            vec![LineInstruction::SetAddress(address)]
1786
                        );
1787
                    }
1788
1789
                    {
1790
                        let mut program = program.clone();
1791
                        program.generate_row();
1792
                        assert_eq!(program.instructions, vec![LineInstruction::Copy]);
1793
                    }
1794
1795
                    {
1796
                        let mut program = program.clone();
1797
                        program.end_sequence(0x1234);
1798
                        assert_eq!(
1799
                            program.instructions,
1800
                            vec![
1801
                                LineInstruction::AdvancePc(0x1234),
1802
                                LineInstruction::EndSequence
1803
                            ]
1804
                        );
1805
                    }
1806
1807
                    {
1808
                        let mut program = program.clone();
1809
                        program.end_sequence(0);
1810
                        assert_eq!(program.instructions, vec![LineInstruction::EndSequence]);
1811
                    }
1812
1813
                    // Test conversion of sequences.
1814
                    let mut expected_instructions = Vec::new();
1815
                    let mut sequence_rows = Vec::new();
1816
                    let mut expected_rows = Vec::new();
1817
                    let mut expected_sequences = Vec::new();
1818
1819
                    // Basic sequence.
1820
                    let address = 0x12;
1821
                    let length = 0x1234;
1822
1823
                    expected_rows.push(ConvertLineRow::SetAddress(address));
1824
                    program.begin_sequence(Some(Address::Constant(address)));
1825
1826
                    expected_rows.push(ConvertLineRow::Row(program.row));
1827
                    sequence_rows.push(program.row);
1828
                    program.generate_row();
1829
1830
                    expected_rows.push(ConvertLineRow::EndSequence(length));
1831
                    expected_sequences.push(ConvertLineSequence {
1832
                        start: Some(0x12),
1833
                        end: ConvertLineSequenceEnd::Length(length),
1834
                        rows: core::mem::take(&mut sequence_rows),
1835
                    });
1836
                    program.end_sequence(length);
1837
1838
                    expected_instructions.extend_from_slice(&[
1839
                        LineInstruction::SetAddress(Address::Constant(address)),
1840
                        LineInstruction::Copy,
1841
                        LineInstruction::AdvancePc(length),
1842
                        LineInstruction::EndSequence,
1843
                    ]);
1844
1845
                    // Empty sequence.
1846
                    program.begin_sequence(None);
1847
1848
                    expected_rows.push(ConvertLineRow::EndSequence(0));
1849
                    expected_sequences.push(ConvertLineSequence {
1850
                        start: None,
1851
                        end: ConvertLineSequenceEnd::Length(0),
1852
                        rows: core::mem::take(&mut sequence_rows),
1853
                    });
1854
                    program.end_sequence(0);
1855
1856
                    expected_instructions.extend_from_slice(&[LineInstruction::EndSequence]);
1857
1858
                    // Multiple `DW_LNE_set_address` with tombstone.
1859
                    let address1 = 0x12;
1860
                    let address2 = 0x34;
1861
                    let tombstone = !0u64 >> (64 - address_size * 8);
1862
1863
                    expected_rows.push(ConvertLineRow::SetAddress(address1));
1864
                    program.set_address(Address::Constant(address1));
1865
1866
                    expected_rows.push(ConvertLineRow::Row(program.row));
1867
                    sequence_rows.push(program.row);
1868
                    program.generate_row();
1869
1870
                    program.set_address(Address::Constant(tombstone));
1871
                    program.generate_row();
1872
1873
                    expected_rows.push(ConvertLineRow::SetAddress(address2));
1874
                    expected_sequences.push(ConvertLineSequence {
1875
                        start: Some(address1),
1876
                        end: ConvertLineSequenceEnd::Address(address2),
1877
                        rows: core::mem::take(&mut sequence_rows),
1878
                    });
1879
                    program.set_address(Address::Constant(address2));
1880
1881
                    expected_rows.push(ConvertLineRow::Row(program.row));
1882
                    sequence_rows.push(program.row);
1883
                    program.generate_row();
1884
1885
                    expected_rows.push(ConvertLineRow::EndSequence(length));
1886
                    expected_sequences.push(ConvertLineSequence {
1887
                        start: Some(address2),
1888
                        end: ConvertLineSequenceEnd::Length(length),
1889
                        rows: core::mem::take(&mut sequence_rows),
1890
                    });
1891
                    program.end_sequence(length);
1892
1893
                    expected_instructions.extend_from_slice(&[
1894
                        LineInstruction::SetAddress(Address::Constant(address1)),
1895
                        LineInstruction::Copy,
1896
                        LineInstruction::SetAddress(Address::Constant(address2)),
1897
                        LineInstruction::Copy,
1898
                        LineInstruction::AdvancePc(length),
1899
                        LineInstruction::EndSequence,
1900
                    ]);
1901
1902
                    // No `DW_LNE_set_address`.
1903
                    expected_rows.push(ConvertLineRow::Row(program.row));
1904
                    sequence_rows.push(program.row);
1905
                    program.generate_row();
1906
1907
                    expected_rows.push(ConvertLineRow::EndSequence(length));
1908
                    expected_sequences.push(ConvertLineSequence {
1909
                        start: None,
1910
                        end: ConvertLineSequenceEnd::Length(length),
1911
                        rows: core::mem::take(&mut sequence_rows),
1912
                    });
1913
                    program.end_sequence(length);
1914
1915
                    expected_instructions.extend_from_slice(&[
1916
                        LineInstruction::Copy,
1917
                        LineInstruction::AdvancePc(length),
1918
                        LineInstruction::EndSequence,
1919
                    ]);
1920
1921
                    // Write the DWARF.
1922
                    let mut unit = Unit::new(encoding, program);
1923
                    let root = unit.get_mut(unit.root());
1924
                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1925
                    let mut dwarf = Dwarf::new();
1926
                    dwarf.units.add(unit);
1927
                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
1928
                    dwarf.write(&mut sections).unwrap();
1929
1930
                    // Read the DWARF.
1931
                    let read_dwarf = sections.read(LittleEndian);
1932
                    let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
1933
                    let read_unit = read_dwarf.unit(read_unit_header).unwrap();
1934
                    let read_program = &read_unit.line_program.unwrap();
1935
1936
                    // Test LineProgram::from.
1937
                    let convert_dwarf =
1938
                        Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
1939
                            .unwrap();
1940
                    let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
1941
                    let convert_program = &convert_unit.line_program;
1942
                    assert_eq!(convert_program.instructions, expected_instructions);
1943
1944
                    // Test ConvertLineProgram::read_row.
1945
                    let mut convert_line_strings = LineStringTable::default();
1946
                    let mut convert_strings = StringTable::default();
1947
                    let mut convert_line = ConvertLineProgram::new(
1948
                        &read_dwarf,
1949
                        read_program.clone(),
1950
                        None,
1951
                        None,
1952
                        None,
1953
                        &mut convert_line_strings,
1954
                        &mut convert_strings,
1955
                    )
1956
                    .unwrap();
1957
                    let mut convert_rows = Vec::new();
1958
                    while let Some(convert_row) = convert_line.read_row().unwrap() {
1959
                        convert_rows.push(convert_row);
1960
                    }
1961
                    assert_eq!(convert_rows, expected_rows);
1962
1963
                    // Test ConvertLineProgram::read_sequence.
1964
                    let mut convert_line_strings = LineStringTable::default();
1965
                    let mut convert_strings = StringTable::default();
1966
                    let mut convert_line = ConvertLineProgram::new(
1967
                        &read_dwarf,
1968
                        read_program.clone(),
1969
                        None,
1970
                        None,
1971
                        None,
1972
                        &mut convert_line_strings,
1973
                        &mut convert_strings,
1974
                    )
1975
                    .unwrap();
1976
                    let mut convert_sequences = Vec::new();
1977
                    while let Some(convert_sequence) = convert_line.read_sequence().unwrap() {
1978
                        convert_sequences.push(convert_sequence);
1979
                    }
1980
                    assert_eq!(convert_sequences, expected_sequences);
1981
                }
1982
            }
1983
        }
1984
    }
1985
1986
    #[test]
1987
    fn test_line_row() {
1988
        let dir1 = &b"dir1"[..];
1989
        let file1 = &b"file1"[..];
1990
        let file2 = &b"file2"[..];
1991
1992
        for &version in &[2, 3, 4, 5] {
1993
            for &address_size in &[4, 8] {
1994
                for &format in &[Format::Dwarf32, Format::Dwarf64] {
1995
                    let encoding = Encoding {
1996
                        format,
1997
                        version,
1998
                        address_size,
1999
                    };
2000
                    let line_base = -5;
2001
                    let line_range = 14;
2002
                    let neg_line_base = (-line_base) as u8;
2003
                    let mut program = LineProgram::new(
2004
                        encoding,
2005
                        LineEncoding {
2006
                            line_base,
2007
                            line_range,
2008
                            ..Default::default()
2009
                        },
2010
                        LineString::String(dir1.to_vec()),
2011
                        None,
2012
                        LineString::String(file1.to_vec()),
2013
                        None,
2014
                    );
2015
                    let dir_id = program.default_directory();
2016
                    let file1_id =
2017
                        program.add_file(LineString::String(file1.to_vec()), dir_id, None);
2018
                    let file2_id =
2019
                        program.add_file(LineString::String(file2.to_vec()), dir_id, None);
2020
2021
                    // Create a base program.
2022
                    program.row.line = 0x1000;
2023
                    program.generate_row();
2024
                    let base_row = program.row;
2025
                    let base_instructions = program.instructions.clone();
2026
2027
                    // Create test cases.
2028
                    let mut tests = Vec::new();
2029
2030
                    let row = base_row;
2031
                    tests.push((row, vec![LineInstruction::Copy]));
2032
2033
                    let mut row = base_row;
2034
                    row.line -= u64::from(neg_line_base);
2035
                    tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)]));
2036
2037
                    let mut row = base_row;
2038
                    row.line += u64::from(line_range) - 1;
2039
                    row.line -= u64::from(neg_line_base);
2040
                    tests.push((
2041
                        row,
2042
                        vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)],
2043
                    ));
2044
2045
                    let mut row = base_row;
2046
                    row.line += u64::from(line_range);
2047
                    row.line -= u64::from(neg_line_base);
2048
                    tests.push((
2049
                        row,
2050
                        vec![
2051
                            LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)),
2052
                            LineInstruction::Copy,
2053
                        ],
2054
                    ));
2055
2056
                    let mut row = base_row;
2057
                    row.address_offset = 1;
2058
                    row.line -= u64::from(neg_line_base);
2059
                    tests.push((
2060
                        row,
2061
                        vec![LineInstruction::Special(OPCODE_BASE + line_range)],
2062
                    ));
2063
2064
                    let op_range = (255 - OPCODE_BASE) / line_range;
2065
                    let mut row = base_row;
2066
                    row.address_offset = u64::from(op_range);
2067
                    row.line -= u64::from(neg_line_base);
2068
                    tests.push((
2069
                        row,
2070
                        vec![LineInstruction::Special(
2071
                            OPCODE_BASE + op_range * line_range,
2072
                        )],
2073
                    ));
2074
2075
                    let mut row = base_row;
2076
                    row.address_offset = u64::from(op_range);
2077
                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
2078
                    row.line -= u64::from(neg_line_base);
2079
                    tests.push((row, vec![LineInstruction::Special(255)]));
2080
2081
                    let mut row = base_row;
2082
                    row.address_offset = u64::from(op_range);
2083
                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
2084
                    row.line -= u64::from(neg_line_base);
2085
                    tests.push((
2086
                        row,
2087
                        vec![LineInstruction::ConstAddPc, LineInstruction::Copy],
2088
                    ));
2089
2090
                    let mut row = base_row;
2091
                    row.address_offset = u64::from(op_range);
2092
                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
2093
                    row.line -= u64::from(neg_line_base);
2094
                    tests.push((
2095
                        row,
2096
                        vec![
2097
                            LineInstruction::ConstAddPc,
2098
                            LineInstruction::Special(OPCODE_BASE + 6),
2099
                        ],
2100
                    ));
2101
2102
                    let mut row = base_row;
2103
                    row.address_offset = u64::from(op_range) * 2;
2104
                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
2105
                    row.line -= u64::from(neg_line_base);
2106
                    tests.push((
2107
                        row,
2108
                        vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)],
2109
                    ));
2110
2111
                    let mut row = base_row;
2112
                    row.address_offset = u64::from(op_range) * 2;
2113
                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
2114
                    row.line -= u64::from(neg_line_base);
2115
                    tests.push((
2116
                        row,
2117
                        vec![
2118
                            LineInstruction::AdvancePc(row.address_offset),
2119
                            LineInstruction::Copy,
2120
                        ],
2121
                    ));
2122
2123
                    let mut row = base_row;
2124
                    row.address_offset = u64::from(op_range) * 2;
2125
                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
2126
                    row.line -= u64::from(neg_line_base);
2127
                    tests.push((
2128
                        row,
2129
                        vec![
2130
                            LineInstruction::AdvancePc(row.address_offset),
2131
                            LineInstruction::Special(OPCODE_BASE + 6),
2132
                        ],
2133
                    ));
2134
2135
                    let mut row = base_row;
2136
                    row.address_offset = 0x1234;
2137
                    tests.push((
2138
                        row,
2139
                        vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy],
2140
                    ));
2141
2142
                    let mut row = base_row;
2143
                    row.line += 0x1234;
2144
                    tests.push((
2145
                        row,
2146
                        vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy],
2147
                    ));
2148
2149
                    let mut row = base_row;
2150
                    row.file = file1_id;
2151
                    if version == 5 {
2152
                        // Version 5 is 0-based, but the default file is 1, so this row
2153
                        // will need to set the file.
2154
                        tests.push((
2155
                            row,
2156
                            vec![LineInstruction::SetFile(file1_id), LineInstruction::Copy],
2157
                        ));
2158
                    } else {
2159
                        // This is the first file, so normally this is already the default.
2160
                        tests.push((row, vec![LineInstruction::Copy]));
2161
                    }
2162
2163
                    let mut row = base_row;
2164
                    row.file = file2_id;
2165
                    if version == 5 {
2166
                        tests.push((row, vec![LineInstruction::Copy]));
2167
                    } else {
2168
                        tests.push((
2169
                            row,
2170
                            vec![LineInstruction::SetFile(file2_id), LineInstruction::Copy],
2171
                        ));
2172
                    }
2173
2174
                    let mut row = base_row;
2175
                    row.column = 0x1234;
2176
                    tests.push((
2177
                        row,
2178
                        vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy],
2179
                    ));
2180
2181
                    let mut row = base_row;
2182
                    row.discriminator = 0x1234;
2183
                    tests.push((
2184
                        row,
2185
                        vec![
2186
                            LineInstruction::SetDiscriminator(0x1234),
2187
                            LineInstruction::Copy,
2188
                        ],
2189
                    ));
2190
2191
                    let mut row = base_row;
2192
                    row.is_statement = !row.is_statement;
2193
                    tests.push((
2194
                        row,
2195
                        vec![LineInstruction::NegateStatement, LineInstruction::Copy],
2196
                    ));
2197
2198
                    let mut row = base_row;
2199
                    row.basic_block = true;
2200
                    tests.push((
2201
                        row,
2202
                        vec![LineInstruction::SetBasicBlock, LineInstruction::Copy],
2203
                    ));
2204
2205
                    let mut row = base_row;
2206
                    row.prologue_end = true;
2207
                    tests.push((
2208
                        row,
2209
                        vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy],
2210
                    ));
2211
2212
                    let mut row = base_row;
2213
                    row.epilogue_begin = true;
2214
                    tests.push((
2215
                        row,
2216
                        vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy],
2217
                    ));
2218
2219
                    let mut row = base_row;
2220
                    row.isa = 0x1234;
2221
                    tests.push((
2222
                        row,
2223
                        vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy],
2224
                    ));
2225
2226
                    for test in tests {
2227
                        // Test generate_row().
2228
                        let mut program = program.clone();
2229
                        program.row = test.0;
2230
                        program.generate_row();
2231
                        program.end_sequence(test.0.address_offset);
2232
                        assert_eq!(
2233
                            &program.instructions
2234
                                [base_instructions.len()..program.instructions.len() - 1],
2235
                            &test.1[..]
2236
                        );
2237
                        assert_eq!(
2238
                            program.instructions.last(),
2239
                            Some(&LineInstruction::EndSequence)
2240
                        );
2241
2242
                        // Test LineProgram::from().
2243
                        let mut unit = Unit::new(encoding, program);
2244
                        let root = unit.get_mut(unit.root());
2245
                        root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2246
2247
                        let mut dwarf = Dwarf::new();
2248
                        dwarf.units.add(unit);
2249
2250
                        let mut sections = Sections::new(EndianVec::new(LittleEndian));
2251
                        dwarf.write(&mut sections).unwrap();
2252
                        let read_dwarf = sections.read(LittleEndian);
2253
2254
                        let convert_dwarf =
2255
                            Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2256
                                .unwrap();
2257
                        let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
2258
                        let convert_program = &convert_unit.line_program;
2259
2260
                        assert_eq!(
2261
                            &convert_program.instructions
2262
                                [base_instructions.len()..convert_program.instructions.len() - 1],
2263
                            &test.1[..]
2264
                        );
2265
                        assert_eq!(
2266
                            convert_program.instructions.last(),
2267
                            Some(&LineInstruction::EndSequence)
2268
                        );
2269
                    }
2270
                }
2271
            }
2272
        }
2273
    }
2274
2275
    #[test]
2276
    fn test_line_instruction() {
2277
        let dir1 = &b"dir1"[..];
2278
        let file1 = &b"file1"[..];
2279
2280
        for &version in &[2, 3, 4, 5] {
2281
            for &address_size in &[4, 8] {
2282
                for &format in &[Format::Dwarf32, Format::Dwarf64] {
2283
                    let encoding = Encoding {
2284
                        format,
2285
                        version,
2286
                        address_size,
2287
                    };
2288
                    let mut program = LineProgram::new(
2289
                        encoding,
2290
                        LineEncoding::default(),
2291
                        LineString::String(dir1.to_vec()),
2292
                        None,
2293
                        LineString::String(file1.to_vec()),
2294
                        None,
2295
                    );
2296
                    let dir_id = program.default_directory();
2297
                    let file_id =
2298
                        program.add_file(LineString::String(file1.to_vec()), dir_id, None);
2299
2300
                    for (inst, expect_inst) in &[
2301
                        (
2302
                            LineInstruction::Special(OPCODE_BASE),
2303
                            read::LineInstruction::Special(OPCODE_BASE),
2304
                        ),
2305
                        (
2306
                            LineInstruction::Special(255),
2307
                            read::LineInstruction::Special(255),
2308
                        ),
2309
                        (LineInstruction::Copy, read::LineInstruction::Copy),
2310
                        (
2311
                            LineInstruction::AdvancePc(0x12),
2312
                            read::LineInstruction::AdvancePc(0x12),
2313
                        ),
2314
                        (
2315
                            LineInstruction::AdvanceLine(0x12),
2316
                            read::LineInstruction::AdvanceLine(0x12),
2317
                        ),
2318
                        (
2319
                            LineInstruction::SetFile(file_id),
2320
                            read::LineInstruction::SetFile(file_id.raw(encoding.version)),
2321
                        ),
2322
                        (
2323
                            LineInstruction::SetColumn(0x12),
2324
                            read::LineInstruction::SetColumn(0x12),
2325
                        ),
2326
                        (
2327
                            LineInstruction::NegateStatement,
2328
                            read::LineInstruction::NegateStatement,
2329
                        ),
2330
                        (
2331
                            LineInstruction::SetBasicBlock,
2332
                            read::LineInstruction::SetBasicBlock,
2333
                        ),
2334
                        (
2335
                            LineInstruction::ConstAddPc,
2336
                            read::LineInstruction::ConstAddPc,
2337
                        ),
2338
                        (
2339
                            LineInstruction::SetPrologueEnd,
2340
                            read::LineInstruction::SetPrologueEnd,
2341
                        ),
2342
                        (
2343
                            LineInstruction::SetEpilogueBegin,
2344
                            read::LineInstruction::SetEpilogueBegin,
2345
                        ),
2346
                        (
2347
                            LineInstruction::SetIsa(0x12),
2348
                            read::LineInstruction::SetIsa(0x12),
2349
                        ),
2350
                        (
2351
                            LineInstruction::EndSequence,
2352
                            read::LineInstruction::EndSequence,
2353
                        ),
2354
                        (
2355
                            LineInstruction::SetAddress(Address::Constant(0x12)),
2356
                            read::LineInstruction::SetAddress(0x12),
2357
                        ),
2358
                        (
2359
                            LineInstruction::SetDiscriminator(0x12),
2360
                            read::LineInstruction::SetDiscriminator(0x12),
2361
                        ),
2362
                    ][..]
2363
                    {
2364
                        let mut program = program.clone();
2365
                        program.instructions.push(*inst);
2366
2367
                        let mut unit = Unit::new(encoding, program);
2368
                        let root = unit.get_mut(unit.root());
2369
                        root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2370
2371
                        let mut dwarf = Dwarf::new();
2372
                        dwarf.units.add(unit);
2373
                        let mut sections = Sections::new(EndianVec::new(LittleEndian));
2374
                        dwarf.write(&mut sections).unwrap();
2375
2376
                        let read_dwarf = sections.read(LittleEndian);
2377
                        let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2378
                        let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2379
                        let read_unit = read_unit.unit_ref(&read_dwarf);
2380
                        let read_header = read_unit.line_program.as_ref().unwrap().header();
2381
                        let mut read_insts = read_header.instructions();
2382
                        assert_eq!(
2383
                            *expect_inst,
2384
                            read_insts.next_instruction(read_header).unwrap().unwrap()
2385
                        );
2386
                        assert_eq!(None, read_insts.next_instruction(read_header).unwrap());
2387
                    }
2388
                }
2389
            }
2390
        }
2391
    }
2392
2393
    // Test that the address/line advance is correct. We don't test for optimality.
2394
    #[test]
2395
    fn test_advance() {
2396
        let encoding = Encoding {
2397
            format: Format::Dwarf32,
2398
            version: 4,
2399
            address_size: 8,
2400
        };
2401
2402
        let dir1 = &b"dir1"[..];
2403
        let file1 = &b"file1"[..];
2404
2405
        let addresses = 0..50;
2406
        let lines = -10..25i64;
2407
2408
        for minimum_instruction_length in [1, 4] {
2409
            for maximum_operations_per_instruction in [1, 3] {
2410
                for line_base in [-5, 0] {
2411
                    for line_range in [10, 20] {
2412
                        let line_encoding = LineEncoding {
2413
                            minimum_instruction_length,
2414
                            maximum_operations_per_instruction,
2415
                            line_base,
2416
                            line_range,
2417
                            default_is_stmt: true,
2418
                        };
2419
                        let mut program = LineProgram::new(
2420
                            encoding,
2421
                            line_encoding,
2422
                            LineString::String(dir1.to_vec()),
2423
                            None,
2424
                            LineString::String(file1.to_vec()),
2425
                            None,
2426
                        );
2427
                        for address_advance in addresses.clone() {
2428
                            program.set_address(Address::Constant(0x1000));
2429
                            program.row().line = 0x10000;
2430
                            program.generate_row();
2431
                            for line_advance in lines.clone() {
2432
                                {
2433
                                    let row = program.row();
2434
                                    row.address_offset +=
2435
                                        address_advance * u64::from(minimum_instruction_length);
2436
                                    row.line = row.line.wrapping_add(line_advance as u64);
2437
                                }
2438
                                program.generate_row();
2439
                            }
2440
                            let address_offset = program.row().address_offset
2441
                                + u64::from(minimum_instruction_length);
2442
                            program.end_sequence(address_offset);
2443
                        }
2444
2445
                        let mut unit = Unit::new(encoding, program);
2446
                        let root = unit.get_mut(unit.root());
2447
                        root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2448
2449
                        let mut dwarf = Dwarf::new();
2450
                        dwarf.units.add(unit);
2451
                        let mut sections = Sections::new(EndianVec::new(LittleEndian));
2452
                        dwarf.write(&mut sections).unwrap();
2453
2454
                        let read_dwarf = sections.read(LittleEndian);
2455
                        let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2456
                        let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2457
                        let read_unit = read_unit.unit_ref(&read_dwarf);
2458
                        let read_program = read_unit.line_program.clone().unwrap();
2459
2460
                        let mut rows = read_program.rows();
2461
                        for address_advance in addresses.clone() {
2462
                            let mut address;
2463
                            let mut line;
2464
                            {
2465
                                let row = rows.next_row().unwrap().unwrap().1;
2466
                                address = row.address();
2467
                                line = row.line().unwrap().get();
2468
                            }
2469
                            assert_eq!(address, 0x1000);
2470
                            assert_eq!(line, 0x10000);
2471
                            for line_advance in lines.clone() {
2472
                                let row = rows.next_row().unwrap().unwrap().1;
2473
                                assert_eq!(
2474
                                    row.address() - address,
2475
                                    address_advance * u64::from(minimum_instruction_length)
2476
                                );
2477
                                assert_eq!(
2478
                                    (row.line().unwrap().get() as i64) - (line as i64),
2479
                                    line_advance
2480
                                );
2481
                                address = row.address();
2482
                                line = row.line().unwrap().get();
2483
                            }
2484
                            let row = rows.next_row().unwrap().unwrap().1;
2485
                            assert!(row.end_sequence());
2486
                        }
2487
                    }
2488
                }
2489
            }
2490
        }
2491
    }
2492
2493
    #[test]
2494
    fn test_line_string() {
2495
        let version = 5;
2496
2497
        let file1 = "file1";
2498
2499
        for &address_size in &[4, 8] {
2500
            for &format in &[Format::Dwarf32, Format::Dwarf64] {
2501
                let encoding = Encoding {
2502
                    format,
2503
                    version,
2504
                    address_size,
2505
                };
2506
2507
                let files: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [
2508
                    &mut |_dwarf| LineString::String(file1.as_bytes().to_vec()),
2509
                    &mut |dwarf| LineString::StringRef(dwarf.strings.add(file1)),
2510
                    &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(file1)),
2511
                ];
2512
2513
                for file in files {
2514
                    let mut dwarf = Dwarf::new();
2515
                    let file = file(&mut dwarf);
2516
2517
                    let mut program = LineProgram::new(
2518
                        encoding,
2519
                        LineEncoding::default(),
2520
                        LineString::String(b"dir".to_vec()),
2521
                        None,
2522
                        file.clone(),
2523
                        None,
2524
                    );
2525
                    program.set_address(Address::Constant(0x1000));
2526
                    program.row().line = 0x10000;
2527
                    program.generate_row();
2528
2529
                    let mut unit = Unit::new(encoding, program);
2530
                    let root = unit.get_mut(unit.root());
2531
                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2532
                    dwarf.units.add(unit);
2533
2534
                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
2535
                    dwarf.write(&mut sections).unwrap();
2536
2537
                    let read_dwarf = sections.read(LittleEndian);
2538
                    let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2539
                    let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2540
                    let read_unit = read_unit.unit_ref(&read_dwarf);
2541
                    let read_program = read_unit.line_program.clone().unwrap();
2542
                    let read_header = read_program.header();
2543
                    let read_file = read_header.file(0).unwrap();
2544
                    let read_path = read_unit.attr_string(read_file.path_name()).unwrap();
2545
                    assert_eq!(read_path.slice(), file1.as_bytes());
2546
                }
2547
            }
2548
        }
2549
    }
2550
2551
    #[test]
2552
    fn test_missing_comp_dir() {
2553
        for &version in &[2, 3, 4, 5] {
2554
            for &address_size in &[4, 8] {
2555
                for &format in &[Format::Dwarf32, Format::Dwarf64] {
2556
                    let encoding = Encoding {
2557
                        format,
2558
                        version,
2559
                        address_size,
2560
                    };
2561
                    let mut program = LineProgram::new(
2562
                        encoding,
2563
                        LineEncoding::default(),
2564
                        LineString::String(Vec::new()),
2565
                        None,
2566
                        LineString::String(Vec::new()),
2567
                        None,
2568
                    );
2569
                    // Ensure the program is not empty.
2570
                    let dir_id = program.default_directory();
2571
                    let file_id =
2572
                        program.add_file(LineString::String(b"file1".to_vec()), dir_id, None);
2573
                    program.set_address(Address::Constant(0x1000));
2574
                    program.row().file = file_id;
2575
                    program.row().line = 0x10000;
2576
                    program.generate_row();
2577
                    program.end_sequence(20);
2578
2579
                    let mut unit = Unit::new(encoding, program);
2580
                    let root = unit.get_mut(unit.root());
2581
                    // Testing missing DW_AT_comp_dir/DW_AT_name.
2582
                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2583
2584
                    let mut dwarf = Dwarf::new();
2585
                    dwarf.units.add(unit);
2586
                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
2587
                    dwarf.write(&mut sections).unwrap();
2588
                    let read_dwarf = sections.read(LittleEndian);
2589
                    let _convert_dwarf =
2590
                        Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2591
                            .unwrap();
2592
                }
2593
            }
2594
        }
2595
    }
2596
2597
    #[test]
2598
    fn test_separate_working_dir() {
2599
        let working_dir = LineString::String(b"working".to_vec());
2600
        let source_dir = LineString::String(b"source".to_vec());
2601
        let source_file = LineString::String(b"file".to_vec());
2602
2603
        for &version in &[2, 3, 4, 5] {
2604
            for &address_size in &[4, 8] {
2605
                for &format in &[Format::Dwarf32, Format::Dwarf64] {
2606
                    let encoding = Encoding {
2607
                        format,
2608
                        version,
2609
                        address_size,
2610
                    };
2611
                    let mut program = LineProgram::new(
2612
                        encoding,
2613
                        LineEncoding::default(),
2614
                        working_dir.clone(),
2615
                        Some(source_dir.clone()),
2616
                        source_file.clone(),
2617
                        None,
2618
                    );
2619
2620
                    assert_eq!(
2621
                        &working_dir,
2622
                        program.get_directory(program.default_directory())
2623
                    );
2624
2625
                    // Ensure the program is not empty.
2626
                    let dir_id = program.add_directory(source_dir.clone());
2627
                    let file_id = program.add_file(source_file.clone(), dir_id, None);
2628
                    program.begin_sequence(Some(Address::Constant(0x1000)));
2629
                    program.row().file = file_id;
2630
                    program.row().line = 0x10000;
2631
                    program.generate_row();
2632
                    program.end_sequence(20);
2633
2634
                    // Test LineProgram::from().
2635
                    let mut unit = Unit::new(encoding, program);
2636
                    let root = unit.get_mut(unit.root());
2637
                    root.set(
2638
                        constants::DW_AT_comp_dir,
2639
                        AttributeValue::String(b"working".to_vec()),
2640
                    );
2641
                    root.set(
2642
                        constants::DW_AT_name,
2643
                        AttributeValue::String(b"source/file".to_vec()),
2644
                    );
2645
                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2646
2647
                    let mut dwarf = Dwarf::new();
2648
                    dwarf.units.add(unit);
2649
2650
                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
2651
                    dwarf.write(&mut sections).unwrap();
2652
                    let read_dwarf = sections.read(LittleEndian);
2653
2654
                    let convert_dwarf =
2655
                        Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2656
                            .unwrap();
2657
                    let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
2658
                    let convert_program = &convert_unit.line_program;
2659
2660
                    assert_eq!(
2661
                        dwarf.get_line_string(&working_dir),
2662
                        convert_dwarf.get_line_string(
2663
                            convert_program.get_directory(convert_program.default_directory())
2664
                        ),
2665
                    );
2666
                    let (_file_id, file, dir_id) = convert_program.files().next().unwrap();
2667
                    let dir = convert_program.get_directory(dir_id);
2668
                    assert_eq!(
2669
                        dwarf.get_line_string(&source_file),
2670
                        convert_dwarf.get_line_string(file)
2671
                    );
2672
                    assert_eq!(
2673
                        dwarf.get_line_string(&source_dir),
2674
                        convert_dwarf.get_line_string(dir),
2675
                    );
2676
                }
2677
            }
2678
        }
2679
    }
2680
2681
    #[test]
2682
    fn test_file_source() {
2683
        let version = 5;
2684
2685
        let source1 = "source1";
2686
2687
        for &address_size in &[4, 8] {
2688
            for &format in &[Format::Dwarf32, Format::Dwarf64] {
2689
                let encoding = Encoding {
2690
                    format,
2691
                    version,
2692
                    address_size,
2693
                };
2694
2695
                let sources: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [
2696
                    &mut |_dwarf| LineString::String(source1.as_bytes().to_vec()),
2697
                    &mut |dwarf| LineString::StringRef(dwarf.strings.add(source1)),
2698
                    &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(source1)),
2699
                ];
2700
2701
                for source in sources {
2702
                    let mut dwarf = Dwarf::new();
2703
                    let source = Some(source(&mut dwarf));
2704
2705
                    let mut program = LineProgram::new(
2706
                        encoding,
2707
                        LineEncoding::default(),
2708
                        LineString::String(b"dir".to_vec()),
2709
                        None,
2710
                        LineString::String(b"file".to_vec()),
2711
                        Some(FileInfo {
2712
                            timestamp: 0,
2713
                            size: 0,
2714
                            md5: [0; 16],
2715
                            source,
2716
                        }),
2717
                    );
2718
                    program.file_has_source = true;
2719
2720
                    let file_id = program.files().next().unwrap().0;
2721
                    program.begin_sequence(Some(Address::Constant(0x1000)));
2722
                    program.row().file = file_id;
2723
                    program.row().line = 0x10000;
2724
                    program.generate_row();
2725
                    program.end_sequence(20);
2726
2727
                    let mut unit = Unit::new(encoding, program);
2728
                    let root = unit.get_mut(unit.root());
2729
                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2730
                    dwarf.units.add(unit);
2731
2732
                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
2733
                    dwarf.write(&mut sections).unwrap();
2734
2735
                    let read_dwarf = sections.read(LittleEndian);
2736
                    let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2737
                    let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2738
                    let read_unit = read_unit.unit_ref(&read_dwarf);
2739
                    let read_program = read_unit.line_program.clone().unwrap();
2740
                    let read_header = read_program.header();
2741
                    let read_file = read_header.file(0).unwrap();
2742
                    let read_source = read_unit.attr_string(read_file.source().unwrap()).unwrap();
2743
                    assert_eq!(read_source.slice(), source1.as_bytes());
2744
2745
                    let convert_dwarf =
2746
                        Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2747
                            .unwrap();
2748
                    let (_, convert_unit) = convert_dwarf.units.iter().next().unwrap();
2749
                    let convert_program = &convert_unit.line_program;
2750
                    let convert_file_id = convert_program.files().next().unwrap().0;
2751
                    let convert_file_info = convert_program.get_file_info(convert_file_id);
2752
                    assert_eq!(
2753
                        convert_dwarf.get_line_string(convert_file_info.source.as_ref().unwrap()),
2754
                        source1.as_bytes(),
2755
                    );
2756
                }
2757
            }
2758
        }
2759
    }
2760
}