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 = from_row.line().unwrap_or(0);
1106                                 program.row().column = match from_row.column() {
1107                                     read::ColumnType::LeftEdge => 0,
1108                                     read::ColumnType::Column(val) => val,
1109                                 };
1110                                 program.row().discriminator = from_row.discriminator();
1111                                 program.row().is_statement = from_row.is_stmt();
1112                                 program.row().basic_block = from_row.basic_block();
1113                                 program.row().prologue_end = from_row.prologue_end();
1114                                 program.row().epilogue_begin = from_row.epilogue_begin();
1115                                 program.row().isa = from_row.isa();
1116                                 program.generate_row();
1117                             }
1118                             from_row.reset(from_program.header());
1119                         }
1120                     }
1121                 };
1122             }
1123             Ok((program, files))
1124         }
1125     }
1126 
1127     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>1128         fn from<R: Reader<Offset = usize>>(
1129             from_attr: read::AttributeValue<R>,
1130             dwarf: &read::Dwarf<R>,
1131             line_strings: &mut write::LineStringTable,
1132             strings: &mut write::StringTable,
1133         ) -> ConvertResult<LineString> {
1134             Ok(match from_attr {
1135                 read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()),
1136                 read::AttributeValue::DebugStrRef(offset) => {
1137                     let r = dwarf.debug_str.get_str(offset)?;
1138                     let id = strings.add(r.to_slice()?);
1139                     LineString::StringRef(id)
1140                 }
1141                 read::AttributeValue::DebugLineStrRef(offset) => {
1142                     let r = dwarf.debug_line_str.get_str(offset)?;
1143                     let id = line_strings.add(r.to_slice()?);
1144                     LineString::LineStringRef(id)
1145                 }
1146                 _ => return Err(ConvertError::UnsupportedLineStringForm),
1147             })
1148         }
1149     }
1150 }
1151 
1152 #[cfg(test)]
1153 #[cfg(feature = "read")]
1154 mod tests {
1155     use super::*;
1156     use crate::read;
1157     use crate::write::{DebugLineStr, DebugStr, EndianVec, StringTable};
1158     use crate::LittleEndian;
1159 
1160     #[test]
test_line_program_table()1161     fn test_line_program_table() {
1162         let dir1 = LineString::String(b"dir1".to_vec());
1163         let file1 = LineString::String(b"file1".to_vec());
1164         let dir2 = LineString::String(b"dir2".to_vec());
1165         let file2 = LineString::String(b"file2".to_vec());
1166 
1167         let mut programs = Vec::new();
1168         for &version in &[2, 3, 4, 5] {
1169             for &address_size in &[4, 8] {
1170                 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1171                     let encoding = Encoding {
1172                         format,
1173                         version,
1174                         address_size,
1175                     };
1176                     let mut program = LineProgram::new(
1177                         encoding,
1178                         LineEncoding::default(),
1179                         dir1.clone(),
1180                         file1.clone(),
1181                         None,
1182                     );
1183 
1184                     {
1185                         assert_eq!(&dir1, program.get_directory(program.default_directory()));
1186                         program.file_has_timestamp = true;
1187                         program.file_has_size = true;
1188                         if encoding.version >= 5 {
1189                             program.file_has_md5 = true;
1190                         }
1191 
1192                         let dir_id = program.add_directory(dir2.clone());
1193                         assert_eq!(&dir2, program.get_directory(dir_id));
1194                         assert_eq!(dir_id, program.add_directory(dir2.clone()));
1195 
1196                         let file_info = FileInfo {
1197                             timestamp: 1,
1198                             size: 2,
1199                             md5: if encoding.version >= 5 {
1200                                 [3; 16]
1201                             } else {
1202                                 [0; 16]
1203                             },
1204                         };
1205                         let file_id = program.add_file(file2.clone(), dir_id, Some(file_info));
1206                         assert_eq!((&file2, dir_id), program.get_file(file_id));
1207                         assert_eq!(file_info, *program.get_file_info(file_id));
1208 
1209                         program.get_file_info_mut(file_id).size = 3;
1210                         assert_ne!(file_info, *program.get_file_info(file_id));
1211                         assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None));
1212                         assert_ne!(file_info, *program.get_file_info(file_id));
1213                         assert_eq!(
1214                             file_id,
1215                             program.add_file(file2.clone(), dir_id, Some(file_info))
1216                         );
1217                         assert_eq!(file_info, *program.get_file_info(file_id));
1218 
1219                         programs.push((program, file_id, encoding));
1220                     }
1221                 }
1222             }
1223         }
1224 
1225         let debug_line_str_offsets = DebugLineStrOffsets::none();
1226         let debug_str_offsets = DebugStrOffsets::none();
1227         let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1228         let mut debug_line_offsets = Vec::new();
1229         for (program, _, encoding) in &programs {
1230             debug_line_offsets.push(
1231                 program
1232                     .write(
1233                         &mut debug_line,
1234                         *encoding,
1235                         &debug_line_str_offsets,
1236                         &debug_str_offsets,
1237                     )
1238                     .unwrap(),
1239             );
1240         }
1241 
1242         let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
1243 
1244         let convert_address = &|address| Some(Address::Constant(address));
1245         for ((program, file_id, encoding), offset) in programs.iter().zip(debug_line_offsets.iter())
1246         {
1247             let read_program = read_debug_line
1248                 .program(
1249                     *offset,
1250                     encoding.address_size,
1251                     Some(read::EndianSlice::new(b"dir1", LittleEndian)),
1252                     Some(read::EndianSlice::new(b"file1", LittleEndian)),
1253                 )
1254                 .unwrap();
1255 
1256             let dwarf = read::Dwarf::default();
1257             let mut convert_line_strings = LineStringTable::default();
1258             let mut convert_strings = StringTable::default();
1259             let (convert_program, convert_files) = LineProgram::from(
1260                 read_program,
1261                 &dwarf,
1262                 &mut convert_line_strings,
1263                 &mut convert_strings,
1264                 convert_address,
1265             )
1266             .unwrap();
1267             assert_eq!(convert_program.version(), program.version());
1268             assert_eq!(convert_program.address_size(), program.address_size());
1269             assert_eq!(convert_program.format(), program.format());
1270 
1271             let convert_file_id = convert_files[file_id.raw() as usize];
1272             let (file, dir) = program.get_file(*file_id);
1273             let (convert_file, convert_dir) = convert_program.get_file(convert_file_id);
1274             assert_eq!(file, convert_file);
1275             assert_eq!(
1276                 program.get_directory(dir),
1277                 convert_program.get_directory(convert_dir)
1278             );
1279             assert_eq!(
1280                 program.get_file_info(*file_id),
1281                 convert_program.get_file_info(convert_file_id)
1282             );
1283         }
1284     }
1285 
1286     #[test]
test_line_row()1287     fn test_line_row() {
1288         let dir1 = &b"dir1"[..];
1289         let file1 = &b"file1"[..];
1290         let file2 = &b"file2"[..];
1291         let convert_address = &|address| Some(Address::Constant(address));
1292 
1293         let debug_line_str_offsets = DebugLineStrOffsets::none();
1294         let debug_str_offsets = DebugStrOffsets::none();
1295 
1296         for &version in &[2, 3, 4, 5] {
1297             for &address_size in &[4, 8] {
1298                 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1299                     let encoding = Encoding {
1300                         format,
1301                         version,
1302                         address_size,
1303                     };
1304                     let line_base = -5;
1305                     let line_range = 14;
1306                     let neg_line_base = (-line_base) as u8;
1307                     let mut program = LineProgram::new(
1308                         encoding,
1309                         LineEncoding {
1310                             line_base,
1311                             line_range,
1312                             ..Default::default()
1313                         },
1314                         LineString::String(dir1.to_vec()),
1315                         LineString::String(file1.to_vec()),
1316                         None,
1317                     );
1318                     let dir_id = program.default_directory();
1319                     program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1320                     let file_id =
1321                         program.add_file(LineString::String(file2.to_vec()), dir_id, None);
1322 
1323                     // Test sequences.
1324                     {
1325                         let mut program = program.clone();
1326                         let address = Address::Constant(0x12);
1327                         program.begin_sequence(Some(address));
1328                         assert_eq!(
1329                             program.instructions,
1330                             vec![LineInstruction::SetAddress(address)]
1331                         );
1332                     }
1333 
1334                     {
1335                         let mut program = program.clone();
1336                         program.begin_sequence(None);
1337                         assert_eq!(program.instructions, Vec::new());
1338                     }
1339 
1340                     {
1341                         let mut program = program.clone();
1342                         program.begin_sequence(None);
1343                         program.end_sequence(0x1234);
1344                         assert_eq!(
1345                             program.instructions,
1346                             vec![
1347                                 LineInstruction::AdvancePc(0x1234),
1348                                 LineInstruction::EndSequence
1349                             ]
1350                         );
1351                     }
1352 
1353                     // Create a base program.
1354                     program.begin_sequence(None);
1355                     program.row.line = 0x1000;
1356                     program.generate_row();
1357                     let base_row = program.row;
1358                     let base_instructions = program.instructions.clone();
1359 
1360                     // Create test cases.
1361                     let mut tests = Vec::new();
1362 
1363                     let row = base_row;
1364                     tests.push((row, vec![LineInstruction::Copy]));
1365 
1366                     let mut row = base_row;
1367                     row.line -= u64::from(neg_line_base);
1368                     tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)]));
1369 
1370                     let mut row = base_row;
1371                     row.line += u64::from(line_range) - 1;
1372                     row.line -= u64::from(neg_line_base);
1373                     tests.push((
1374                         row,
1375                         vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)],
1376                     ));
1377 
1378                     let mut row = base_row;
1379                     row.line += u64::from(line_range);
1380                     row.line -= u64::from(neg_line_base);
1381                     tests.push((
1382                         row,
1383                         vec![
1384                             LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)),
1385                             LineInstruction::Copy,
1386                         ],
1387                     ));
1388 
1389                     let mut row = base_row;
1390                     row.address_offset = 1;
1391                     row.line -= u64::from(neg_line_base);
1392                     tests.push((
1393                         row,
1394                         vec![LineInstruction::Special(OPCODE_BASE + line_range)],
1395                     ));
1396 
1397                     let op_range = (255 - OPCODE_BASE) / line_range;
1398                     let mut row = base_row;
1399                     row.address_offset = u64::from(op_range);
1400                     row.line -= u64::from(neg_line_base);
1401                     tests.push((
1402                         row,
1403                         vec![LineInstruction::Special(
1404                             OPCODE_BASE + op_range * line_range,
1405                         )],
1406                     ));
1407 
1408                     let mut row = base_row;
1409                     row.address_offset = u64::from(op_range);
1410                     row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
1411                     row.line -= u64::from(neg_line_base);
1412                     tests.push((row, vec![LineInstruction::Special(255)]));
1413 
1414                     let mut row = base_row;
1415                     row.address_offset = u64::from(op_range);
1416                     row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
1417                     row.line -= u64::from(neg_line_base);
1418                     tests.push((
1419                         row,
1420                         vec![LineInstruction::ConstAddPc, LineInstruction::Copy],
1421                     ));
1422 
1423                     let mut row = base_row;
1424                     row.address_offset = u64::from(op_range);
1425                     row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
1426                     row.line -= u64::from(neg_line_base);
1427                     tests.push((
1428                         row,
1429                         vec![
1430                             LineInstruction::ConstAddPc,
1431                             LineInstruction::Special(OPCODE_BASE + 6),
1432                         ],
1433                     ));
1434 
1435                     let mut row = base_row;
1436                     row.address_offset = u64::from(op_range) * 2;
1437                     row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
1438                     row.line -= u64::from(neg_line_base);
1439                     tests.push((
1440                         row,
1441                         vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)],
1442                     ));
1443 
1444                     let mut row = base_row;
1445                     row.address_offset = u64::from(op_range) * 2;
1446                     row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
1447                     row.line -= u64::from(neg_line_base);
1448                     tests.push((
1449                         row,
1450                         vec![
1451                             LineInstruction::AdvancePc(row.address_offset),
1452                             LineInstruction::Copy,
1453                         ],
1454                     ));
1455 
1456                     let mut row = base_row;
1457                     row.address_offset = u64::from(op_range) * 2;
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::AdvancePc(row.address_offset),
1464                             LineInstruction::Special(OPCODE_BASE + 6),
1465                         ],
1466                     ));
1467 
1468                     let mut row = base_row;
1469                     row.address_offset = 0x1234;
1470                     tests.push((
1471                         row,
1472                         vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy],
1473                     ));
1474 
1475                     let mut row = base_row;
1476                     row.line += 0x1234;
1477                     tests.push((
1478                         row,
1479                         vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy],
1480                     ));
1481 
1482                     let mut row = base_row;
1483                     row.file = file_id;
1484                     tests.push((
1485                         row,
1486                         vec![LineInstruction::SetFile(file_id), LineInstruction::Copy],
1487                     ));
1488 
1489                     let mut row = base_row;
1490                     row.column = 0x1234;
1491                     tests.push((
1492                         row,
1493                         vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy],
1494                     ));
1495 
1496                     let mut row = base_row;
1497                     row.discriminator = 0x1234;
1498                     tests.push((
1499                         row,
1500                         vec![
1501                             LineInstruction::SetDiscriminator(0x1234),
1502                             LineInstruction::Copy,
1503                         ],
1504                     ));
1505 
1506                     let mut row = base_row;
1507                     row.is_statement = !row.is_statement;
1508                     tests.push((
1509                         row,
1510                         vec![LineInstruction::NegateStatement, LineInstruction::Copy],
1511                     ));
1512 
1513                     let mut row = base_row;
1514                     row.basic_block = true;
1515                     tests.push((
1516                         row,
1517                         vec![LineInstruction::SetBasicBlock, LineInstruction::Copy],
1518                     ));
1519 
1520                     let mut row = base_row;
1521                     row.prologue_end = true;
1522                     tests.push((
1523                         row,
1524                         vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy],
1525                     ));
1526 
1527                     let mut row = base_row;
1528                     row.epilogue_begin = true;
1529                     tests.push((
1530                         row,
1531                         vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy],
1532                     ));
1533 
1534                     let mut row = base_row;
1535                     row.isa = 0x1234;
1536                     tests.push((
1537                         row,
1538                         vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy],
1539                     ));
1540 
1541                     for test in tests {
1542                         // Test generate_row().
1543                         let mut program = program.clone();
1544                         program.row = test.0;
1545                         program.generate_row();
1546                         assert_eq!(
1547                             &program.instructions[base_instructions.len()..],
1548                             &test.1[..]
1549                         );
1550 
1551                         // Test LineProgram::from().
1552                         let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1553                         let debug_line_offset = program
1554                             .write(
1555                                 &mut debug_line,
1556                                 encoding,
1557                                 &debug_line_str_offsets,
1558                                 &debug_str_offsets,
1559                             )
1560                             .unwrap();
1561 
1562                         let read_debug_line =
1563                             read::DebugLine::new(debug_line.slice(), LittleEndian);
1564                         let read_program = read_debug_line
1565                             .program(
1566                                 debug_line_offset,
1567                                 address_size,
1568                                 Some(read::EndianSlice::new(dir1, LittleEndian)),
1569                                 Some(read::EndianSlice::new(file1, LittleEndian)),
1570                             )
1571                             .unwrap();
1572 
1573                         let dwarf = read::Dwarf::default();
1574                         let mut convert_line_strings = LineStringTable::default();
1575                         let mut convert_strings = StringTable::default();
1576                         let (convert_program, _convert_files) = LineProgram::from(
1577                             read_program,
1578                             &dwarf,
1579                             &mut convert_line_strings,
1580                             &mut convert_strings,
1581                             convert_address,
1582                         )
1583                         .unwrap();
1584                         assert_eq!(
1585                             &convert_program.instructions[base_instructions.len()..],
1586                             &test.1[..]
1587                         );
1588                     }
1589                 }
1590             }
1591         }
1592     }
1593 
1594     #[test]
test_line_instruction()1595     fn test_line_instruction() {
1596         let dir1 = &b"dir1"[..];
1597         let file1 = &b"file1"[..];
1598 
1599         let debug_line_str_offsets = DebugLineStrOffsets::none();
1600         let debug_str_offsets = DebugStrOffsets::none();
1601 
1602         for &version in &[2, 3, 4, 5] {
1603             for &address_size in &[4, 8] {
1604                 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1605                     let encoding = Encoding {
1606                         format,
1607                         version,
1608                         address_size,
1609                     };
1610                     let mut program = LineProgram::new(
1611                         encoding,
1612                         LineEncoding::default(),
1613                         LineString::String(dir1.to_vec()),
1614                         LineString::String(file1.to_vec()),
1615                         None,
1616                     );
1617                     let dir_id = program.default_directory();
1618                     let file_id =
1619                         program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1620 
1621                     for &(ref inst, ref expect_inst) in &[
1622                         (
1623                             LineInstruction::Special(OPCODE_BASE),
1624                             read::LineInstruction::Special(OPCODE_BASE),
1625                         ),
1626                         (
1627                             LineInstruction::Special(255),
1628                             read::LineInstruction::Special(255),
1629                         ),
1630                         (LineInstruction::Copy, read::LineInstruction::Copy),
1631                         (
1632                             LineInstruction::AdvancePc(0x12),
1633                             read::LineInstruction::AdvancePc(0x12),
1634                         ),
1635                         (
1636                             LineInstruction::AdvanceLine(0x12),
1637                             read::LineInstruction::AdvanceLine(0x12),
1638                         ),
1639                         (
1640                             LineInstruction::SetFile(file_id),
1641                             read::LineInstruction::SetFile(file_id.raw()),
1642                         ),
1643                         (
1644                             LineInstruction::SetColumn(0x12),
1645                             read::LineInstruction::SetColumn(0x12),
1646                         ),
1647                         (
1648                             LineInstruction::NegateStatement,
1649                             read::LineInstruction::NegateStatement,
1650                         ),
1651                         (
1652                             LineInstruction::SetBasicBlock,
1653                             read::LineInstruction::SetBasicBlock,
1654                         ),
1655                         (
1656                             LineInstruction::ConstAddPc,
1657                             read::LineInstruction::ConstAddPc,
1658                         ),
1659                         (
1660                             LineInstruction::SetPrologueEnd,
1661                             read::LineInstruction::SetPrologueEnd,
1662                         ),
1663                         (
1664                             LineInstruction::SetEpilogueBegin,
1665                             read::LineInstruction::SetEpilogueBegin,
1666                         ),
1667                         (
1668                             LineInstruction::SetIsa(0x12),
1669                             read::LineInstruction::SetIsa(0x12),
1670                         ),
1671                         (
1672                             LineInstruction::EndSequence,
1673                             read::LineInstruction::EndSequence,
1674                         ),
1675                         (
1676                             LineInstruction::SetAddress(Address::Constant(0x12)),
1677                             read::LineInstruction::SetAddress(0x12),
1678                         ),
1679                         (
1680                             LineInstruction::SetDiscriminator(0x12),
1681                             read::LineInstruction::SetDiscriminator(0x12),
1682                         ),
1683                     ][..]
1684                     {
1685                         let mut program = program.clone();
1686                         program.instructions.push(*inst);
1687 
1688                         let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1689                         let debug_line_offset = program
1690                             .write(
1691                                 &mut debug_line,
1692                                 encoding,
1693                                 &debug_line_str_offsets,
1694                                 &debug_str_offsets,
1695                             )
1696                             .unwrap();
1697 
1698                         let read_debug_line =
1699                             read::DebugLine::new(debug_line.slice(), LittleEndian);
1700                         let read_program = read_debug_line
1701                             .program(
1702                                 debug_line_offset,
1703                                 address_size,
1704                                 Some(read::EndianSlice::new(dir1, LittleEndian)),
1705                                 Some(read::EndianSlice::new(file1, LittleEndian)),
1706                             )
1707                             .unwrap();
1708                         let read_header = read_program.header();
1709                         let mut read_insts = read_header.instructions();
1710                         assert_eq!(
1711                             *expect_inst,
1712                             read_insts.next_instruction(read_header).unwrap().unwrap()
1713                         );
1714                         assert_eq!(None, read_insts.next_instruction(read_header).unwrap());
1715                     }
1716                 }
1717             }
1718         }
1719     }
1720 
1721     // Test that the address/line advance is correct. We don't test for optimality.
1722     #[test]
1723     #[allow(clippy::useless_vec)]
test_advance()1724     fn test_advance() {
1725         let encoding = Encoding {
1726             format: Format::Dwarf32,
1727             version: 4,
1728             address_size: 8,
1729         };
1730 
1731         let dir1 = &b"dir1"[..];
1732         let file1 = &b"file1"[..];
1733 
1734         let addresses = 0..50;
1735         let lines = -10..25i64;
1736 
1737         let debug_line_str_offsets = DebugLineStrOffsets::none();
1738         let debug_str_offsets = DebugStrOffsets::none();
1739 
1740         for minimum_instruction_length in vec![1, 4] {
1741             for maximum_operations_per_instruction in vec![1, 3] {
1742                 for line_base in vec![-5, 0] {
1743                     for line_range in vec![10, 20] {
1744                         let line_encoding = LineEncoding {
1745                             minimum_instruction_length,
1746                             maximum_operations_per_instruction,
1747                             line_base,
1748                             line_range,
1749                             default_is_stmt: true,
1750                         };
1751                         let mut program = LineProgram::new(
1752                             encoding,
1753                             line_encoding,
1754                             LineString::String(dir1.to_vec()),
1755                             LineString::String(file1.to_vec()),
1756                             None,
1757                         );
1758                         for address_advance in addresses.clone() {
1759                             program.begin_sequence(Some(Address::Constant(0x1000)));
1760                             program.row().line = 0x10000;
1761                             program.generate_row();
1762                             for line_advance in lines.clone() {
1763                                 {
1764                                     let row = program.row();
1765                                     row.address_offset +=
1766                                         address_advance * u64::from(minimum_instruction_length);
1767                                     row.line = row.line.wrapping_add(line_advance as u64);
1768                                 }
1769                                 program.generate_row();
1770                             }
1771                             let address_offset = program.row().address_offset
1772                                 + u64::from(minimum_instruction_length);
1773                             program.end_sequence(address_offset);
1774                         }
1775 
1776                         let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1777                         let debug_line_offset = program
1778                             .write(
1779                                 &mut debug_line,
1780                                 encoding,
1781                                 &debug_line_str_offsets,
1782                                 &debug_str_offsets,
1783                             )
1784                             .unwrap();
1785 
1786                         let read_debug_line =
1787                             read::DebugLine::new(debug_line.slice(), LittleEndian);
1788                         let read_program = read_debug_line
1789                             .program(
1790                                 debug_line_offset,
1791                                 8,
1792                                 Some(read::EndianSlice::new(dir1, LittleEndian)),
1793                                 Some(read::EndianSlice::new(file1, LittleEndian)),
1794                             )
1795                             .unwrap();
1796 
1797                         let mut rows = read_program.rows();
1798                         for address_advance in addresses.clone() {
1799                             let mut address;
1800                             let mut line;
1801                             {
1802                                 let row = rows.next_row().unwrap().unwrap().1;
1803                                 address = row.address();
1804                                 line = row.line().unwrap();
1805                             }
1806                             assert_eq!(address, 0x1000);
1807                             assert_eq!(line, 0x10000);
1808                             for line_advance in lines.clone() {
1809                                 let row = rows.next_row().unwrap().unwrap().1;
1810                                 assert_eq!(
1811                                     row.address() - address,
1812                                     address_advance * u64::from(minimum_instruction_length)
1813                                 );
1814                                 assert_eq!(
1815                                     (row.line().unwrap() as i64) - (line as i64),
1816                                     line_advance
1817                                 );
1818                                 address = row.address();
1819                                 line = row.line().unwrap();
1820                             }
1821                             let row = rows.next_row().unwrap().unwrap().1;
1822                             assert!(row.end_sequence());
1823                         }
1824                     }
1825                 }
1826             }
1827         }
1828     }
1829 
1830     #[test]
test_line_string()1831     fn test_line_string() {
1832         let version = 5;
1833 
1834         let file = b"file1";
1835 
1836         let mut strings = StringTable::default();
1837         let string_id = strings.add("file2");
1838         let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
1839         let debug_str_offsets = strings.write(&mut debug_str).unwrap();
1840 
1841         let mut line_strings = LineStringTable::default();
1842         let line_string_id = line_strings.add("file3");
1843         let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian));
1844         let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap();
1845 
1846         for &address_size in &[4, 8] {
1847             for &format in &[Format::Dwarf32, Format::Dwarf64] {
1848                 let encoding = Encoding {
1849                     format,
1850                     version,
1851                     address_size,
1852                 };
1853 
1854                 for (file, expect_file) in vec![
1855                     (
1856                         LineString::String(file.to_vec()),
1857                         read::AttributeValue::String(read::EndianSlice::new(file, LittleEndian)),
1858                     ),
1859                     (
1860                         LineString::StringRef(string_id),
1861                         read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)),
1862                     ),
1863                     (
1864                         LineString::LineStringRef(line_string_id),
1865                         read::AttributeValue::DebugLineStrRef(
1866                             debug_line_str_offsets.get(line_string_id),
1867                         ),
1868                     ),
1869                 ] {
1870                     let program = LineProgram::new(
1871                         encoding,
1872                         LineEncoding::default(),
1873                         LineString::String(b"dir".to_vec()),
1874                         file,
1875                         None,
1876                     );
1877 
1878                     let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1879                     let debug_line_offset = program
1880                         .write(
1881                             &mut debug_line,
1882                             encoding,
1883                             &debug_line_str_offsets,
1884                             &debug_str_offsets,
1885                         )
1886                         .unwrap();
1887 
1888                     let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
1889                     let read_program = read_debug_line
1890                         .program(debug_line_offset, address_size, None, None)
1891                         .unwrap();
1892                     let read_header = read_program.header();
1893                     assert_eq!(read_header.file(0).unwrap().path_name(), expect_file);
1894                 }
1895             }
1896         }
1897     }
1898 
1899     #[test]
test_missing_comp_dir()1900     fn test_missing_comp_dir() {
1901         let debug_line_str_offsets = DebugLineStrOffsets::none();
1902         let debug_str_offsets = DebugStrOffsets::none();
1903 
1904         for &version in &[2, 3, 4, 5] {
1905             for &address_size in &[4, 8] {
1906                 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1907                     let encoding = Encoding {
1908                         format,
1909                         version,
1910                         address_size,
1911                     };
1912                     let program = LineProgram::new(
1913                         encoding,
1914                         LineEncoding::default(),
1915                         LineString::String(Vec::new()),
1916                         LineString::String(Vec::new()),
1917                         None,
1918                     );
1919 
1920                     let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1921                     let debug_line_offset = program
1922                         .write(
1923                             &mut debug_line,
1924                             encoding,
1925                             &debug_line_str_offsets,
1926                             &debug_str_offsets,
1927                         )
1928                         .unwrap();
1929 
1930                     let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
1931                     let read_program = read_debug_line
1932                         .program(
1933                             debug_line_offset,
1934                             address_size,
1935                             // Testing missing comp_dir/comp_name.
1936                             None,
1937                             None,
1938                         )
1939                         .unwrap();
1940 
1941                     let dwarf = read::Dwarf::default();
1942                     let mut convert_line_strings = LineStringTable::default();
1943                     let mut convert_strings = StringTable::default();
1944                     let convert_address = &|address| Some(Address::Constant(address));
1945                     LineProgram::from(
1946                         read_program,
1947                         &dwarf,
1948                         &mut convert_line_strings,
1949                         &mut convert_strings,
1950                         convert_address,
1951                     )
1952                     .unwrap();
1953                 }
1954             }
1955         }
1956     }
1957 }
1958