gimli/write/
line.rs

1use alloc::vec::Vec;
2use indexmap::{IndexMap, IndexSet};
3use std::ops::{Deref, DerefMut};
4
5use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId};
6use crate::constants;
7use crate::leb128;
8use crate::write::{
9    Address, DebugLineStrOffsets, DebugStrOffsets, Error, LineStringId, LineStringTable, Result,
10    Section, StringId, StringTable, Writer,
11};
12
13/// The number assigned to the first special opcode.
14//
15// We output all instructions for all DWARF versions, since readers
16// should be able to ignore instructions they don't support.
17const OPCODE_BASE: u8 = 13;
18
19/// A line number program.
20#[derive(Debug, Clone)]
21pub struct LineProgram {
22    /// True if this line program was created with `LineProgram::none()`.
23    none: bool,
24    encoding: Encoding,
25    line_encoding: LineEncoding,
26
27    /// A list of source directory path names.
28    ///
29    /// If a path is relative, then the directory is located relative to the working
30    /// directory of the compilation unit.
31    ///
32    /// The first entry is for the working directory of the compilation unit.
33    directories: IndexSet<LineString>,
34
35    /// A list of source file entries.
36    ///
37    /// Each entry has a path name and a directory.
38    ///
39    /// If a path is a relative, then the file is located relative to the
40    /// directory. Otherwise the directory is meaningless.
41    ///
42    /// Does not include comp_file, even for version >= 5.
43    files: IndexMap<(LineString, DirectoryId), FileInfo>,
44
45    /// True if the file entries may have valid timestamps.
46    ///
47    /// Entries may still have a timestamp of 0 even if this is set.
48    /// For version <= 4, this is ignored.
49    /// For version 5, this controls whether to emit `DW_LNCT_timestamp`.
50    pub file_has_timestamp: bool,
51
52    /// True if the file entries may have valid sizes.
53    ///
54    /// Entries may still have a size of 0 even if this is set.
55    /// For version <= 4, this is ignored.
56    /// For version 5, this controls whether to emit `DW_LNCT_size`.
57    pub file_has_size: bool,
58
59    /// True if the file entries have valid MD5 checksums.
60    ///
61    /// For version <= 4, this is ignored.
62    /// For version 5, this controls whether to emit `DW_LNCT_MD5`.
63    pub file_has_md5: bool,
64
65    /// True if the file entries have embedded source code.
66    ///
67    /// For version <= 4, this is ignored.
68    /// For version 5, this controls whether to emit `DW_LNCT_LLVM_source`.
69    pub file_has_source: bool,
70
71    prev_row: LineRow,
72    row: LineRow,
73    // TODO: this probably should be either rows or sequences instead
74    instructions: Vec<LineInstruction>,
75    in_sequence: bool,
76}
77
78impl LineProgram {
79    /// Create a new `LineProgram`.
80    ///
81    /// `working_dir` defines the working directory of the compilation unit.
82    ///
83    /// `source_dir`, `source_file` and `source_file_info` define the first
84    /// file entry. `source_dir` may be relative to `working_dir`, and may be
85    /// `None` if `source_file` is in `working_dir`. The first file entry
86    /// is usually the primary source file.
87    ///
88    /// The standard specifies that `working_dir` should be the same as the
89    /// `DW_AT_comp_dir` attribute of the compilation unit DIE, and the
90    /// combination of `source_dir` and `source_file` should be the same
91    /// as the `DW_AT_name` attribute of the compilation unit DIE.
92    /// However, neither of these are enforced by this library.
93    ///
94    /// # Panics
95    ///
96    /// Panics if `line_encoding.line_base` > 0.
97    ///
98    /// Panics if `line_encoding.line_base` + `line_encoding.line_range` <= 0.
99    ///
100    /// Panics if `working_dir`, `source_dir`, or `source_file` are empty or
101    /// contain a null byte.
102    pub fn new(
103        encoding: Encoding,
104        line_encoding: LineEncoding,
105        working_dir: LineString,
106        source_dir: Option<LineString>,
107        source_file: LineString,
108        source_file_info: Option<FileInfo>,
109    ) -> LineProgram {
110        // We require a special opcode for a line advance of 0.
111        // See the debug_asserts in generate_row().
112        assert!(line_encoding.line_base <= 0);
113        assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0);
114        let mut program = LineProgram {
115            none: false,
116            encoding,
117            line_encoding,
118            directories: IndexSet::new(),
119            files: IndexMap::new(),
120            prev_row: LineRow::initial_state(encoding, line_encoding),
121            row: LineRow::initial_state(encoding, line_encoding),
122            instructions: Vec::new(),
123            in_sequence: false,
124            file_has_timestamp: false,
125            file_has_size: false,
126            file_has_md5: false,
127            file_has_source: false,
128        };
129        // For all DWARF versions, directory index 0 is working_dir.
130        // For version <= 4, the entry is implicit. We still add
131        // it here so that we use it, but we don't emit it.
132        let working_dir_id = program.add_directory(working_dir);
133        // For DWARF version >= 5, file index 0 is source_file and must exist.
134        if encoding.version >= 5 {
135            let source_dir_id = match source_dir {
136                Some(source_dir) => program.add_directory(source_dir),
137                None => working_dir_id,
138            };
139            program.add_file(source_file, source_dir_id, source_file_info);
140        }
141        program
142    }
143
144    /// Create a new `LineProgram` with no fields set.
145    ///
146    /// This can be used when the `LineProgram` will not be used.
147    ///
148    /// You should not attempt to add files or line instructions to
149    /// this line program, or write it to the `.debug_line` section.
150    pub fn none() -> Self {
151        let encoding = Encoding {
152            format: Format::Dwarf32,
153            version: 2,
154            address_size: 0,
155        };
156        let line_encoding = LineEncoding::default();
157        LineProgram {
158            none: true,
159            encoding,
160            line_encoding,
161            directories: IndexSet::new(),
162            files: IndexMap::new(),
163            prev_row: LineRow::initial_state(encoding, line_encoding),
164            row: LineRow::initial_state(encoding, line_encoding),
165            instructions: Vec::new(),
166            in_sequence: false,
167            file_has_timestamp: false,
168            file_has_size: false,
169            file_has_md5: false,
170            file_has_source: false,
171        }
172    }
173
174    /// Return true if this line program was created with `LineProgram::none()`.
175    #[inline]
176    pub fn is_none(&self) -> bool {
177        self.none
178    }
179
180    /// Return the encoding parameters for this line program.
181    #[inline]
182    pub fn encoding(&self) -> Encoding {
183        self.encoding
184    }
185
186    /// Return the DWARF version for this line program.
187    #[inline]
188    pub fn version(&self) -> u16 {
189        self.encoding.version
190    }
191
192    /// Return the address size in bytes for this line program.
193    #[inline]
194    pub fn address_size(&self) -> u8 {
195        self.encoding.address_size
196    }
197
198    /// Return the DWARF format for this line program.
199    #[inline]
200    pub fn format(&self) -> Format {
201        self.encoding.format
202    }
203
204    /// Return the id for the working directory of the compilation unit.
205    #[inline]
206    pub fn default_directory(&self) -> DirectoryId {
207        DirectoryId(0)
208    }
209
210    /// Add a directory entry and return its id.
211    ///
212    /// If the directory already exists, then return the id of the existing entry.
213    ///
214    /// If the path is relative, then the directory is located relative to the working
215    /// directory of the compilation unit.
216    ///
217    /// # Panics
218    ///
219    /// Panics if `directory` is empty or contains a null byte.
220    pub fn add_directory(&mut self, directory: LineString) -> DirectoryId {
221        if let LineString::String(ref val) = directory {
222            // For DWARF version <= 4, directories must not be empty.
223            // The first directory isn't emitted so skip the check for it.
224            if self.encoding.version <= 4 && !self.directories.is_empty() {
225                assert!(!val.is_empty());
226            }
227            assert!(!val.contains(&0));
228        }
229        let (index, _) = self.directories.insert_full(directory);
230        DirectoryId(index)
231    }
232
233    /// Get a reference to a directory entry.
234    ///
235    /// # Panics
236    ///
237    /// Panics if `id` is invalid.
238    pub fn get_directory(&self, id: DirectoryId) -> &LineString {
239        self.directories.get_index(id.0).unwrap()
240    }
241
242    /// Add a file entry and return its id.
243    ///
244    /// If the file already exists, then return the id of the existing entry.
245    ///
246    /// If the file path is relative, then the file is located relative
247    /// to the directory. Otherwise the directory is meaningless, but it
248    /// is still used as a key for file entries.
249    ///
250    /// If `info` is `None`, then new entries are assigned
251    /// default information, and existing entries are unmodified.
252    ///
253    /// If `info` is not `None`, then it is always assigned to the
254    /// entry, even if the entry already exists.
255    ///
256    /// # Panics
257    ///
258    /// Panics if 'file' is empty or contains a null byte.
259    pub fn add_file(
260        &mut self,
261        file: LineString,
262        directory: DirectoryId,
263        info: Option<FileInfo>,
264    ) -> FileId {
265        if let LineString::String(ref val) = file {
266            if self.encoding.version <= 4 {
267                assert!(!val.is_empty());
268            }
269            assert!(!val.contains(&0));
270        }
271
272        let key = (file, directory);
273        let index = if let Some(info) = info {
274            let (index, _) = self.files.insert_full(key, info);
275            index
276        } else {
277            let entry = self.files.entry(key);
278            let index = entry.index();
279            entry.or_default();
280            index
281        };
282        FileId::new(index)
283    }
284
285    /// Get an iterator for the files.
286    pub fn files(&self) -> impl Iterator<Item = (FileId, &LineString, DirectoryId)> {
287        self.files
288            .iter()
289            .enumerate()
290            .map(move |(index, entry)| (FileId::new(index), &(entry.0).0, (entry.0).1))
291    }
292
293    /// Get a reference to a file entry.
294    ///
295    /// # Panics
296    ///
297    /// Panics if `id` is invalid.
298    pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) {
299        self.files
300            .get_index(id.index())
301            .map(|entry| (&(entry.0).0, (entry.0).1))
302            .unwrap()
303    }
304
305    /// Get a reference to the info for a file entry.
306    ///
307    /// # Panics
308    ///
309    /// Panics if `id` is invalid.
310    pub fn get_file_info(&self, id: FileId) -> &FileInfo {
311        self.files
312            .get_index(id.index())
313            .map(|entry| entry.1)
314            .unwrap()
315    }
316
317    /// Get a mutable reference to the info for a file entry.
318    ///
319    /// # Panics
320    ///
321    /// Panics if `id` is invalid.
322    pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo {
323        self.files
324            .get_index_mut(id.index())
325            .map(|entry| entry.1)
326            .unwrap()
327    }
328
329    /// Begin a new sequence and set its base address.
330    ///
331    /// # Panics
332    ///
333    /// Panics if a sequence has already begun.
334    pub fn begin_sequence(&mut self, address: Option<Address>) {
335        assert!(!self.in_sequence);
336        self.in_sequence = true;
337        if let Some(address) = address {
338            self.instructions.push(LineInstruction::SetAddress(address));
339        }
340    }
341
342    /// End the sequence, and reset the row to its default values.
343    ///
344    /// Only the `address_offset` and op_index` fields of the current row are used.
345    ///
346    /// # Panics
347    ///
348    /// Panics if a sequence has not begun.
349    pub fn end_sequence(&mut self, address_offset: u64) {
350        assert!(self.in_sequence);
351        self.in_sequence = false;
352        self.row.address_offset = address_offset;
353        let op_advance = self.op_advance();
354        if op_advance != 0 {
355            self.instructions
356                .push(LineInstruction::AdvancePc(op_advance));
357        }
358        self.instructions.push(LineInstruction::EndSequence);
359        self.prev_row = LineRow::initial_state(self.encoding, self.line_encoding);
360        self.row = LineRow::initial_state(self.encoding, self.line_encoding);
361    }
362
363    /// Return true if a sequence has begun.
364    #[inline]
365    pub fn in_sequence(&self) -> bool {
366        self.in_sequence
367    }
368
369    /// Returns a reference to the data for the current row.
370    #[inline]
371    pub fn row(&mut self) -> &mut LineRow {
372        &mut self.row
373    }
374
375    /// Generates the line number information instructions for the current row.
376    ///
377    /// After the instructions are generated, it sets `discriminator` to 0, and sets
378    /// `basic_block`, `prologue_end`, and `epilogue_begin` to false.
379    ///
380    /// # Panics
381    ///
382    /// Panics if a sequence has not begun.
383    /// Panics if the address_offset decreases.
384    pub fn generate_row(&mut self) {
385        assert!(self.in_sequence);
386
387        // Output fields that are reset on every row.
388        if self.row.discriminator != 0 {
389            self.instructions
390                .push(LineInstruction::SetDiscriminator(self.row.discriminator));
391            self.row.discriminator = 0;
392        }
393        if self.row.basic_block {
394            self.instructions.push(LineInstruction::SetBasicBlock);
395            self.row.basic_block = false;
396        }
397        if self.row.prologue_end {
398            self.instructions.push(LineInstruction::SetPrologueEnd);
399            self.row.prologue_end = false;
400        }
401        if self.row.epilogue_begin {
402            self.instructions.push(LineInstruction::SetEpilogueBegin);
403            self.row.epilogue_begin = false;
404        }
405
406        // Output fields that are not reset on every row.
407        if self.row.is_statement != self.prev_row.is_statement {
408            self.instructions.push(LineInstruction::NegateStatement);
409        }
410        if self.row.file != self.prev_row.file {
411            self.instructions
412                .push(LineInstruction::SetFile(self.row.file));
413        }
414        if self.row.column != self.prev_row.column {
415            self.instructions
416                .push(LineInstruction::SetColumn(self.row.column));
417        }
418        if self.row.isa != self.prev_row.isa {
419            self.instructions
420                .push(LineInstruction::SetIsa(self.row.isa));
421        }
422
423        // Advance the line, address, and operation index.
424        let line_base = i64::from(self.line_encoding.line_base) as u64;
425        let line_range = u64::from(self.line_encoding.line_range);
426        let line_advance = self.row.line as i64 - self.prev_row.line as i64;
427        let op_advance = self.op_advance();
428
429        // Default to special advances of 0.
430        let special_base = u64::from(OPCODE_BASE);
431        // TODO: handle lack of special opcodes for 0 line advance
432        debug_assert!(self.line_encoding.line_base <= 0);
433        debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0);
434        let special_default = special_base.wrapping_sub(line_base);
435        let mut special = special_default;
436        let mut use_special = false;
437
438        if line_advance != 0 {
439            let special_line = (line_advance as u64).wrapping_sub(line_base);
440            if special_line < line_range {
441                special = special_base + special_line;
442                use_special = true;
443            } else {
444                self.instructions
445                    .push(LineInstruction::AdvanceLine(line_advance));
446            }
447        }
448
449        if op_advance != 0 {
450            // Using ConstAddPc can save a byte.
451            let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 {
452                (op_advance, false)
453            } else {
454                let op_range = (255 - special_base) / line_range;
455                (op_advance - op_range, true)
456            };
457
458            let special_op = special_op_advance * line_range;
459            if special + special_op <= 255 {
460                special += special_op;
461                use_special = true;
462                if const_add_pc {
463                    self.instructions.push(LineInstruction::ConstAddPc);
464                }
465            } else {
466                self.instructions
467                    .push(LineInstruction::AdvancePc(op_advance));
468            }
469        }
470
471        if use_special && special != special_default {
472            debug_assert!(special >= special_base);
473            debug_assert!(special <= 255);
474            self.instructions
475                .push(LineInstruction::Special(special as u8));
476        } else {
477            self.instructions.push(LineInstruction::Copy);
478        }
479
480        self.prev_row = self.row;
481    }
482
483    fn op_advance(&self) -> u64 {
484        debug_assert!(self.row.address_offset >= self.prev_row.address_offset);
485        let mut address_advance = self.row.address_offset - self.prev_row.address_offset;
486        if self.line_encoding.minimum_instruction_length != 1 {
487            debug_assert_eq!(
488                self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length),
489                0
490            );
491            address_advance /= u64::from(self.line_encoding.minimum_instruction_length);
492        }
493        address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction)
494            + self.row.op_index
495            - self.prev_row.op_index
496    }
497
498    /// Returns true if the line number program has no instructions.
499    ///
500    /// Does not check the file or directory entries.
501    #[inline]
502    pub fn is_empty(&self) -> bool {
503        self.instructions.is_empty()
504    }
505
506    /// Write the line number program to the given section.
507    ///
508    /// # Panics
509    ///
510    /// Panics if `self.is_none()`.
511    pub fn write<W: Writer>(
512        &self,
513        w: &mut DebugLine<W>,
514        encoding: Encoding,
515        debug_line_str_offsets: &DebugLineStrOffsets,
516        debug_str_offsets: &DebugStrOffsets,
517    ) -> Result<DebugLineOffset> {
518        assert!(!self.is_none());
519
520        if encoding.version < self.version()
521            || encoding.format != self.format()
522            || encoding.address_size != self.address_size()
523        {
524            return Err(Error::IncompatibleLineProgramEncoding);
525        }
526
527        let offset = w.offset();
528
529        let length_offset = w.write_initial_length(self.format())?;
530        let length_base = w.len();
531
532        if self.version() < 2 || self.version() > 5 {
533            return Err(Error::UnsupportedVersion(self.version()));
534        }
535        w.write_u16(self.version())?;
536
537        if self.version() >= 5 {
538            w.write_u8(self.address_size())?;
539            // Segment selector size.
540            w.write_u8(0)?;
541        }
542
543        let header_length_offset = w.len();
544        w.write_udata(0, self.format().word_size())?;
545        let header_length_base = w.len();
546
547        w.write_u8(self.line_encoding.minimum_instruction_length)?;
548        if self.version() >= 4 {
549            w.write_u8(self.line_encoding.maximum_operations_per_instruction)?;
550        } else if self.line_encoding.maximum_operations_per_instruction != 1 {
551            return Err(Error::NeedVersion(4));
552        };
553        w.write_u8(if self.line_encoding.default_is_stmt {
554            1
555        } else {
556            0
557        })?;
558        w.write_u8(self.line_encoding.line_base as u8)?;
559        w.write_u8(self.line_encoding.line_range)?;
560        w.write_u8(OPCODE_BASE)?;
561        w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?;
562
563        if self.version() <= 4 {
564            // The first directory is stored as DW_AT_comp_dir.
565            for dir in self.directories.iter().skip(1) {
566                dir.write(
567                    w,
568                    constants::DW_FORM_string,
569                    self.encoding,
570                    debug_line_str_offsets,
571                    debug_str_offsets,
572                )?;
573            }
574            w.write_u8(0)?;
575
576            for ((file, dir), info) in self.files.iter() {
577                file.write(
578                    w,
579                    constants::DW_FORM_string,
580                    self.encoding,
581                    debug_line_str_offsets,
582                    debug_str_offsets,
583                )?;
584                w.write_uleb128(dir.0 as u64)?;
585                w.write_uleb128(info.timestamp)?;
586                w.write_uleb128(info.size)?;
587            }
588            w.write_u8(0)?;
589        } else {
590            // Directory entry formats (only ever 1).
591            w.write_u8(1)?;
592            w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
593            let dir_form = self.directories.get_index(0).unwrap().form();
594            w.write_uleb128(dir_form.0.into())?;
595
596            // Directory entries.
597            w.write_uleb128(self.directories.len() as u64)?;
598            for dir in self.directories.iter() {
599                dir.write(
600                    w,
601                    dir_form,
602                    self.encoding,
603                    debug_line_str_offsets,
604                    debug_str_offsets,
605                )?;
606            }
607
608            // File name entry formats.
609            let count = 2
610                + if self.file_has_timestamp { 1 } else { 0 }
611                + if self.file_has_size { 1 } else { 0 }
612                + if self.file_has_md5 { 1 } else { 0 }
613                + if self.file_has_source { 1 } else { 0 };
614            w.write_u8(count)?;
615            w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
616            let file_form = (self.files.get_index(0).unwrap().0).0.form();
617            w.write_uleb128(file_form.0.into())?;
618            w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?;
619            w.write_uleb128(constants::DW_FORM_udata.0.into())?;
620            if self.file_has_timestamp {
621                w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?;
622                w.write_uleb128(constants::DW_FORM_udata.0.into())?;
623            }
624            if self.file_has_size {
625                w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?;
626                w.write_uleb128(constants::DW_FORM_udata.0.into())?;
627            }
628            if self.file_has_md5 {
629                w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?;
630                w.write_uleb128(constants::DW_FORM_data16.0.into())?;
631            }
632            let file_source_form = self
633                .files
634                .iter()
635                .find_map(|file| file.1.source.as_ref().map(LineString::form))
636                .unwrap_or(constants::DW_FORM_string);
637            // Create a string to use for files with no source.
638            // Note: An empty DW_LNCT_LLVM_source is interpreted as missing
639            // source code. Included source code should always be
640            // terminated by a "\n" line ending.
641            let file_source_empty = match file_source_form {
642                // If any file source is set, then `get_empty` will succeed.
643                // If all are missing then `file_source_form` will be `DW_FORM_string`.
644                constants::DW_FORM_line_strp => debug_line_str_offsets
645                    .get_empty()
646                    .map(LineString::LineStringRef),
647                constants::DW_FORM_strp => debug_str_offsets.get_empty().map(LineString::StringRef),
648                _ => None,
649            }
650            .unwrap_or(LineString::String(Vec::new()));
651            if self.file_has_source {
652                w.write_uleb128(u64::from(constants::DW_LNCT_LLVM_source.0))?;
653                w.write_uleb128(file_source_form.0.into())?;
654            }
655
656            // File name entries.
657            w.write_uleb128(self.files.len() as u64)?;
658            let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| {
659                file.write(
660                    w,
661                    file_form,
662                    self.encoding,
663                    debug_line_str_offsets,
664                    debug_str_offsets,
665                )?;
666                w.write_uleb128(dir.0 as u64)?;
667                if self.file_has_timestamp {
668                    w.write_uleb128(info.timestamp)?;
669                }
670                if self.file_has_size {
671                    w.write_uleb128(info.size)?;
672                }
673                if self.file_has_md5 {
674                    w.write(&info.md5)?;
675                }
676                if self.file_has_source {
677                    let source = info.source.as_ref().unwrap_or(&file_source_empty);
678                    source.write(
679                        w,
680                        file_source_form,
681                        self.encoding,
682                        debug_line_str_offsets,
683                        debug_str_offsets,
684                    )?;
685                }
686                Ok(())
687            };
688            for ((file, dir), info) in self.files.iter() {
689                write_file(file, *dir, info)?;
690            }
691        }
692
693        let header_length = (w.len() - header_length_base) as u64;
694        w.write_udata_at(
695            header_length_offset,
696            header_length,
697            self.format().word_size(),
698        )?;
699
700        for instruction in &self.instructions {
701            instruction.write(w, self.encoding)?;
702        }
703
704        let length = (w.len() - length_base) as u64;
705        w.write_initial_length_at(length_offset, length, self.format())?;
706
707        Ok(offset)
708    }
709}
710
711/// A row in the line number table that corresponds to a machine instruction.
712#[derive(Debug, Clone, Copy)]
713pub struct LineRow {
714    /// The offset of the instruction from the start address of the sequence.
715    pub address_offset: u64,
716    /// The index of an operation within a VLIW instruction.
717    ///
718    /// The index of the first operation is 0.
719    /// Set to 0 for non-VLIW instructions.
720    pub op_index: u64,
721
722    /// The source file corresponding to the instruction.
723    pub file: FileId,
724    /// The line number within the source file.
725    ///
726    /// Lines are numbered beginning at 1. Set to 0 if there is no source line.
727    pub line: u64,
728    /// The column number within the source line.
729    ///
730    /// Columns are numbered beginning at 1. Set to 0 for the "left edge" of the line.
731    pub column: u64,
732    /// An additional discriminator used to distinguish between source locations.
733    /// This value is assigned arbitrarily by the DWARF producer.
734    pub discriminator: u64,
735
736    /// Set to true if the instruction is a recommended breakpoint for a statement.
737    pub is_statement: bool,
738    /// Set to true if the instruction is the beginning of a basic block.
739    pub basic_block: bool,
740    /// Set to true if the instruction is a recommended breakpoint at the entry of a
741    /// function.
742    pub prologue_end: bool,
743    /// Set to true if the instruction is a recommended breakpoint prior to the exit of
744    /// a function.
745    pub epilogue_begin: bool,
746
747    /// The instruction set architecture of the instruction.
748    ///
749    /// Set to 0 for the default ISA. Other values are defined by the architecture ABI.
750    pub isa: u64,
751}
752
753impl LineRow {
754    /// Return the initial state as specified in the DWARF standard.
755    fn initial_state(encoding: Encoding, line_encoding: LineEncoding) -> Self {
756        LineRow {
757            address_offset: 0,
758            op_index: 0,
759
760            file: FileId::initial_state(encoding.version),
761            line: 1,
762            column: 0,
763            discriminator: 0,
764
765            is_statement: line_encoding.default_is_stmt,
766            basic_block: false,
767            prologue_end: false,
768            epilogue_begin: false,
769
770            isa: 0,
771        }
772    }
773}
774
775/// An instruction in a line number program.
776#[derive(Debug, Clone, Copy, PartialEq, Eq)]
777enum LineInstruction {
778    // Special opcodes
779    Special(u8),
780
781    // Standard opcodes
782    Copy,
783    AdvancePc(u64),
784    AdvanceLine(i64),
785    SetFile(FileId),
786    SetColumn(u64),
787    NegateStatement,
788    SetBasicBlock,
789    ConstAddPc,
790    // DW_LNS_fixed_advance_pc is not supported.
791    SetPrologueEnd,
792    SetEpilogueBegin,
793    SetIsa(u64),
794
795    // Extended opcodes
796    EndSequence,
797    // TODO: this doubles the size of this enum.
798    SetAddress(Address),
799    // DW_LNE_define_file is not supported.
800    SetDiscriminator(u64),
801}
802
803impl LineInstruction {
804    /// Write the line number instruction to the given section.
805    fn write<W: Writer>(self, w: &mut DebugLine<W>, encoding: Encoding) -> Result<()> {
806        use self::LineInstruction::*;
807        match self {
808            Special(val) => w.write_u8(val)?,
809            Copy => w.write_u8(constants::DW_LNS_copy.0)?,
810            AdvancePc(val) => {
811                w.write_u8(constants::DW_LNS_advance_pc.0)?;
812                w.write_uleb128(val)?;
813            }
814            AdvanceLine(val) => {
815                w.write_u8(constants::DW_LNS_advance_line.0)?;
816                w.write_sleb128(val)?;
817            }
818            SetFile(val) => {
819                w.write_u8(constants::DW_LNS_set_file.0)?;
820                w.write_uleb128(val.raw(encoding.version))?;
821            }
822            SetColumn(val) => {
823                w.write_u8(constants::DW_LNS_set_column.0)?;
824                w.write_uleb128(val)?;
825            }
826            NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?,
827            SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?,
828            ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?,
829            SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?,
830            SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?,
831            SetIsa(val) => {
832                w.write_u8(constants::DW_LNS_set_isa.0)?;
833                w.write_uleb128(val)?;
834            }
835            EndSequence => {
836                w.write_u8(0)?;
837                w.write_uleb128(1)?;
838                w.write_u8(constants::DW_LNE_end_sequence.0)?;
839            }
840            SetAddress(address) => {
841                w.write_u8(0)?;
842                w.write_uleb128(1 + u64::from(encoding.address_size))?;
843                w.write_u8(constants::DW_LNE_set_address.0)?;
844                w.write_address(address, encoding.address_size)?;
845            }
846            SetDiscriminator(val) => {
847                let mut bytes = [0u8; 10];
848                // bytes is long enough so this will never fail.
849                let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap();
850                w.write_u8(0)?;
851                w.write_uleb128(1 + len as u64)?;
852                w.write_u8(constants::DW_LNE_set_discriminator.0)?;
853                w.write(&bytes[..len])?;
854            }
855        }
856        Ok(())
857    }
858}
859
860/// A string value for use in defining paths in line number programs.
861#[derive(Debug, Clone, PartialEq, Eq, Hash)]
862pub enum LineString {
863    /// A slice of bytes representing a string. Must not include null bytes.
864    /// Not guaranteed to be UTF-8 or anything like that.
865    String(Vec<u8>),
866
867    /// A reference to a string in the `.debug_str` section.
868    StringRef(StringId),
869
870    /// A reference to a string in the `.debug_line_str` section.
871    LineStringRef(LineStringId),
872}
873
874impl LineString {
875    /// Create a `LineString` using the normal form for the given encoding.
876    pub fn new<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self
877    where
878        T: Into<Vec<u8>>,
879    {
880        let val = val.into();
881        if encoding.version <= 4 {
882            LineString::String(val)
883        } else {
884            LineString::LineStringRef(line_strings.add(val))
885        }
886    }
887
888    /// Get a reference to the string data.
889    pub fn get<'a>(
890        &'a self,
891        strings: &'a StringTable,
892        line_strings: &'a LineStringTable,
893    ) -> &'a [u8] {
894        match self {
895            LineString::String(val) => val,
896            LineString::StringRef(val) => strings.get(*val),
897            LineString::LineStringRef(val) => line_strings.get(*val),
898        }
899    }
900
901    fn form(&self) -> constants::DwForm {
902        match *self {
903            LineString::String(..) => constants::DW_FORM_string,
904            LineString::StringRef(..) => constants::DW_FORM_strp,
905            LineString::LineStringRef(..) => constants::DW_FORM_line_strp,
906        }
907    }
908
909    fn write<W: Writer>(
910        &self,
911        w: &mut DebugLine<W>,
912        form: constants::DwForm,
913        encoding: Encoding,
914        debug_line_str_offsets: &DebugLineStrOffsets,
915        debug_str_offsets: &DebugStrOffsets,
916    ) -> Result<()> {
917        if form != self.form() {
918            return Err(Error::LineStringFormMismatch);
919        }
920
921        match *self {
922            LineString::String(ref val) => {
923                if encoding.version <= 4 {
924                    debug_assert!(!val.is_empty());
925                }
926                w.write(val)?;
927                w.write_u8(0)?;
928            }
929            LineString::StringRef(val) => {
930                if encoding.version < 5 {
931                    return Err(Error::NeedVersion(5));
932                }
933                w.write_offset(
934                    debug_str_offsets.get(val).0,
935                    SectionId::DebugStr,
936                    encoding.format.word_size(),
937                )?;
938            }
939            LineString::LineStringRef(val) => {
940                if encoding.version < 5 {
941                    return Err(Error::NeedVersion(5));
942                }
943                w.write_offset(
944                    debug_line_str_offsets.get(val).0,
945                    SectionId::DebugLineStr,
946                    encoding.format.word_size(),
947                )?;
948            }
949        }
950        Ok(())
951    }
952}
953
954/// An identifier for a directory in a `LineProgram`.
955///
956/// Defaults to the working directory of the compilation unit.
957#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
958pub struct DirectoryId(usize);
959
960// Force FileId access via the methods.
961mod id {
962    /// An identifier for a file in a `LineProgram`.
963    //
964    // We internally use a 0-based index for all versions, but
965    // emit a 1-based index for DWARF version <= 4.
966    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
967    pub struct FileId(usize);
968
969    impl FileId {
970        /// Create a `FileId` given a 0-based index into `LineProgram::files`.
971        pub(crate) fn new(index: usize) -> Self {
972            FileId(index)
973        }
974
975        /// The 0-based index of the file in `LineProgram::files`.
976        pub(super) fn index(self) -> usize {
977            self.0
978        }
979
980        /// The initial state of the file register.
981        pub(super) fn initial_state(version: u16) -> Self {
982            if version == 5 {
983                // For version 5, the files are 0-based and the default file is 1,
984                // which is a 0-based index of 1.
985                FileId(1)
986            } else {
987                // For version <= 4, the files are 1-based and the default file is 1,
988                // which is a 0-based index of 0.
989                // For version >= 6, the files are 0-based and the default file is 0,
990                // which is a 0-based index of 0.
991                FileId(0)
992            }
993        }
994
995        /// Convert to a raw value used for writing.
996        ///
997        /// This converts to a 1-based index for DWARF version <= 4.
998        pub(crate) fn raw(self, version: u16) -> u64 {
999            if version <= 4 {
1000                self.0 as u64 + 1
1001            } else {
1002                self.0 as u64
1003            }
1004        }
1005    }
1006}
1007pub use self::id::*;
1008
1009/// Extra information for file in a `LineProgram`.
1010#[derive(Debug, Default, Clone, PartialEq, Eq)]
1011pub struct FileInfo {
1012    /// The implementation defined timestamp of the last modification of the file,
1013    /// or 0 if not available.
1014    pub timestamp: u64,
1015
1016    /// The size of the file in bytes, or 0 if not available.
1017    pub size: u64,
1018
1019    /// A 16-byte MD5 digest of the file contents.
1020    ///
1021    /// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`.
1022    pub md5: [u8; 16],
1023
1024    /// Optionally some embedded sourcecode.
1025    ///
1026    /// Only used if version >= 5 and `LineProgram::file_has_source` is `true`.
1027    pub source: Option<LineString>,
1028}
1029
1030define_section!(
1031    DebugLine,
1032    DebugLineOffset,
1033    "A writable `.debug_line` section."
1034);
1035
1036#[cfg(feature = "read")]
1037mod convert {
1038    use super::*;
1039    use crate::read::{self, Reader};
1040    use crate::write::{self, ConvertError, ConvertResult};
1041
1042    impl LineProgram {
1043        /// Create a line number program by reading the data from the given program.
1044        ///
1045        /// Return the program and a mapping from file index to `FileId`.
1046        pub fn from<R: Reader<Offset = usize>>(
1047            mut from_program: read::IncompleteLineProgram<R>,
1048            dwarf: &read::Dwarf<R>,
1049            line_strings: &mut write::LineStringTable,
1050            strings: &mut write::StringTable,
1051            convert_address: &dyn Fn(u64) -> Option<Address>,
1052        ) -> ConvertResult<(LineProgram, Vec<FileId>)> {
1053            // Create mappings in case the source has duplicate files or directories.
1054            let mut dirs = Vec::new();
1055            let mut files = Vec::new();
1056
1057            let mut program = {
1058                let from_header = from_program.header();
1059                let encoding = from_header.encoding();
1060
1061                let working_dir = match from_header.directory(0) {
1062                    Some(working_dir) => {
1063                        LineString::from(working_dir, dwarf, line_strings, strings)?
1064                    }
1065                    None => LineString::new(&[][..], encoding, line_strings),
1066                };
1067
1068                let (source_dir, source_file) = match from_header.file(0) {
1069                    Some(source_file) => {
1070                        let source_dir_index = source_file.directory_index();
1071                        let source_dir = if source_dir_index != 0 {
1072                            match from_header.directory(source_dir_index) {
1073                                Some(source_dir) => Some(LineString::from(
1074                                    source_dir,
1075                                    dwarf,
1076                                    line_strings,
1077                                    strings,
1078                                )?),
1079                                None => return Err(ConvertError::InvalidDirectoryIndex),
1080                            }
1081                        } else {
1082                            None
1083                        };
1084                        let source_file = LineString::from(
1085                            source_file.path_name(),
1086                            dwarf,
1087                            line_strings,
1088                            strings,
1089                        )?;
1090                        (source_dir, source_file)
1091                    }
1092                    None => (None, LineString::new(&[][..], encoding, line_strings)),
1093                };
1094
1095                if from_header.line_base() > 0 {
1096                    return Err(ConvertError::InvalidLineBase);
1097                }
1098                let mut program = LineProgram::new(
1099                    encoding,
1100                    from_header.line_encoding(),
1101                    working_dir,
1102                    source_dir,
1103                    source_file,
1104                    None, // We'll set this later if needed when we add the file again.
1105                );
1106
1107                if from_header.version() <= 4 {
1108                    // The first directory is implicit.
1109                    dirs.push(DirectoryId(0));
1110                    // A file index of 0 is invalid for version <= 4, but putting
1111                    // something there makes the indexing easier.
1112                    files.push(FileId::new(0));
1113                }
1114
1115                for from_dir in from_header.include_directories() {
1116                    let from_dir =
1117                        LineString::from(from_dir.clone(), dwarf, line_strings, strings)?;
1118                    dirs.push(program.add_directory(from_dir));
1119                }
1120
1121                program.file_has_timestamp = from_header.file_has_timestamp();
1122                program.file_has_size = from_header.file_has_size();
1123                program.file_has_md5 = from_header.file_has_md5();
1124                program.file_has_source = from_header.file_has_source();
1125                for from_file in from_header.file_names().iter() {
1126                    let from_name =
1127                        LineString::from(from_file.path_name(), dwarf, line_strings, strings)?;
1128                    let from_dir = from_file.directory_index();
1129                    if from_dir >= dirs.len() as u64 {
1130                        return Err(ConvertError::InvalidDirectoryIndex);
1131                    }
1132                    let from_dir = dirs[from_dir as usize];
1133                    let from_info = Some(FileInfo {
1134                        timestamp: from_file.timestamp(),
1135                        size: from_file.size(),
1136                        md5: *from_file.md5(),
1137                        source: match from_file.source() {
1138                            Some(source) => {
1139                                Some(LineString::from(source, dwarf, line_strings, strings)?)
1140                            }
1141                            None => None,
1142                        },
1143                    });
1144                    files.push(program.add_file(from_name, from_dir, from_info));
1145                }
1146
1147                program
1148            };
1149
1150            // We can't use the `from_program.rows()` because that wouldn't let
1151            // us preserve address relocations.
1152            let mut from_row = read::LineRow::new(from_program.header());
1153            let mut instructions = from_program.header().instructions();
1154            let mut address = None;
1155            while let Some(instruction) = instructions.next_instruction(from_program.header())? {
1156                match instruction {
1157                    read::LineInstruction::SetAddress(val) => {
1158                        if program.in_sequence() {
1159                            return Err(ConvertError::UnsupportedLineInstruction);
1160                        }
1161                        match convert_address(val) {
1162                            Some(val) => address = Some(val),
1163                            None => return Err(ConvertError::InvalidAddress),
1164                        }
1165                        from_row
1166                            .execute(read::LineInstruction::SetAddress(0), &mut from_program)?;
1167                    }
1168                    read::LineInstruction::DefineFile(_) => {
1169                        return Err(ConvertError::UnsupportedLineInstruction);
1170                    }
1171                    _ => {
1172                        if from_row.execute(instruction, &mut from_program)? {
1173                            if !program.in_sequence() {
1174                                program.begin_sequence(address);
1175                                address = None;
1176                            }
1177                            if from_row.end_sequence() {
1178                                program.end_sequence(from_row.address());
1179                            } else {
1180                                program.row().address_offset = from_row.address();
1181                                program.row().op_index = from_row.op_index();
1182                                program.row().file = {
1183                                    let file = from_row.file_index();
1184                                    if file >= files.len() as u64 {
1185                                        return Err(ConvertError::InvalidFileIndex);
1186                                    }
1187                                    if file == 0 && program.version() <= 4 {
1188                                        return Err(ConvertError::InvalidFileIndex);
1189                                    }
1190                                    files[file as usize]
1191                                };
1192                                program.row().line = match from_row.line() {
1193                                    Some(line) => line.get(),
1194                                    None => 0,
1195                                };
1196                                program.row().column = match from_row.column() {
1197                                    read::ColumnType::LeftEdge => 0,
1198                                    read::ColumnType::Column(val) => val.get(),
1199                                };
1200                                program.row().discriminator = from_row.discriminator();
1201                                program.row().is_statement = from_row.is_stmt();
1202                                program.row().basic_block = from_row.basic_block();
1203                                program.row().prologue_end = from_row.prologue_end();
1204                                program.row().epilogue_begin = from_row.epilogue_begin();
1205                                program.row().isa = from_row.isa();
1206                                program.generate_row();
1207                            }
1208                            from_row.reset(from_program.header());
1209                        }
1210                    }
1211                };
1212            }
1213            Ok((program, files))
1214        }
1215    }
1216
1217    impl LineString {
1218        fn from<R: Reader<Offset = usize>>(
1219            from_attr: read::AttributeValue<R>,
1220            dwarf: &read::Dwarf<R>,
1221            line_strings: &mut write::LineStringTable,
1222            strings: &mut write::StringTable,
1223        ) -> ConvertResult<LineString> {
1224            Ok(match from_attr {
1225                read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()),
1226                read::AttributeValue::DebugStrRef(offset) => {
1227                    let r = dwarf.debug_str.get_str(offset)?;
1228                    let id = strings.add(r.to_slice()?);
1229                    LineString::StringRef(id)
1230                }
1231                read::AttributeValue::DebugLineStrRef(offset) => {
1232                    let r = dwarf.debug_line_str.get_str(offset)?;
1233                    let id = line_strings.add(r.to_slice()?);
1234                    LineString::LineStringRef(id)
1235                }
1236                _ => return Err(ConvertError::UnsupportedLineStringForm),
1237            })
1238        }
1239    }
1240}
1241
1242#[cfg(test)]
1243#[cfg(feature = "read")]
1244mod tests {
1245    use super::*;
1246    use crate::read;
1247    use crate::write::{AttributeValue, Dwarf, EndianVec, Sections, Unit};
1248    use crate::LittleEndian;
1249
1250    #[test]
1251    fn test_line_program() {
1252        let dir1 = LineString::String(b"dir1".to_vec());
1253        let file1 = LineString::String(b"file1".to_vec());
1254        let dir2 = LineString::String(b"dir2".to_vec());
1255        let file2 = LineString::String(b"file2".to_vec());
1256
1257        let mut dwarf = Dwarf::new();
1258
1259        for &version in &[2, 3, 4, 5] {
1260            for &address_size in &[4, 8] {
1261                for &format in &[Format::Dwarf32, Format::Dwarf64] {
1262                    let encoding = Encoding {
1263                        format,
1264                        version,
1265                        address_size,
1266                    };
1267                    let mut program = LineProgram::new(
1268                        encoding,
1269                        LineEncoding::default(),
1270                        dir1.clone(),
1271                        None,
1272                        file1.clone(),
1273                        None,
1274                    );
1275
1276                    assert_eq!(&dir1, program.get_directory(program.default_directory()));
1277                    program.file_has_timestamp = true;
1278                    program.file_has_size = true;
1279                    if encoding.version >= 5 {
1280                        program.file_has_md5 = true;
1281                    }
1282
1283                    // Note: Embedded source code is an accepted extension
1284                    // that will become part of DWARF v6. We're using the LLVM extension
1285                    // here for v5.
1286                    if encoding.version >= 5 {
1287                        program.file_has_source = true;
1288                    }
1289
1290                    let dir_id = program.add_directory(dir2.clone());
1291                    assert_eq!(&dir2, program.get_directory(dir_id));
1292                    assert_eq!(dir_id, program.add_directory(dir2.clone()));
1293
1294                    let file_info = FileInfo {
1295                        timestamp: 1,
1296                        size: 2,
1297                        md5: if encoding.version >= 5 {
1298                            [3; 16]
1299                        } else {
1300                            [0; 16]
1301                        },
1302                        source: (encoding.version >= 5)
1303                            .then(|| LineString::String(b"the source code\n".to_vec())),
1304                    };
1305                    let file_id = program.add_file(file2.clone(), dir_id, Some(file_info.clone()));
1306                    assert_eq!((&file2, dir_id), program.get_file(file_id));
1307                    assert_eq!(file_info, *program.get_file_info(file_id));
1308
1309                    program.get_file_info_mut(file_id).size = 3;
1310                    assert_ne!(file_info, *program.get_file_info(file_id));
1311                    assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None));
1312                    assert_ne!(file_info, *program.get_file_info(file_id));
1313                    assert_eq!(
1314                        file_id,
1315                        program.add_file(file2.clone(), dir_id, Some(file_info.clone()))
1316                    );
1317                    assert_eq!(file_info, *program.get_file_info(file_id));
1318
1319                    let mut unit = Unit::new(encoding, program);
1320                    let root = unit.get_mut(unit.root());
1321                    root.set(
1322                        constants::DW_AT_comp_dir,
1323                        AttributeValue::String(b"dir1".to_vec()),
1324                    );
1325                    root.set(
1326                        constants::DW_AT_name,
1327                        AttributeValue::String(b"file1".to_vec()),
1328                    );
1329                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1330                    root.set(
1331                        constants::DW_AT_decl_file,
1332                        AttributeValue::FileIndex(Some(file_id)),
1333                    );
1334
1335                    dwarf.units.add(unit);
1336                }
1337            }
1338        }
1339
1340        let mut sections = Sections::new(EndianVec::new(LittleEndian));
1341        dwarf.write(&mut sections).unwrap();
1342        let read_dwarf = sections.read(LittleEndian);
1343        let convert_dwarf =
1344            Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap();
1345
1346        let mut convert_units = convert_dwarf.units.iter();
1347        for (_, unit) in dwarf.units.iter() {
1348            let program = &unit.line_program;
1349            let root = unit.get(unit.root());
1350            let Some(AttributeValue::FileIndex(Some(file_id))) =
1351                root.get(constants::DW_AT_decl_file)
1352            else {
1353                panic!("missing DW_AT_decl_file");
1354            };
1355
1356            let (_, convert_unit) = convert_units.next().unwrap();
1357            let convert_program = &convert_unit.line_program;
1358            let convert_root = convert_unit.get(convert_unit.root());
1359            let Some(AttributeValue::FileIndex(Some(convert_file_id))) =
1360                convert_root.get(constants::DW_AT_decl_file)
1361            else {
1362                panic!("missing DW_AT_decl_file");
1363            };
1364
1365            assert_eq!(convert_program.version(), program.version());
1366            assert_eq!(convert_program.address_size(), program.address_size());
1367            assert_eq!(convert_program.format(), program.format());
1368
1369            let (file, dir) = program.get_file(*file_id);
1370            let (convert_file, convert_dir) = convert_program.get_file(*convert_file_id);
1371            assert_eq!(file, convert_file);
1372            assert_eq!(
1373                program.get_directory(dir),
1374                convert_program.get_directory(convert_dir)
1375            );
1376            assert_eq!(
1377                program.get_file_info(*file_id),
1378                convert_program.get_file_info(*convert_file_id)
1379            );
1380        }
1381    }
1382
1383    #[test]
1384    fn test_line_row() {
1385        let dir1 = &b"dir1"[..];
1386        let file1 = &b"file1"[..];
1387        let file2 = &b"file2"[..];
1388
1389        for &version in &[2, 3, 4, 5] {
1390            for &address_size in &[4, 8] {
1391                for &format in &[Format::Dwarf32, Format::Dwarf64] {
1392                    let encoding = Encoding {
1393                        format,
1394                        version,
1395                        address_size,
1396                    };
1397                    let line_base = -5;
1398                    let line_range = 14;
1399                    let neg_line_base = (-line_base) as u8;
1400                    let mut program = LineProgram::new(
1401                        encoding,
1402                        LineEncoding {
1403                            line_base,
1404                            line_range,
1405                            ..Default::default()
1406                        },
1407                        LineString::String(dir1.to_vec()),
1408                        None,
1409                        LineString::String(file1.to_vec()),
1410                        None,
1411                    );
1412                    let dir_id = program.default_directory();
1413                    let file1_id =
1414                        program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1415                    let file2_id =
1416                        program.add_file(LineString::String(file2.to_vec()), dir_id, None);
1417
1418                    // Test sequences.
1419                    {
1420                        let mut program = program.clone();
1421                        let address = Address::Constant(0x12);
1422                        program.begin_sequence(Some(address));
1423                        assert_eq!(
1424                            program.instructions,
1425                            vec![LineInstruction::SetAddress(address)]
1426                        );
1427                    }
1428
1429                    {
1430                        let mut program = program.clone();
1431                        program.begin_sequence(None);
1432                        assert_eq!(program.instructions, Vec::new());
1433                    }
1434
1435                    {
1436                        let mut program = program.clone();
1437                        program.begin_sequence(None);
1438                        program.end_sequence(0x1234);
1439                        assert_eq!(
1440                            program.instructions,
1441                            vec![
1442                                LineInstruction::AdvancePc(0x1234),
1443                                LineInstruction::EndSequence
1444                            ]
1445                        );
1446                    }
1447
1448                    // Create a base program.
1449                    program.begin_sequence(None);
1450                    program.row.line = 0x1000;
1451                    program.generate_row();
1452                    let base_row = program.row;
1453                    let base_instructions = program.instructions.clone();
1454
1455                    // Create test cases.
1456                    let mut tests = Vec::new();
1457
1458                    let row = base_row;
1459                    tests.push((row, vec![LineInstruction::Copy]));
1460
1461                    let mut row = base_row;
1462                    row.line -= u64::from(neg_line_base);
1463                    tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)]));
1464
1465                    let mut row = base_row;
1466                    row.line += u64::from(line_range) - 1;
1467                    row.line -= u64::from(neg_line_base);
1468                    tests.push((
1469                        row,
1470                        vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)],
1471                    ));
1472
1473                    let mut row = base_row;
1474                    row.line += u64::from(line_range);
1475                    row.line -= u64::from(neg_line_base);
1476                    tests.push((
1477                        row,
1478                        vec![
1479                            LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)),
1480                            LineInstruction::Copy,
1481                        ],
1482                    ));
1483
1484                    let mut row = base_row;
1485                    row.address_offset = 1;
1486                    row.line -= u64::from(neg_line_base);
1487                    tests.push((
1488                        row,
1489                        vec![LineInstruction::Special(OPCODE_BASE + line_range)],
1490                    ));
1491
1492                    let op_range = (255 - OPCODE_BASE) / line_range;
1493                    let mut row = base_row;
1494                    row.address_offset = u64::from(op_range);
1495                    row.line -= u64::from(neg_line_base);
1496                    tests.push((
1497                        row,
1498                        vec![LineInstruction::Special(
1499                            OPCODE_BASE + op_range * line_range,
1500                        )],
1501                    ));
1502
1503                    let mut row = base_row;
1504                    row.address_offset = u64::from(op_range);
1505                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
1506                    row.line -= u64::from(neg_line_base);
1507                    tests.push((row, vec![LineInstruction::Special(255)]));
1508
1509                    let mut row = base_row;
1510                    row.address_offset = u64::from(op_range);
1511                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
1512                    row.line -= u64::from(neg_line_base);
1513                    tests.push((
1514                        row,
1515                        vec![LineInstruction::ConstAddPc, LineInstruction::Copy],
1516                    ));
1517
1518                    let mut row = base_row;
1519                    row.address_offset = u64::from(op_range);
1520                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
1521                    row.line -= u64::from(neg_line_base);
1522                    tests.push((
1523                        row,
1524                        vec![
1525                            LineInstruction::ConstAddPc,
1526                            LineInstruction::Special(OPCODE_BASE + 6),
1527                        ],
1528                    ));
1529
1530                    let mut row = base_row;
1531                    row.address_offset = u64::from(op_range) * 2;
1532                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
1533                    row.line -= u64::from(neg_line_base);
1534                    tests.push((
1535                        row,
1536                        vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)],
1537                    ));
1538
1539                    let mut row = base_row;
1540                    row.address_offset = u64::from(op_range) * 2;
1541                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
1542                    row.line -= u64::from(neg_line_base);
1543                    tests.push((
1544                        row,
1545                        vec![
1546                            LineInstruction::AdvancePc(row.address_offset),
1547                            LineInstruction::Copy,
1548                        ],
1549                    ));
1550
1551                    let mut row = base_row;
1552                    row.address_offset = u64::from(op_range) * 2;
1553                    row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
1554                    row.line -= u64::from(neg_line_base);
1555                    tests.push((
1556                        row,
1557                        vec![
1558                            LineInstruction::AdvancePc(row.address_offset),
1559                            LineInstruction::Special(OPCODE_BASE + 6),
1560                        ],
1561                    ));
1562
1563                    let mut row = base_row;
1564                    row.address_offset = 0x1234;
1565                    tests.push((
1566                        row,
1567                        vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy],
1568                    ));
1569
1570                    let mut row = base_row;
1571                    row.line += 0x1234;
1572                    tests.push((
1573                        row,
1574                        vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy],
1575                    ));
1576
1577                    let mut row = base_row;
1578                    row.file = file1_id;
1579                    if version == 5 {
1580                        // Version 5 is 0-based, but the default file is 1, so this row
1581                        // will need to set the file.
1582                        tests.push((
1583                            row,
1584                            vec![LineInstruction::SetFile(file1_id), LineInstruction::Copy],
1585                        ));
1586                    } else {
1587                        // This is the first file, so normally this is already the default.
1588                        tests.push((row, vec![LineInstruction::Copy]));
1589                    }
1590
1591                    let mut row = base_row;
1592                    row.file = file2_id;
1593                    if version == 5 {
1594                        tests.push((row, vec![LineInstruction::Copy]));
1595                    } else {
1596                        tests.push((
1597                            row,
1598                            vec![LineInstruction::SetFile(file2_id), LineInstruction::Copy],
1599                        ));
1600                    }
1601
1602                    let mut row = base_row;
1603                    row.column = 0x1234;
1604                    tests.push((
1605                        row,
1606                        vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy],
1607                    ));
1608
1609                    let mut row = base_row;
1610                    row.discriminator = 0x1234;
1611                    tests.push((
1612                        row,
1613                        vec![
1614                            LineInstruction::SetDiscriminator(0x1234),
1615                            LineInstruction::Copy,
1616                        ],
1617                    ));
1618
1619                    let mut row = base_row;
1620                    row.is_statement = !row.is_statement;
1621                    tests.push((
1622                        row,
1623                        vec![LineInstruction::NegateStatement, LineInstruction::Copy],
1624                    ));
1625
1626                    let mut row = base_row;
1627                    row.basic_block = true;
1628                    tests.push((
1629                        row,
1630                        vec![LineInstruction::SetBasicBlock, LineInstruction::Copy],
1631                    ));
1632
1633                    let mut row = base_row;
1634                    row.prologue_end = true;
1635                    tests.push((
1636                        row,
1637                        vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy],
1638                    ));
1639
1640                    let mut row = base_row;
1641                    row.epilogue_begin = true;
1642                    tests.push((
1643                        row,
1644                        vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy],
1645                    ));
1646
1647                    let mut row = base_row;
1648                    row.isa = 0x1234;
1649                    tests.push((
1650                        row,
1651                        vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy],
1652                    ));
1653
1654                    for test in tests {
1655                        // Test generate_row().
1656                        let mut program = program.clone();
1657                        program.row = test.0;
1658                        program.generate_row();
1659                        assert_eq!(
1660                            &program.instructions[base_instructions.len()..],
1661                            &test.1[..]
1662                        );
1663
1664                        // Test LineProgram::from().
1665                        let mut unit = Unit::new(encoding, program);
1666                        let root = unit.get_mut(unit.root());
1667                        root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1668
1669                        let mut dwarf = Dwarf::new();
1670                        dwarf.units.add(unit);
1671
1672                        let mut sections = Sections::new(EndianVec::new(LittleEndian));
1673                        dwarf.write(&mut sections).unwrap();
1674                        let read_dwarf = sections.read(LittleEndian);
1675
1676                        let convert_dwarf =
1677                            Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
1678                                .unwrap();
1679                        let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
1680                        let convert_program = &convert_unit.line_program;
1681
1682                        assert_eq!(
1683                            &convert_program.instructions[base_instructions.len()..],
1684                            &test.1[..]
1685                        );
1686                    }
1687                }
1688            }
1689        }
1690    }
1691
1692    #[test]
1693    fn test_line_instruction() {
1694        let dir1 = &b"dir1"[..];
1695        let file1 = &b"file1"[..];
1696
1697        for &version in &[2, 3, 4, 5] {
1698            for &address_size in &[4, 8] {
1699                for &format in &[Format::Dwarf32, Format::Dwarf64] {
1700                    let encoding = Encoding {
1701                        format,
1702                        version,
1703                        address_size,
1704                    };
1705                    let mut program = LineProgram::new(
1706                        encoding,
1707                        LineEncoding::default(),
1708                        LineString::String(dir1.to_vec()),
1709                        None,
1710                        LineString::String(file1.to_vec()),
1711                        None,
1712                    );
1713                    let dir_id = program.default_directory();
1714                    let file_id =
1715                        program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1716
1717                    for (inst, expect_inst) in &[
1718                        (
1719                            LineInstruction::Special(OPCODE_BASE),
1720                            read::LineInstruction::Special(OPCODE_BASE),
1721                        ),
1722                        (
1723                            LineInstruction::Special(255),
1724                            read::LineInstruction::Special(255),
1725                        ),
1726                        (LineInstruction::Copy, read::LineInstruction::Copy),
1727                        (
1728                            LineInstruction::AdvancePc(0x12),
1729                            read::LineInstruction::AdvancePc(0x12),
1730                        ),
1731                        (
1732                            LineInstruction::AdvanceLine(0x12),
1733                            read::LineInstruction::AdvanceLine(0x12),
1734                        ),
1735                        (
1736                            LineInstruction::SetFile(file_id),
1737                            read::LineInstruction::SetFile(file_id.raw(encoding.version)),
1738                        ),
1739                        (
1740                            LineInstruction::SetColumn(0x12),
1741                            read::LineInstruction::SetColumn(0x12),
1742                        ),
1743                        (
1744                            LineInstruction::NegateStatement,
1745                            read::LineInstruction::NegateStatement,
1746                        ),
1747                        (
1748                            LineInstruction::SetBasicBlock,
1749                            read::LineInstruction::SetBasicBlock,
1750                        ),
1751                        (
1752                            LineInstruction::ConstAddPc,
1753                            read::LineInstruction::ConstAddPc,
1754                        ),
1755                        (
1756                            LineInstruction::SetPrologueEnd,
1757                            read::LineInstruction::SetPrologueEnd,
1758                        ),
1759                        (
1760                            LineInstruction::SetEpilogueBegin,
1761                            read::LineInstruction::SetEpilogueBegin,
1762                        ),
1763                        (
1764                            LineInstruction::SetIsa(0x12),
1765                            read::LineInstruction::SetIsa(0x12),
1766                        ),
1767                        (
1768                            LineInstruction::EndSequence,
1769                            read::LineInstruction::EndSequence,
1770                        ),
1771                        (
1772                            LineInstruction::SetAddress(Address::Constant(0x12)),
1773                            read::LineInstruction::SetAddress(0x12),
1774                        ),
1775                        (
1776                            LineInstruction::SetDiscriminator(0x12),
1777                            read::LineInstruction::SetDiscriminator(0x12),
1778                        ),
1779                    ][..]
1780                    {
1781                        let mut program = program.clone();
1782                        program.instructions.push(*inst);
1783
1784                        let mut unit = Unit::new(encoding, program);
1785                        let root = unit.get_mut(unit.root());
1786                        root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1787
1788                        let mut dwarf = Dwarf::new();
1789                        dwarf.units.add(unit);
1790                        let mut sections = Sections::new(EndianVec::new(LittleEndian));
1791                        dwarf.write(&mut sections).unwrap();
1792
1793                        let read_dwarf = sections.read(LittleEndian);
1794                        let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
1795                        let read_unit = read_dwarf.unit(read_unit_header).unwrap();
1796                        let read_unit = read_unit.unit_ref(&read_dwarf);
1797                        let read_header = read_unit.line_program.as_ref().unwrap().header();
1798                        let mut read_insts = read_header.instructions();
1799                        assert_eq!(
1800                            *expect_inst,
1801                            read_insts.next_instruction(read_header).unwrap().unwrap()
1802                        );
1803                        assert_eq!(None, read_insts.next_instruction(read_header).unwrap());
1804                    }
1805                }
1806            }
1807        }
1808    }
1809
1810    // Test that the address/line advance is correct. We don't test for optimality.
1811    #[test]
1812    fn test_advance() {
1813        let encoding = Encoding {
1814            format: Format::Dwarf32,
1815            version: 4,
1816            address_size: 8,
1817        };
1818
1819        let dir1 = &b"dir1"[..];
1820        let file1 = &b"file1"[..];
1821
1822        let addresses = 0..50;
1823        let lines = -10..25i64;
1824
1825        for minimum_instruction_length in [1, 4] {
1826            for maximum_operations_per_instruction in [1, 3] {
1827                for line_base in [-5, 0] {
1828                    for line_range in [10, 20] {
1829                        let line_encoding = LineEncoding {
1830                            minimum_instruction_length,
1831                            maximum_operations_per_instruction,
1832                            line_base,
1833                            line_range,
1834                            default_is_stmt: true,
1835                        };
1836                        let mut program = LineProgram::new(
1837                            encoding,
1838                            line_encoding,
1839                            LineString::String(dir1.to_vec()),
1840                            None,
1841                            LineString::String(file1.to_vec()),
1842                            None,
1843                        );
1844                        for address_advance in addresses.clone() {
1845                            program.begin_sequence(Some(Address::Constant(0x1000)));
1846                            program.row().line = 0x10000;
1847                            program.generate_row();
1848                            for line_advance in lines.clone() {
1849                                {
1850                                    let row = program.row();
1851                                    row.address_offset +=
1852                                        address_advance * u64::from(minimum_instruction_length);
1853                                    row.line = row.line.wrapping_add(line_advance as u64);
1854                                }
1855                                program.generate_row();
1856                            }
1857                            let address_offset = program.row().address_offset
1858                                + u64::from(minimum_instruction_length);
1859                            program.end_sequence(address_offset);
1860                        }
1861
1862                        let mut unit = Unit::new(encoding, program);
1863                        let root = unit.get_mut(unit.root());
1864                        root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1865
1866                        let mut dwarf = Dwarf::new();
1867                        dwarf.units.add(unit);
1868                        let mut sections = Sections::new(EndianVec::new(LittleEndian));
1869                        dwarf.write(&mut sections).unwrap();
1870
1871                        let read_dwarf = sections.read(LittleEndian);
1872                        let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
1873                        let read_unit = read_dwarf.unit(read_unit_header).unwrap();
1874                        let read_unit = read_unit.unit_ref(&read_dwarf);
1875                        let read_program = read_unit.line_program.clone().unwrap();
1876
1877                        let mut rows = read_program.rows();
1878                        for address_advance in addresses.clone() {
1879                            let mut address;
1880                            let mut line;
1881                            {
1882                                let row = rows.next_row().unwrap().unwrap().1;
1883                                address = row.address();
1884                                line = row.line().unwrap().get();
1885                            }
1886                            assert_eq!(address, 0x1000);
1887                            assert_eq!(line, 0x10000);
1888                            for line_advance in lines.clone() {
1889                                let row = rows.next_row().unwrap().unwrap().1;
1890                                assert_eq!(
1891                                    row.address() - address,
1892                                    address_advance * u64::from(minimum_instruction_length)
1893                                );
1894                                assert_eq!(
1895                                    (row.line().unwrap().get() as i64) - (line as i64),
1896                                    line_advance
1897                                );
1898                                address = row.address();
1899                                line = row.line().unwrap().get();
1900                            }
1901                            let row = rows.next_row().unwrap().unwrap().1;
1902                            assert!(row.end_sequence());
1903                        }
1904                    }
1905                }
1906            }
1907        }
1908    }
1909
1910    #[test]
1911    fn test_line_string() {
1912        let version = 5;
1913
1914        let file1 = "file1";
1915
1916        for &address_size in &[4, 8] {
1917            for &format in &[Format::Dwarf32, Format::Dwarf64] {
1918                let encoding = Encoding {
1919                    format,
1920                    version,
1921                    address_size,
1922                };
1923
1924                let files: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [
1925                    &mut |_dwarf| LineString::String(file1.as_bytes().to_vec()),
1926                    &mut |dwarf| LineString::StringRef(dwarf.strings.add(file1)),
1927                    &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(file1)),
1928                ];
1929
1930                for file in files {
1931                    let mut dwarf = Dwarf::new();
1932                    let file = file(&mut dwarf);
1933
1934                    let mut program = LineProgram::new(
1935                        encoding,
1936                        LineEncoding::default(),
1937                        LineString::String(b"dir".to_vec()),
1938                        None,
1939                        file.clone(),
1940                        None,
1941                    );
1942                    program.begin_sequence(Some(Address::Constant(0x1000)));
1943                    program.row().line = 0x10000;
1944                    program.generate_row();
1945
1946                    let mut unit = Unit::new(encoding, program);
1947                    let root = unit.get_mut(unit.root());
1948                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1949                    dwarf.units.add(unit);
1950
1951                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
1952                    dwarf.write(&mut sections).unwrap();
1953
1954                    let read_dwarf = sections.read(LittleEndian);
1955                    let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
1956                    let read_unit = read_dwarf.unit(read_unit_header).unwrap();
1957                    let read_unit = read_unit.unit_ref(&read_dwarf);
1958                    let read_program = read_unit.line_program.clone().unwrap();
1959                    let read_header = read_program.header();
1960                    let read_file = read_header.file(0).unwrap();
1961                    let read_path = read_unit.attr_string(read_file.path_name()).unwrap();
1962                    assert_eq!(read_path.slice(), file1.as_bytes());
1963                }
1964            }
1965        }
1966    }
1967
1968    #[test]
1969    fn test_missing_comp_dir() {
1970        for &version in &[2, 3, 4, 5] {
1971            for &address_size in &[4, 8] {
1972                for &format in &[Format::Dwarf32, Format::Dwarf64] {
1973                    let encoding = Encoding {
1974                        format,
1975                        version,
1976                        address_size,
1977                    };
1978                    let mut program = LineProgram::new(
1979                        encoding,
1980                        LineEncoding::default(),
1981                        LineString::String(Vec::new()),
1982                        None,
1983                        LineString::String(Vec::new()),
1984                        None,
1985                    );
1986                    // Ensure the program is not empty.
1987                    let dir_id = program.default_directory();
1988                    let file_id =
1989                        program.add_file(LineString::String(b"file1".to_vec()), dir_id, None);
1990                    program.begin_sequence(Some(Address::Constant(0x1000)));
1991                    program.row().file = file_id;
1992                    program.row().line = 0x10000;
1993                    program.generate_row();
1994
1995                    let mut unit = Unit::new(encoding, program);
1996                    let root = unit.get_mut(unit.root());
1997                    // Testing missing DW_AT_comp_dir/DW_AT_name.
1998                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1999
2000                    let mut dwarf = Dwarf::new();
2001                    dwarf.units.add(unit);
2002                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
2003                    dwarf.write(&mut sections).unwrap();
2004                    let read_dwarf = sections.read(LittleEndian);
2005                    let _convert_dwarf =
2006                        Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2007                            .unwrap();
2008                }
2009            }
2010        }
2011    }
2012
2013    #[test]
2014    fn test_separate_working_dir() {
2015        let working_dir = LineString::String(b"working".to_vec());
2016        let source_dir = LineString::String(b"source".to_vec());
2017        let source_file = LineString::String(b"file".to_vec());
2018
2019        for &version in &[2, 3, 4, 5] {
2020            for &address_size in &[4, 8] {
2021                for &format in &[Format::Dwarf32, Format::Dwarf64] {
2022                    let encoding = Encoding {
2023                        format,
2024                        version,
2025                        address_size,
2026                    };
2027                    let mut program = LineProgram::new(
2028                        encoding,
2029                        LineEncoding::default(),
2030                        working_dir.clone(),
2031                        Some(source_dir.clone()),
2032                        source_file.clone(),
2033                        None,
2034                    );
2035
2036                    assert_eq!(
2037                        &working_dir,
2038                        program.get_directory(program.default_directory())
2039                    );
2040
2041                    // Ensure the program is not empty.
2042                    let dir_id = program.add_directory(source_dir.clone());
2043                    let file_id = program.add_file(source_file.clone(), dir_id, None);
2044                    program.begin_sequence(Some(Address::Constant(0x1000)));
2045                    program.row().file = file_id;
2046                    program.row().line = 0x10000;
2047                    program.generate_row();
2048
2049                    // Test LineProgram::from().
2050                    let mut unit = Unit::new(encoding, program);
2051                    let root = unit.get_mut(unit.root());
2052                    root.set(
2053                        constants::DW_AT_comp_dir,
2054                        AttributeValue::String(b"working".to_vec()),
2055                    );
2056                    root.set(
2057                        constants::DW_AT_name,
2058                        AttributeValue::String(b"source/file".to_vec()),
2059                    );
2060                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2061
2062                    let mut dwarf = Dwarf::new();
2063                    dwarf.units.add(unit);
2064
2065                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
2066                    dwarf.write(&mut sections).unwrap();
2067                    let read_dwarf = sections.read(LittleEndian);
2068
2069                    let convert_dwarf =
2070                        Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2071                            .unwrap();
2072                    let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
2073                    let convert_program = &convert_unit.line_program;
2074
2075                    assert_eq!(
2076                        &working_dir,
2077                        convert_program.get_directory(convert_program.default_directory())
2078                    );
2079                    let (_file_id, file, dir_id) = convert_program.files().next().unwrap();
2080                    assert_eq!(&source_file, file);
2081                    assert_eq!(&source_dir, convert_program.get_directory(dir_id));
2082                }
2083            }
2084        }
2085    }
2086
2087    #[test]
2088    fn test_file_source() {
2089        let version = 5;
2090
2091        let source1 = "source1";
2092
2093        for &address_size in &[4, 8] {
2094            for &format in &[Format::Dwarf32, Format::Dwarf64] {
2095                let encoding = Encoding {
2096                    format,
2097                    version,
2098                    address_size,
2099                };
2100
2101                let sources: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [
2102                    &mut |_dwarf| LineString::String(source1.as_bytes().to_vec()),
2103                    &mut |dwarf| LineString::StringRef(dwarf.strings.add(source1)),
2104                    &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(source1)),
2105                ];
2106
2107                for source in sources {
2108                    let mut dwarf = Dwarf::new();
2109                    let source = Some(source(&mut dwarf));
2110
2111                    let mut program = LineProgram::new(
2112                        encoding,
2113                        LineEncoding::default(),
2114                        LineString::String(b"dir".to_vec()),
2115                        None,
2116                        LineString::String(b"file".to_vec()),
2117                        Some(FileInfo {
2118                            timestamp: 0,
2119                            size: 0,
2120                            md5: [0; 16],
2121                            source,
2122                        }),
2123                    );
2124                    program.file_has_source = true;
2125
2126                    let file_id = program.files().next().unwrap().0;
2127                    program.begin_sequence(Some(Address::Constant(0x1000)));
2128                    program.row().file = file_id;
2129                    program.row().line = 0x10000;
2130                    program.generate_row();
2131
2132                    let mut unit = Unit::new(encoding, program);
2133                    let root = unit.get_mut(unit.root());
2134                    root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2135                    dwarf.units.add(unit);
2136
2137                    let mut sections = Sections::new(EndianVec::new(LittleEndian));
2138                    dwarf.write(&mut sections).unwrap();
2139
2140                    let read_dwarf = sections.read(LittleEndian);
2141                    let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2142                    let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2143                    let read_unit = read_unit.unit_ref(&read_dwarf);
2144                    let read_program = read_unit.line_program.clone().unwrap();
2145                    let read_header = read_program.header();
2146                    let read_file = read_header.file(0).unwrap();
2147                    let read_source = read_unit.attr_string(read_file.source().unwrap()).unwrap();
2148                    assert_eq!(read_source.slice(), source1.as_bytes());
2149
2150                    let convert_dwarf =
2151                        Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2152                            .unwrap();
2153                    let (_, convert_unit) = convert_dwarf.units.iter().next().unwrap();
2154                    let convert_program = &convert_unit.line_program;
2155                    let convert_file_id = convert_program.files().next().unwrap().0;
2156                    let convert_file_info = convert_program.get_file_info(convert_file_id);
2157                    assert_eq!(
2158                        convert_dwarf.get_line_string(convert_file_info.source.as_ref().unwrap()),
2159                        source1.as_bytes(),
2160                    );
2161                }
2162            }
2163        }
2164    }
2165}