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