Skip to main content

gimli/write/
line.rs

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