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