gimli/write/
line.rs

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