1use alloc::vec::Vec;
2use core::ops::{Deref, DerefMut};
3
4use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId};
5use crate::constants;
6use crate::leb128::write::Leb128;
7use crate::write::{
8 Address, Error, FnvIndexMap, FnvIndexSet, LineStringId, LineStringTable, Result, Section,
9 StringId, StringTable, Writer,
10};
11
12const OPCODE_BASE: u8 = 13;
17
18#[derive(Debug, Clone)]
20pub struct LineProgram {
21 none: bool,
23 encoding: Encoding,
24 line_encoding: LineEncoding,
25
26 directories: FnvIndexSet<LineString>,
33
34 files: FnvIndexMap<(LineString, DirectoryId), FileInfo>,
43
44 pub file_has_timestamp: bool,
50
51 pub file_has_size: bool,
57
58 pub file_has_md5: bool,
63
64 pub file_has_source: bool,
69
70 prev_row: LineRow,
71 row: LineRow,
72 instructions: Vec<LineInstruction>,
74 in_sequence: bool,
75}
76
77impl LineProgram {
78 pub fn new(
102 encoding: Encoding,
103 line_encoding: LineEncoding,
104 working_dir: LineString,
105 source_dir: Option<LineString>,
106 source_file: LineString,
107 source_file_info: Option<FileInfo>,
108 ) -> LineProgram {
109 assert!(line_encoding.line_base <= 0);
112 assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0);
113 let mut program = LineProgram {
114 none: false,
115 encoding,
116 line_encoding,
117 directories: FnvIndexSet::default(),
118 files: FnvIndexMap::default(),
119 prev_row: LineRow::initial_state(encoding, line_encoding),
120 row: LineRow::initial_state(encoding, line_encoding),
121 instructions: Vec::new(),
122 in_sequence: false,
123 file_has_timestamp: false,
124 file_has_size: false,
125 file_has_md5: false,
126 file_has_source: false,
127 };
128 let working_dir_id = program.add_directory(working_dir);
132 if encoding.version >= 5 {
134 let source_dir_id = match source_dir {
135 Some(source_dir) => program.add_directory(source_dir),
136 None => working_dir_id,
137 };
138 program.add_file(source_file, source_dir_id, source_file_info);
139 }
140 program
141 }
142
143 pub fn none() -> Self {
150 let encoding = Encoding {
151 format: Format::Dwarf32,
152 version: 2,
153 address_size: 0,
154 };
155 let line_encoding = LineEncoding::default();
156 LineProgram {
157 none: true,
158 encoding,
159 line_encoding,
160 directories: FnvIndexSet::default(),
161 files: FnvIndexMap::default(),
162 prev_row: LineRow::initial_state(encoding, line_encoding),
163 row: LineRow::initial_state(encoding, line_encoding),
164 instructions: Vec::new(),
165 in_sequence: false,
166 file_has_timestamp: false,
167 file_has_size: false,
168 file_has_md5: false,
169 file_has_source: false,
170 }
171 }
172
173 #[inline]
175 pub fn is_none(&self) -> bool {
176 self.none
177 }
178
179 #[inline]
181 pub fn encoding(&self) -> Encoding {
182 self.encoding
183 }
184
185 #[inline]
187 pub fn version(&self) -> u16 {
188 self.encoding.version
189 }
190
191 #[inline]
193 pub fn address_size(&self) -> u8 {
194 self.encoding.address_size
195 }
196
197 #[inline]
199 pub fn format(&self) -> Format {
200 self.encoding.format
201 }
202
203 #[inline]
205 pub fn default_directory(&self) -> DirectoryId {
206 DirectoryId(0)
207 }
208
209 pub fn add_directory(&mut self, directory: LineString) -> DirectoryId {
220 if let LineString::String(ref val) = directory {
221 if self.encoding.version <= 4 && !self.directories.is_empty() {
224 assert!(!val.is_empty());
225 }
226 assert!(!val.contains(&0));
227 }
228 let (index, _) = self.directories.insert_full(directory);
229 DirectoryId(index)
230 }
231
232 pub fn get_directory(&self, id: DirectoryId) -> &LineString {
238 self.directories.get_index(id.0).unwrap()
239 }
240
241 pub fn add_file(
259 &mut self,
260 file: LineString,
261 directory: DirectoryId,
262 info: Option<FileInfo>,
263 ) -> FileId {
264 if let LineString::String(ref val) = file {
265 if self.encoding.version <= 4 {
266 assert!(!val.is_empty());
267 }
268 assert!(!val.contains(&0));
269 }
270
271 let key = (file, directory);
272 let index = if let Some(info) = info {
273 let (index, _) = self.files.insert_full(key, info);
274 index
275 } else {
276 let entry = self.files.entry(key);
277 let index = entry.index();
278 entry.or_default();
279 index
280 };
281 FileId::new(index)
282 }
283
284 pub fn files(&self) -> impl Iterator<Item = (FileId, &LineString, DirectoryId)> {
286 self.files
287 .iter()
288 .enumerate()
289 .map(move |(index, entry)| (FileId::new(index), &(entry.0).0, (entry.0).1))
290 }
291
292 pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) {
298 self.files
299 .get_index(id.index())
300 .map(|entry| (&(entry.0).0, (entry.0).1))
301 .unwrap()
302 }
303
304 pub fn get_file_info(&self, id: FileId) -> &FileInfo {
310 self.files
311 .get_index(id.index())
312 .map(|entry| entry.1)
313 .unwrap()
314 }
315
316 pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo {
322 self.files
323 .get_index_mut(id.index())
324 .map(|entry| entry.1)
325 .unwrap()
326 }
327
328 pub fn begin_sequence(&mut self, address: Option<Address>) {
339 assert!(!self.in_sequence);
340 self.in_sequence = true;
341 if let Some(address) = address {
342 self.instructions.push(LineInstruction::SetAddress(address));
343 }
344 }
345
346 pub fn set_address(&mut self, address: Address) {
353 self.in_sequence = true;
354 self.instructions.push(LineInstruction::SetAddress(address));
355 }
356
357 pub fn end_sequence(&mut self, address_offset: u64) {
363 self.in_sequence = false;
364 self.row.address_offset = address_offset;
365 let op_advance = self.op_advance();
366 if op_advance != 0 {
367 self.instructions
368 .push(LineInstruction::AdvancePc(op_advance));
369 }
370 self.instructions.push(LineInstruction::EndSequence);
371 self.prev_row = LineRow::initial_state(self.encoding, self.line_encoding);
372 self.row = LineRow::initial_state(self.encoding, self.line_encoding);
373 }
374
375 #[inline]
377 pub fn in_sequence(&self) -> bool {
378 self.in_sequence
379 }
380
381 #[inline]
383 pub fn row(&mut self) -> &mut LineRow {
384 &mut self.row
385 }
386
387 pub fn generate_row(&mut self) {
398 self.in_sequence = true;
399 if self.row.discriminator != 0 {
401 self.instructions
402 .push(LineInstruction::SetDiscriminator(self.row.discriminator));
403 self.row.discriminator = 0;
404 }
405 if self.row.basic_block {
406 self.instructions.push(LineInstruction::SetBasicBlock);
407 self.row.basic_block = false;
408 }
409 if self.row.prologue_end {
410 self.instructions.push(LineInstruction::SetPrologueEnd);
411 self.row.prologue_end = false;
412 }
413 if self.row.epilogue_begin {
414 self.instructions.push(LineInstruction::SetEpilogueBegin);
415 self.row.epilogue_begin = false;
416 }
417
418 if self.row.is_statement != self.prev_row.is_statement {
420 self.instructions.push(LineInstruction::NegateStatement);
421 }
422 if self.row.file != self.prev_row.file {
423 self.instructions
424 .push(LineInstruction::SetFile(self.row.file));
425 }
426 if self.row.column != self.prev_row.column {
427 self.instructions
428 .push(LineInstruction::SetColumn(self.row.column));
429 }
430 if self.row.isa != self.prev_row.isa {
431 self.instructions
432 .push(LineInstruction::SetIsa(self.row.isa));
433 }
434
435 let line_base = i64::from(self.line_encoding.line_base) as u64;
437 let line_range = u64::from(self.line_encoding.line_range);
438 let line_advance = self.row.line as i64 - self.prev_row.line as i64;
439 let op_advance = self.op_advance();
440
441 let special_base = u64::from(OPCODE_BASE);
443 debug_assert!(self.line_encoding.line_base <= 0);
445 debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0);
446 let special_default = special_base.wrapping_sub(line_base);
447 let mut special = special_default;
448 let mut use_special = false;
449
450 if line_advance != 0 {
451 let special_line = (line_advance as u64).wrapping_sub(line_base);
452 if special_line < line_range {
453 special = special_base + special_line;
454 use_special = true;
455 } else {
456 self.instructions
457 .push(LineInstruction::AdvanceLine(line_advance));
458 }
459 }
460
461 if op_advance != 0 {
462 let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 {
464 (op_advance, false)
465 } else {
466 let op_range = (255 - special_base) / line_range;
467 (op_advance - op_range, true)
468 };
469
470 let special_op = special_op_advance * line_range;
471 if special + special_op <= 255 {
472 special += special_op;
473 use_special = true;
474 if const_add_pc {
475 self.instructions.push(LineInstruction::ConstAddPc);
476 }
477 } else {
478 self.instructions
479 .push(LineInstruction::AdvancePc(op_advance));
480 }
481 }
482
483 if use_special && special != special_default {
484 debug_assert!(special >= special_base);
485 debug_assert!(special <= 255);
486 self.instructions
487 .push(LineInstruction::Special(special as u8));
488 } else {
489 self.instructions.push(LineInstruction::Copy);
490 }
491
492 self.prev_row = self.row;
493 }
494
495 fn op_advance(&self) -> u64 {
496 debug_assert!(self.row.address_offset >= self.prev_row.address_offset);
497 let mut address_advance = self.row.address_offset - self.prev_row.address_offset;
498 if self.line_encoding.minimum_instruction_length != 1 {
499 debug_assert_eq!(
500 self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length),
501 0
502 );
503 address_advance /= u64::from(self.line_encoding.minimum_instruction_length);
504 }
505 address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction)
506 + self.row.op_index
507 - self.prev_row.op_index
508 }
509
510 #[inline]
514 pub fn is_empty(&self) -> bool {
515 self.instructions.is_empty()
516 }
517
518 pub fn write<W: Writer>(
528 &self,
529 w: &mut DebugLine<W>,
530 encoding: Encoding,
531 line_strings: &mut LineStringTable,
532 strings: &mut StringTable,
533 ) -> Result<DebugLineOffset> {
534 assert!(!self.is_none());
535
536 if encoding.version < 5 && self.version() >= 5
541 || encoding.address_size != self.address_size()
542 {
543 return Err(Error::IncompatibleLineProgramEncoding);
544 }
545
546 let offset = w.offset();
547
548 let length_offset = w.write_initial_length(self.format())?;
549 let length_base = w.len();
550
551 if self.version() < 2 || self.version() > 5 {
552 return Err(Error::UnsupportedVersion(self.version()));
553 }
554 w.write_u16(self.version())?;
555
556 if self.version() >= 5 {
557 w.write_u8(self.address_size())?;
558 w.write_u8(0)?;
560 }
561
562 let header_length_offset = w.len();
563 w.write_udata(0, self.format().word_size())?;
564 let header_length_base = w.len();
565
566 w.write_u8(self.line_encoding.minimum_instruction_length)?;
567 if self.version() >= 4 {
568 w.write_u8(self.line_encoding.maximum_operations_per_instruction)?;
569 } else if self.line_encoding.maximum_operations_per_instruction != 1 {
570 return Err(Error::NeedVersion(4));
571 };
572 w.write_u8(if self.line_encoding.default_is_stmt {
573 1
574 } else {
575 0
576 })?;
577 w.write_u8(self.line_encoding.line_base as u8)?;
578 w.write_u8(self.line_encoding.line_range)?;
579 w.write_u8(OPCODE_BASE)?;
580 w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?;
581
582 if self.version() <= 4 {
583 for dir in self.directories.iter().skip(1) {
585 dir.write(
586 w,
587 constants::DW_FORM_string,
588 self.encoding,
589 line_strings,
590 strings,
591 )?;
592 }
593 w.write_u8(0)?;
594
595 for ((file, dir), info) in self.files.iter() {
596 file.write(
597 w,
598 constants::DW_FORM_string,
599 self.encoding,
600 line_strings,
601 strings,
602 )?;
603 w.write_uleb128(dir.0 as u64)?;
604 w.write_uleb128(info.timestamp)?;
605 w.write_uleb128(info.size)?;
606 }
607 w.write_u8(0)?;
608 } else {
609 w.write_u8(1)?;
611 w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
612 let dir_form = self.directories.get_index(0).unwrap().form();
613 w.write_uleb128(dir_form.0.into())?;
614
615 w.write_uleb128(self.directories.len() as u64)?;
617 for dir in self.directories.iter() {
618 dir.write(w, dir_form, self.encoding, line_strings, strings)?;
619 }
620
621 let count = 2
623 + if self.file_has_timestamp { 1 } else { 0 }
624 + if self.file_has_size { 1 } else { 0 }
625 + if self.file_has_md5 { 1 } else { 0 }
626 + if self.file_has_source { 1 } else { 0 };
627 w.write_u8(count)?;
628 w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
629 let file_form = (self.files.get_index(0).unwrap().0).0.form();
630 w.write_uleb128(file_form.0.into())?;
631 w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?;
632 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
633 if self.file_has_timestamp {
634 w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?;
635 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
636 }
637 if self.file_has_size {
638 w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?;
639 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
640 }
641 if self.file_has_md5 {
642 w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?;
643 w.write_uleb128(constants::DW_FORM_data16.0.into())?;
644 }
645 let file_source_form = self
646 .files
647 .iter()
648 .find_map(|file| file.1.source.as_ref().map(LineString::form))
649 .unwrap_or(constants::DW_FORM_string);
650 if self.file_has_source {
651 w.write_uleb128(u64::from(constants::DW_LNCT_LLVM_source.0))?;
652 w.write_uleb128(file_source_form.0.into())?;
653 }
654
655 w.write_uleb128(self.files.len() as u64)?;
657 let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| {
658 file.write(w, file_form, self.encoding, line_strings, strings)?;
659 w.write_uleb128(dir.0 as u64)?;
660 if self.file_has_timestamp {
661 w.write_uleb128(info.timestamp)?;
662 }
663 if self.file_has_size {
664 w.write_uleb128(info.size)?;
665 }
666 if self.file_has_md5 {
667 w.write(&info.md5)?;
668 }
669 if self.file_has_source {
670 if let Some(source) = info.source.as_ref() {
671 source.write(w, file_source_form, self.encoding, line_strings, strings)?;
672 } else {
673 let source = match file_source_form {
678 constants::DW_FORM_line_strp => {
679 LineString::LineStringRef(line_strings.add([]))
680 }
681 constants::DW_FORM_strp => LineString::StringRef(strings.add([])),
682 _ => LineString::String(Vec::new()),
683 };
684 source.write(w, file_source_form, self.encoding, line_strings, strings)?;
685 }
686 }
687 Ok(())
688 };
689 for ((file, dir), info) in self.files.iter() {
690 write_file(file, *dir, info)?;
691 }
692 }
693
694 let header_length = (w.len() - header_length_base) as u64;
695 w.write_udata_at(
696 header_length_offset,
697 header_length,
698 self.format().word_size(),
699 )?;
700
701 for instruction in &self.instructions {
702 instruction.write(w, self.encoding)?;
703 }
704
705 let length = (w.len() - length_base) as u64;
706 w.write_initial_length_at(length_offset, length, self.format())?;
707
708 Ok(offset)
709 }
710}
711
712#[derive(Debug, Clone, Copy, PartialEq, Eq)]
714pub struct LineRow {
715 pub address_offset: u64,
717 pub op_index: u64,
722
723 pub file: FileId,
725 pub line: u64,
729 pub column: u64,
733 pub discriminator: u64,
736
737 pub is_statement: bool,
739 pub basic_block: bool,
741 pub prologue_end: bool,
744 pub epilogue_begin: bool,
747
748 pub isa: u64,
752}
753
754impl LineRow {
755 fn initial_state(encoding: Encoding, line_encoding: LineEncoding) -> Self {
757 LineRow {
758 address_offset: 0,
759 op_index: 0,
760
761 file: FileId::initial_state(encoding.version),
762 line: 1,
763 column: 0,
764 discriminator: 0,
765
766 is_statement: line_encoding.default_is_stmt,
767 basic_block: false,
768 prologue_end: false,
769 epilogue_begin: false,
770
771 isa: 0,
772 }
773 }
774}
775
776#[derive(Debug, Clone, Copy, PartialEq, Eq)]
778enum LineInstruction {
779 Special(u8),
781
782 Copy,
784 AdvancePc(u64),
785 AdvanceLine(i64),
786 SetFile(FileId),
787 SetColumn(u64),
788 NegateStatement,
789 SetBasicBlock,
790 ConstAddPc,
791 SetPrologueEnd,
793 SetEpilogueBegin,
794 SetIsa(u64),
795
796 EndSequence,
798 SetAddress(Address),
800 SetDiscriminator(u64),
802}
803
804impl LineInstruction {
805 fn write<W: Writer>(self, w: &mut DebugLine<W>, encoding: Encoding) -> Result<()> {
807 use self::LineInstruction::*;
808 match self {
809 Special(val) => w.write_u8(val)?,
810 Copy => w.write_u8(constants::DW_LNS_copy.0)?,
811 AdvancePc(val) => {
812 w.write_u8(constants::DW_LNS_advance_pc.0)?;
813 w.write_uleb128(val)?;
814 }
815 AdvanceLine(val) => {
816 w.write_u8(constants::DW_LNS_advance_line.0)?;
817 w.write_sleb128(val)?;
818 }
819 SetFile(val) => {
820 w.write_u8(constants::DW_LNS_set_file.0)?;
821 w.write_uleb128(val.raw(encoding.version))?;
822 }
823 SetColumn(val) => {
824 w.write_u8(constants::DW_LNS_set_column.0)?;
825 w.write_uleb128(val)?;
826 }
827 NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?,
828 SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?,
829 ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?,
830 SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?,
831 SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?,
832 SetIsa(val) => {
833 w.write_u8(constants::DW_LNS_set_isa.0)?;
834 w.write_uleb128(val)?;
835 }
836 EndSequence => {
837 w.write_u8(0)?;
838 w.write_uleb128(1)?;
839 w.write_u8(constants::DW_LNE_end_sequence.0)?;
840 }
841 SetAddress(address) => {
842 w.write_u8(0)?;
843 w.write_uleb128(1 + u64::from(encoding.address_size))?;
844 w.write_u8(constants::DW_LNE_set_address.0)?;
845 w.write_address(address, encoding.address_size)?;
846 }
847 SetDiscriminator(val) => {
848 let val = Leb128::unsigned(val);
849 w.write_u8(0)?;
850 w.write_uleb128(1 + val.len() as u64)?;
851 w.write_u8(constants::DW_LNE_set_discriminator.0)?;
852 w.write(val.bytes())?;
853 }
854 }
855 Ok(())
856 }
857}
858
859#[derive(Debug, Clone, PartialEq, Eq, Hash)]
861pub enum LineString {
862 String(Vec<u8>),
865
866 StringRef(StringId),
868
869 LineStringRef(LineStringId),
871}
872
873impl LineString {
874 pub fn new<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self
876 where
877 T: Into<Vec<u8>>,
878 {
879 let val = val.into();
880 if encoding.version <= 4 {
881 LineString::String(val)
882 } else {
883 LineString::LineStringRef(line_strings.add(val))
884 }
885 }
886
887 pub fn get<'a>(
889 &'a self,
890 strings: &'a StringTable,
891 line_strings: &'a LineStringTable,
892 ) -> &'a [u8] {
893 match self {
894 LineString::String(val) => val,
895 LineString::StringRef(val) => strings.get(*val),
896 LineString::LineStringRef(val) => line_strings.get(*val),
897 }
898 }
899
900 fn form(&self) -> constants::DwForm {
901 match *self {
902 LineString::String(..) => constants::DW_FORM_string,
903 LineString::StringRef(..) => constants::DW_FORM_strp,
904 LineString::LineStringRef(..) => constants::DW_FORM_line_strp,
905 }
906 }
907
908 fn write<W: Writer>(
909 &self,
910 w: &mut DebugLine<W>,
911 form: constants::DwForm,
912 encoding: Encoding,
913 line_strings: &LineStringTable,
914 strings: &StringTable,
915 ) -> Result<()> {
916 if form != self.form() {
917 return Err(Error::LineStringFormMismatch);
918 }
919
920 match *self {
921 LineString::String(ref val) => {
922 if encoding.version <= 4 {
923 debug_assert!(!val.is_empty());
924 }
925 w.write(val)?;
926 w.write_u8(0)?;
927 }
928 LineString::StringRef(val) => {
929 if encoding.version < 5 {
930 return Err(Error::NeedVersion(5));
931 }
932 w.write_offset(
933 strings.offset(val).0,
934 SectionId::DebugStr,
935 encoding.format.word_size(),
936 )?;
937 }
938 LineString::LineStringRef(val) => {
939 if encoding.version < 5 {
940 return Err(Error::NeedVersion(5));
941 }
942 w.write_offset(
943 line_strings.offset(val).0,
944 SectionId::DebugLineStr,
945 encoding.format.word_size(),
946 )?;
947 }
948 }
949 Ok(())
950 }
951}
952
953#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
957pub struct DirectoryId(usize);
958
959mod id {
961 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
966 pub struct FileId(usize);
967
968 impl FileId {
969 pub(crate) fn new(index: usize) -> Self {
971 FileId(index)
972 }
973
974 pub(super) fn index(self) -> usize {
976 self.0
977 }
978
979 pub(super) fn initial_state(version: u16) -> Self {
981 if version == 5 {
982 FileId(1)
985 } else {
986 FileId(0)
991 }
992 }
993
994 pub(crate) fn raw(self, version: u16) -> u64 {
998 if version <= 4 {
999 self.0 as u64 + 1
1000 } else {
1001 self.0 as u64
1002 }
1003 }
1004 }
1005}
1006pub use self::id::*;
1007
1008#[derive(Debug, Default, Clone, PartialEq, Eq)]
1010pub struct FileInfo {
1011 pub timestamp: u64,
1014
1015 pub size: u64,
1017
1018 pub md5: [u8; 16],
1022
1023 pub source: Option<LineString>,
1027}
1028
1029define_section!(
1030 DebugLine,
1031 DebugLineOffset,
1032 "A writable `.debug_line` section."
1033);
1034
1035#[cfg(feature = "read")]
1036pub use convert::*;
1037#[cfg(feature = "read")]
1038mod convert {
1039 use super::*;
1040 use crate::read::{self, Reader};
1041 use crate::write::{self, ConvertError, ConvertResult};
1042
1043 #[derive(Debug, PartialEq, Eq)]
1045 pub enum ConvertLineRow {
1046 SetAddress(u64),
1051 Row(LineRow),
1053 EndSequence(u64),
1055 }
1056
1057 #[derive(Debug, PartialEq, Eq)]
1059 pub struct ConvertLineSequence {
1060 pub start: Option<u64>,
1064 pub end: ConvertLineSequenceEnd,
1066 pub rows: Vec<LineRow>,
1070 }
1071
1072 #[derive(Debug, PartialEq, Eq)]
1074 pub enum ConvertLineSequenceEnd {
1075 Length(u64),
1077 Address(u64),
1079 }
1080
1081 #[derive(Debug)]
1083 enum ConvertLineState {
1084 ReadRow,
1086 SetAddress,
1088 ConvertRow,
1093 }
1094
1095 #[derive(Debug)]
1154 pub struct ConvertLineProgram<'a, R: Reader> {
1155 from_dwarf: &'a read::Dwarf<R>,
1156 from_program: read::IncompleteLineProgram<R>,
1157 from_row: read::LineRow,
1158 from_instructions: read::LineInstructions<R>,
1159 files: Vec<FileId>,
1160 dirs: Vec<DirectoryId>,
1161 program: LineProgram,
1162 line_strings: &'a mut write::LineStringTable,
1163 #[allow(unused)] strings: &'a mut write::StringTable,
1165 address: Option<u64>,
1166 state: ConvertLineState,
1167 }
1168
1169 impl<'a, R: Reader + 'a> ConvertLineProgram<'a, R> {
1170 pub(crate) fn new(
1171 from_dwarf: &'a read::Dwarf<R>,
1172 from_program: read::IncompleteLineProgram<R>,
1173 mut from_comp_name: Option<R>,
1174 encoding: Option<Encoding>,
1175 line_encoding: Option<LineEncoding>,
1176 line_strings: &'a mut write::LineStringTable,
1177 strings: &'a mut write::StringTable,
1178 ) -> ConvertResult<Self> {
1179 let encoding = encoding.unwrap_or(from_program.header().encoding());
1180 let line_encoding = line_encoding.unwrap_or(from_program.header().line_encoding());
1181
1182 let mut dirs = Vec::new();
1184 let mut files = Vec::new();
1185
1186 let from_header = from_program.header();
1187
1188 let working_dir = if let Some(from_working_dir) = from_header.directory(0) {
1189 Self::convert_string(from_working_dir, from_dwarf, encoding, line_strings)?
1190 } else if encoding.version <= 4 {
1191 LineString::String(Vec::new())
1193 } else {
1194 return Err(ConvertError::MissingCompilationDirectory);
1195 };
1196
1197 if from_header.version() >= 5 {
1199 from_comp_name = None;
1200 }
1201 let (source_dir, source_file) = if let Some(from_comp_name) = from_comp_name {
1202 (
1203 None,
1204 LineString::new(from_comp_name.to_slice()?, encoding, line_strings),
1205 )
1206 } else if let Some(from_source_file) = from_header.file(0) {
1207 let dir_index = from_source_file.directory_index();
1208 let source_dir = if dir_index != 0 {
1209 match from_header.directory(dir_index) {
1210 Some(from_source_dir) => Some(Self::convert_string(
1211 from_source_dir,
1212 from_dwarf,
1213 encoding,
1214 line_strings,
1215 )?),
1216 None => return Err(ConvertError::InvalidDirectoryIndex),
1217 }
1218 } else {
1219 None
1221 };
1222 let source_file = Self::convert_string(
1223 from_source_file.path_name(),
1224 from_dwarf,
1225 encoding,
1226 line_strings,
1227 )?;
1228 (source_dir, source_file)
1229 } else if encoding.version <= 4 {
1230 (None, LineString::String(Vec::new()))
1232 } else {
1233 return Err(ConvertError::MissingCompilationName);
1234 };
1235
1236 if from_header.line_base() > 0 {
1237 return Err(ConvertError::InvalidLineBase);
1238 }
1239 let mut program = LineProgram::new(
1240 encoding,
1241 line_encoding,
1242 working_dir,
1243 source_dir,
1244 source_file,
1245 None, );
1247
1248 if from_header.version() <= 4 {
1249 dirs.push(DirectoryId(0));
1251 files.push(FileId::new(0));
1254 }
1255
1256 for from_attr in from_header.include_directories() {
1257 let from_dir =
1258 Self::convert_string(from_attr.clone(), from_dwarf, encoding, line_strings)?;
1259 dirs.push(program.add_directory(from_dir));
1260 }
1261
1262 program.file_has_timestamp = from_header.file_has_timestamp();
1263 program.file_has_size = from_header.file_has_size();
1264 program.file_has_md5 = from_header.file_has_md5();
1265 program.file_has_source = from_header.file_has_source();
1266 for from_file in from_header.file_names() {
1267 let (from_name, from_dir, from_info) =
1268 Self::convert_file(from_file, from_dwarf, &dirs, encoding, line_strings)?;
1269 files.push(program.add_file(from_name, from_dir, from_info));
1270 }
1271
1272 let from_row = read::LineRow::new(from_program.header());
1275 let from_instructions = from_program.header().instructions();
1276 Ok(ConvertLineProgram {
1277 from_dwarf,
1278 from_program,
1279 from_row,
1280 from_instructions,
1281 files,
1282 dirs,
1283 program,
1284 line_strings,
1285 strings,
1286 address: None,
1287 state: ConvertLineState::ReadRow,
1288 })
1289 }
1290
1291 fn convert_string(
1292 from_attr: read::AttributeValue<R>,
1293 from_dwarf: &read::Dwarf<R>,
1294 encoding: Encoding,
1295 line_strings: &mut write::LineStringTable,
1296 ) -> ConvertResult<LineString> {
1297 let r = from_dwarf.attr_line_string(from_attr)?;
1298 Ok(LineString::new(r.to_slice()?, encoding, line_strings))
1299 }
1300
1301 fn convert_file(
1302 from_file: &read::FileEntry<R>,
1303 from_dwarf: &read::Dwarf<R>,
1304 dirs: &[DirectoryId],
1305 encoding: Encoding,
1306 line_strings: &mut write::LineStringTable,
1307 ) -> ConvertResult<(LineString, DirectoryId, Option<FileInfo>)> {
1308 let from_name =
1309 Self::convert_string(from_file.path_name(), from_dwarf, encoding, line_strings)?;
1310 let from_dir = from_file.directory_index();
1311 if from_dir >= dirs.len() as u64 {
1312 return Err(ConvertError::InvalidDirectoryIndex);
1313 }
1314 let from_dir = dirs[from_dir as usize];
1315 let from_info = Some(FileInfo {
1316 timestamp: from_file.timestamp(),
1317 size: from_file.size(),
1318 md5: *from_file.md5(),
1319 source: match from_file.source() {
1320 Some(source) => Some(Self::convert_string(
1321 source,
1322 from_dwarf,
1323 encoding,
1324 line_strings,
1325 )?),
1326 None => None,
1327 },
1328 });
1329 Ok((from_name, from_dir, from_info))
1330 }
1331
1332 pub fn read_row(&mut self) -> ConvertResult<Option<ConvertLineRow>> {
1336 match self.state {
1337 ConvertLineState::ReadRow => {}
1338 ConvertLineState::SetAddress => {
1339 if let Some(address) = self.address.take() {
1340 self.state = ConvertLineState::ConvertRow;
1341 return Ok(Some(ConvertLineRow::SetAddress(address)));
1342 }
1343 self.state = ConvertLineState::ReadRow;
1344 return Ok(Some(ConvertLineRow::Row(self.convert_row()?)));
1345 }
1346 ConvertLineState::ConvertRow => {
1347 self.state = ConvertLineState::ReadRow;
1348 return Ok(Some(ConvertLineRow::Row(self.convert_row()?)));
1349 }
1350 }
1351 let mut tombstone = false;
1352 self.address = None;
1353 self.from_row.reset(self.from_program.header());
1354 while let Some(instruction) = self
1355 .from_instructions
1356 .next_instruction(self.from_program.header())?
1357 {
1358 match instruction {
1359 read::LineInstruction::SetAddress(val) => {
1360 self.from_row.execute(
1362 read::LineInstruction::SetAddress(0),
1363 &mut self.from_program,
1364 )?;
1365 let tombstone_address =
1367 !0 >> (64 - self.from_program.header().encoding().address_size * 8);
1368 tombstone = val == tombstone_address;
1369 if !tombstone {
1370 self.address = Some(val);
1371 }
1372 continue;
1373 }
1374 read::LineInstruction::DefineFile(ref from_file) => {
1375 let (from_name, from_dir, from_info) = Self::convert_file(
1376 from_file,
1377 self.from_dwarf,
1378 &self.dirs,
1379 self.program.encoding(),
1380 self.line_strings,
1381 )?;
1382 self.files
1383 .push(self.program.add_file(from_name, from_dir, from_info));
1384 continue;
1385 }
1386 _ => {}
1387 }
1388 if !self.from_row.execute(instruction, &mut self.from_program)? {
1389 continue;
1391 }
1392 if tombstone {
1393 if self.from_row.end_sequence() {
1397 tombstone = false;
1398 self.address = None;
1399 }
1400 self.from_row.reset(self.from_program.header());
1401 continue;
1402 }
1403 if self.from_row.end_sequence() {
1404 return Ok(Some(ConvertLineRow::EndSequence(self.from_row.address())));
1405 }
1406 if let Some(address) = self.address.take() {
1407 self.state = ConvertLineState::ConvertRow;
1408 return Ok(Some(ConvertLineRow::SetAddress(address)));
1409 } else {
1410 self.state = ConvertLineState::ReadRow;
1411 return Ok(Some(ConvertLineRow::Row(self.convert_row()?)));
1412 }
1413 }
1414 Ok(None)
1415 }
1416
1417 fn convert_row(&self) -> ConvertResult<LineRow> {
1418 Ok(LineRow {
1419 address_offset: self.from_row.address(),
1420 op_index: self.from_row.op_index(),
1421 file: {
1422 let file = self.from_row.file_index();
1423 if file >= self.files.len() as u64 {
1424 return Err(ConvertError::InvalidFileIndex);
1425 }
1426 if file == 0 && self.from_program.header().version() <= 4 {
1427 return Err(ConvertError::InvalidFileIndex);
1428 }
1429 self.files[file as usize]
1430 },
1431 line: match self.from_row.line() {
1432 Some(line) => line.get(),
1433 None => 0,
1434 },
1435 column: match self.from_row.column() {
1436 read::ColumnType::LeftEdge => 0,
1437 read::ColumnType::Column(val) => val.get(),
1438 },
1439 discriminator: self.from_row.discriminator(),
1440 is_statement: self.from_row.is_stmt(),
1441 basic_block: self.from_row.basic_block(),
1442 prologue_end: self.from_row.prologue_end(),
1443 epilogue_begin: self.from_row.epilogue_begin(),
1444 isa: self.from_row.isa(),
1445 })
1446 }
1447
1448 pub fn read_sequence(&mut self) -> ConvertResult<Option<ConvertLineSequence>> {
1474 let mut start = None;
1475 let mut rows = Vec::new();
1476 match self.state {
1477 ConvertLineState::ReadRow => {}
1478 ConvertLineState::SetAddress | ConvertLineState::ConvertRow => {
1479 start = self.address;
1480 rows.push(self.convert_row()?);
1481 self.state = ConvertLineState::ReadRow;
1482 }
1483 }
1484 while let Some(row) = self.read_row()? {
1485 match row {
1486 ConvertLineRow::SetAddress(address) => {
1487 if !rows.is_empty() {
1488 self.address = Some(address);
1489 self.state = ConvertLineState::SetAddress;
1490 return Ok(Some(ConvertLineSequence {
1491 start,
1492 end: ConvertLineSequenceEnd::Address(address),
1493 rows,
1494 }));
1495 }
1496 start = Some(address);
1497 }
1498 ConvertLineRow::Row(row) => {
1499 rows.push(row);
1500 }
1501 ConvertLineRow::EndSequence(length) => {
1502 return Ok(Some(ConvertLineSequence {
1503 start,
1504 end: ConvertLineSequenceEnd::Length(length),
1505 rows,
1506 }));
1507 }
1508 }
1509 }
1510 if !rows.is_empty() {
1511 return Err(ConvertError::MissingLineEndSequence);
1512 }
1513 Ok(None)
1514 }
1515
1516 pub fn begin_sequence(&mut self, address: Option<Address>) {
1518 self.program.begin_sequence(address);
1519 }
1520
1521 pub fn set_address(&mut self, address: Address) {
1523 self.program.set_address(address);
1524 }
1525
1526 pub fn end_sequence(&mut self, address_offset: u64) {
1528 self.program.end_sequence(address_offset);
1529 }
1530
1531 pub fn in_sequence(&self) -> bool {
1533 self.program.in_sequence()
1534 }
1535
1536 pub fn generate_row(&mut self, row: LineRow) {
1538 *self.program.row() = row;
1539 self.program.generate_row();
1540 }
1541
1542 pub fn program(self) -> (LineProgram, Vec<FileId>) {
1547 (self.program, self.files)
1548 }
1549
1550 pub fn convert(
1557 mut self,
1558 convert_address: &dyn Fn(u64) -> Option<Address>,
1559 ) -> ConvertResult<(LineProgram, Vec<FileId>)> {
1560 while let Some(row) = self.read_row()? {
1561 match row {
1562 ConvertLineRow::SetAddress(address) => {
1563 let address =
1564 convert_address(address).ok_or(ConvertError::InvalidAddress)?;
1565 self.set_address(address);
1566 }
1567 ConvertLineRow::Row(row) => {
1568 self.generate_row(row);
1569 }
1570 ConvertLineRow::EndSequence(length) => {
1571 self.end_sequence(length);
1572 }
1573 }
1574 }
1575 if self.in_sequence() {
1576 return Err(ConvertError::MissingLineEndSequence);
1577 }
1578 Ok(self.program())
1579 }
1580 }
1581}
1582
1583#[cfg(test)]
1584#[cfg(feature = "read")]
1585mod tests {
1586 use super::*;
1587 use crate::LittleEndian;
1588 use crate::read;
1589 use crate::write::{AttributeValue, Dwarf, EndianVec, Sections, Unit};
1590
1591 #[test]
1592 fn test_line_program() {
1593 let dir1 = LineString::String(b"dir1".to_vec());
1594 let file1 = LineString::String(b"file1".to_vec());
1595 let dir2 = LineString::String(b"dir2".to_vec());
1596 let file2 = LineString::String(b"file2".to_vec());
1597
1598 let mut dwarf = Dwarf::new();
1599
1600 for &version in &[2, 3, 4, 5] {
1601 for &address_size in &[4, 8] {
1602 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1603 let encoding = Encoding {
1604 format,
1605 version,
1606 address_size,
1607 };
1608 let mut program = LineProgram::new(
1609 encoding,
1610 LineEncoding::default(),
1611 dir1.clone(),
1612 None,
1613 file1.clone(),
1614 None,
1615 );
1616
1617 assert_eq!(&dir1, program.get_directory(program.default_directory()));
1618 program.file_has_timestamp = true;
1619 program.file_has_size = true;
1620 if encoding.version >= 5 {
1621 program.file_has_md5 = true;
1622 }
1623
1624 if encoding.version >= 5 {
1628 program.file_has_source = true;
1629 }
1630
1631 let dir_id = program.add_directory(dir2.clone());
1632 assert_eq!(&dir2, program.get_directory(dir_id));
1633 assert_eq!(dir_id, program.add_directory(dir2.clone()));
1634
1635 let file_info = FileInfo {
1636 timestamp: 1,
1637 size: 2,
1638 md5: if encoding.version >= 5 {
1639 [3; 16]
1640 } else {
1641 [0; 16]
1642 },
1643 source: (encoding.version >= 5)
1644 .then(|| LineString::String(b"the source code\n".to_vec())),
1645 };
1646 let file_id = program.add_file(file2.clone(), dir_id, Some(file_info.clone()));
1647 assert_eq!((&file2, dir_id), program.get_file(file_id));
1648 assert_eq!(file_info, *program.get_file_info(file_id));
1649
1650 program.get_file_info_mut(file_id).size = 3;
1651 assert_ne!(file_info, *program.get_file_info(file_id));
1652 assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None));
1653 assert_ne!(file_info, *program.get_file_info(file_id));
1654 assert_eq!(
1655 file_id,
1656 program.add_file(file2.clone(), dir_id, Some(file_info.clone()))
1657 );
1658 assert_eq!(file_info, *program.get_file_info(file_id));
1659
1660 let mut unit = Unit::new(encoding, program);
1661 let root = unit.get_mut(unit.root());
1662 root.set(
1663 constants::DW_AT_comp_dir,
1664 AttributeValue::String(b"dir1".to_vec()),
1665 );
1666 root.set(
1667 constants::DW_AT_name,
1668 AttributeValue::String(b"file1".to_vec()),
1669 );
1670 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1671 root.set(
1672 constants::DW_AT_decl_file,
1673 AttributeValue::FileIndex(Some(file_id)),
1674 );
1675
1676 dwarf.units.add(unit);
1677 }
1678 }
1679 }
1680
1681 let mut sections = Sections::new(EndianVec::new(LittleEndian));
1682 dwarf.write(&mut sections).unwrap();
1683 let read_dwarf = sections.read(LittleEndian);
1684 let convert_dwarf =
1685 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap();
1686
1687 let mut convert_units = convert_dwarf.units.iter();
1688 for (_, unit) in dwarf.units.iter() {
1689 let program = &unit.line_program;
1690 let root = unit.get(unit.root());
1691 let Some(AttributeValue::FileIndex(Some(file_id))) =
1692 root.get(constants::DW_AT_decl_file)
1693 else {
1694 panic!("missing DW_AT_decl_file");
1695 };
1696
1697 let (_, convert_unit) = convert_units.next().unwrap();
1698 let convert_program = &convert_unit.line_program;
1699 let convert_root = convert_unit.get(convert_unit.root());
1700 let Some(AttributeValue::FileIndex(Some(convert_file_id))) =
1701 convert_root.get(constants::DW_AT_decl_file)
1702 else {
1703 panic!("missing DW_AT_decl_file");
1704 };
1705
1706 assert_eq!(convert_program.version(), program.version());
1707 assert_eq!(convert_program.address_size(), program.address_size());
1708 assert_eq!(convert_program.format(), program.format());
1709
1710 let (file, dir) = program.get_file(*file_id);
1711 let (convert_file, convert_dir) = convert_program.get_file(*convert_file_id);
1712 assert_eq!(
1713 dwarf.get_line_string(file),
1714 convert_dwarf.get_line_string(convert_file)
1715 );
1716 assert_eq!(
1717 dwarf.get_line_string(program.get_directory(dir)),
1718 convert_dwarf.get_line_string(convert_program.get_directory(convert_dir)),
1719 );
1720 let info = program.get_file_info(*file_id);
1721 let convert_info = convert_program.get_file_info(*convert_file_id);
1722 assert_eq!(info.timestamp, convert_info.timestamp);
1723 assert_eq!(info.size, convert_info.size);
1724 assert_eq!(info.md5, convert_info.md5);
1725 assert_eq!(
1726 info.source.as_ref().map(|s| dwarf.get_line_string(s)),
1727 convert_info
1728 .source
1729 .as_ref()
1730 .map(|s| convert_dwarf.get_line_string(s)),
1731 );
1732 }
1733 }
1734
1735 #[test]
1736 fn test_line_sequence() {
1737 let dir1 = &b"dir1"[..];
1738 let file1 = &b"file1"[..];
1739 let file2 = &b"file2"[..];
1740
1741 for &version in &[2, 3, 4, 5] {
1742 for &address_size in &[4, 8] {
1743 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1744 let encoding = Encoding {
1745 format,
1746 version,
1747 address_size,
1748 };
1749 let mut program = LineProgram::new(
1750 encoding,
1751 LineEncoding::default(),
1752 LineString::String(dir1.to_vec()),
1753 None,
1754 LineString::String(file1.to_vec()),
1755 None,
1756 );
1757 let dir_id = program.default_directory();
1758 program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1759 program.add_file(LineString::String(file2.to_vec()), dir_id, None);
1761
1762 {
1764 let mut program = program.clone();
1765 let address = Address::Constant(0x12);
1766 program.begin_sequence(Some(address));
1767 assert_eq!(
1768 program.instructions,
1769 vec![LineInstruction::SetAddress(address)]
1770 );
1771 }
1772
1773 {
1774 let mut program = program.clone();
1775 program.begin_sequence(None);
1776 assert_eq!(program.instructions, Vec::new());
1777 }
1778
1779 {
1780 let mut program = program.clone();
1781 let address = Address::Constant(0x12);
1782 program.set_address(address);
1783 assert_eq!(
1784 program.instructions,
1785 vec![LineInstruction::SetAddress(address)]
1786 );
1787 }
1788
1789 {
1790 let mut program = program.clone();
1791 program.generate_row();
1792 assert_eq!(program.instructions, vec![LineInstruction::Copy]);
1793 }
1794
1795 {
1796 let mut program = program.clone();
1797 program.end_sequence(0x1234);
1798 assert_eq!(
1799 program.instructions,
1800 vec![
1801 LineInstruction::AdvancePc(0x1234),
1802 LineInstruction::EndSequence
1803 ]
1804 );
1805 }
1806
1807 {
1808 let mut program = program.clone();
1809 program.end_sequence(0);
1810 assert_eq!(program.instructions, vec![LineInstruction::EndSequence]);
1811 }
1812
1813 let mut expected_instructions = Vec::new();
1815 let mut sequence_rows = Vec::new();
1816 let mut expected_rows = Vec::new();
1817 let mut expected_sequences = Vec::new();
1818
1819 let address = 0x12;
1821 let length = 0x1234;
1822
1823 expected_rows.push(ConvertLineRow::SetAddress(address));
1824 program.begin_sequence(Some(Address::Constant(address)));
1825
1826 expected_rows.push(ConvertLineRow::Row(program.row));
1827 sequence_rows.push(program.row);
1828 program.generate_row();
1829
1830 expected_rows.push(ConvertLineRow::EndSequence(length));
1831 expected_sequences.push(ConvertLineSequence {
1832 start: Some(0x12),
1833 end: ConvertLineSequenceEnd::Length(length),
1834 rows: core::mem::take(&mut sequence_rows),
1835 });
1836 program.end_sequence(length);
1837
1838 expected_instructions.extend_from_slice(&[
1839 LineInstruction::SetAddress(Address::Constant(address)),
1840 LineInstruction::Copy,
1841 LineInstruction::AdvancePc(length),
1842 LineInstruction::EndSequence,
1843 ]);
1844
1845 program.begin_sequence(None);
1847
1848 expected_rows.push(ConvertLineRow::EndSequence(0));
1849 expected_sequences.push(ConvertLineSequence {
1850 start: None,
1851 end: ConvertLineSequenceEnd::Length(0),
1852 rows: core::mem::take(&mut sequence_rows),
1853 });
1854 program.end_sequence(0);
1855
1856 expected_instructions.extend_from_slice(&[LineInstruction::EndSequence]);
1857
1858 let address1 = 0x12;
1860 let address2 = 0x34;
1861 let tombstone = !0u64 >> (64 - address_size * 8);
1862
1863 expected_rows.push(ConvertLineRow::SetAddress(address1));
1864 program.set_address(Address::Constant(address1));
1865
1866 expected_rows.push(ConvertLineRow::Row(program.row));
1867 sequence_rows.push(program.row);
1868 program.generate_row();
1869
1870 program.set_address(Address::Constant(tombstone));
1871 program.generate_row();
1872
1873 expected_rows.push(ConvertLineRow::SetAddress(address2));
1874 expected_sequences.push(ConvertLineSequence {
1875 start: Some(address1),
1876 end: ConvertLineSequenceEnd::Address(address2),
1877 rows: core::mem::take(&mut sequence_rows),
1878 });
1879 program.set_address(Address::Constant(address2));
1880
1881 expected_rows.push(ConvertLineRow::Row(program.row));
1882 sequence_rows.push(program.row);
1883 program.generate_row();
1884
1885 expected_rows.push(ConvertLineRow::EndSequence(length));
1886 expected_sequences.push(ConvertLineSequence {
1887 start: Some(address2),
1888 end: ConvertLineSequenceEnd::Length(length),
1889 rows: core::mem::take(&mut sequence_rows),
1890 });
1891 program.end_sequence(length);
1892
1893 expected_instructions.extend_from_slice(&[
1894 LineInstruction::SetAddress(Address::Constant(address1)),
1895 LineInstruction::Copy,
1896 LineInstruction::SetAddress(Address::Constant(address2)),
1897 LineInstruction::Copy,
1898 LineInstruction::AdvancePc(length),
1899 LineInstruction::EndSequence,
1900 ]);
1901
1902 expected_rows.push(ConvertLineRow::Row(program.row));
1904 sequence_rows.push(program.row);
1905 program.generate_row();
1906
1907 expected_rows.push(ConvertLineRow::EndSequence(length));
1908 expected_sequences.push(ConvertLineSequence {
1909 start: None,
1910 end: ConvertLineSequenceEnd::Length(length),
1911 rows: core::mem::take(&mut sequence_rows),
1912 });
1913 program.end_sequence(length);
1914
1915 expected_instructions.extend_from_slice(&[
1916 LineInstruction::Copy,
1917 LineInstruction::AdvancePc(length),
1918 LineInstruction::EndSequence,
1919 ]);
1920
1921 let mut unit = Unit::new(encoding, program);
1923 let root = unit.get_mut(unit.root());
1924 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1925 let mut dwarf = Dwarf::new();
1926 dwarf.units.add(unit);
1927 let mut sections = Sections::new(EndianVec::new(LittleEndian));
1928 dwarf.write(&mut sections).unwrap();
1929
1930 let read_dwarf = sections.read(LittleEndian);
1932 let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
1933 let read_unit = read_dwarf.unit(read_unit_header).unwrap();
1934 let read_program = &read_unit.line_program.unwrap();
1935
1936 let convert_dwarf =
1938 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
1939 .unwrap();
1940 let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
1941 let convert_program = &convert_unit.line_program;
1942 assert_eq!(convert_program.instructions, expected_instructions);
1943
1944 let mut convert_line_strings = LineStringTable::default();
1946 let mut convert_strings = StringTable::default();
1947 let mut convert_line = ConvertLineProgram::new(
1948 &read_dwarf,
1949 read_program.clone(),
1950 None,
1951 None,
1952 None,
1953 &mut convert_line_strings,
1954 &mut convert_strings,
1955 )
1956 .unwrap();
1957 let mut convert_rows = Vec::new();
1958 while let Some(convert_row) = convert_line.read_row().unwrap() {
1959 convert_rows.push(convert_row);
1960 }
1961 assert_eq!(convert_rows, expected_rows);
1962
1963 let mut convert_line_strings = LineStringTable::default();
1965 let mut convert_strings = StringTable::default();
1966 let mut convert_line = ConvertLineProgram::new(
1967 &read_dwarf,
1968 read_program.clone(),
1969 None,
1970 None,
1971 None,
1972 &mut convert_line_strings,
1973 &mut convert_strings,
1974 )
1975 .unwrap();
1976 let mut convert_sequences = Vec::new();
1977 while let Some(convert_sequence) = convert_line.read_sequence().unwrap() {
1978 convert_sequences.push(convert_sequence);
1979 }
1980 assert_eq!(convert_sequences, expected_sequences);
1981 }
1982 }
1983 }
1984 }
1985
1986 #[test]
1987 fn test_line_row() {
1988 let dir1 = &b"dir1"[..];
1989 let file1 = &b"file1"[..];
1990 let file2 = &b"file2"[..];
1991
1992 for &version in &[2, 3, 4, 5] {
1993 for &address_size in &[4, 8] {
1994 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1995 let encoding = Encoding {
1996 format,
1997 version,
1998 address_size,
1999 };
2000 let line_base = -5;
2001 let line_range = 14;
2002 let neg_line_base = (-line_base) as u8;
2003 let mut program = LineProgram::new(
2004 encoding,
2005 LineEncoding {
2006 line_base,
2007 line_range,
2008 ..Default::default()
2009 },
2010 LineString::String(dir1.to_vec()),
2011 None,
2012 LineString::String(file1.to_vec()),
2013 None,
2014 );
2015 let dir_id = program.default_directory();
2016 let file1_id =
2017 program.add_file(LineString::String(file1.to_vec()), dir_id, None);
2018 let file2_id =
2019 program.add_file(LineString::String(file2.to_vec()), dir_id, None);
2020
2021 program.row.line = 0x1000;
2023 program.generate_row();
2024 let base_row = program.row;
2025 let base_instructions = program.instructions.clone();
2026
2027 let mut tests = Vec::new();
2029
2030 let row = base_row;
2031 tests.push((row, vec![LineInstruction::Copy]));
2032
2033 let mut row = base_row;
2034 row.line -= u64::from(neg_line_base);
2035 tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)]));
2036
2037 let mut row = base_row;
2038 row.line += u64::from(line_range) - 1;
2039 row.line -= u64::from(neg_line_base);
2040 tests.push((
2041 row,
2042 vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)],
2043 ));
2044
2045 let mut row = base_row;
2046 row.line += u64::from(line_range);
2047 row.line -= u64::from(neg_line_base);
2048 tests.push((
2049 row,
2050 vec![
2051 LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)),
2052 LineInstruction::Copy,
2053 ],
2054 ));
2055
2056 let mut row = base_row;
2057 row.address_offset = 1;
2058 row.line -= u64::from(neg_line_base);
2059 tests.push((
2060 row,
2061 vec![LineInstruction::Special(OPCODE_BASE + line_range)],
2062 ));
2063
2064 let op_range = (255 - OPCODE_BASE) / line_range;
2065 let mut row = base_row;
2066 row.address_offset = u64::from(op_range);
2067 row.line -= u64::from(neg_line_base);
2068 tests.push((
2069 row,
2070 vec![LineInstruction::Special(
2071 OPCODE_BASE + op_range * line_range,
2072 )],
2073 ));
2074
2075 let mut row = base_row;
2076 row.address_offset = u64::from(op_range);
2077 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
2078 row.line -= u64::from(neg_line_base);
2079 tests.push((row, vec![LineInstruction::Special(255)]));
2080
2081 let mut row = base_row;
2082 row.address_offset = u64::from(op_range);
2083 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
2084 row.line -= u64::from(neg_line_base);
2085 tests.push((
2086 row,
2087 vec![LineInstruction::ConstAddPc, LineInstruction::Copy],
2088 ));
2089
2090 let mut row = base_row;
2091 row.address_offset = u64::from(op_range);
2092 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
2093 row.line -= u64::from(neg_line_base);
2094 tests.push((
2095 row,
2096 vec![
2097 LineInstruction::ConstAddPc,
2098 LineInstruction::Special(OPCODE_BASE + 6),
2099 ],
2100 ));
2101
2102 let mut row = base_row;
2103 row.address_offset = u64::from(op_range) * 2;
2104 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
2105 row.line -= u64::from(neg_line_base);
2106 tests.push((
2107 row,
2108 vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)],
2109 ));
2110
2111 let mut row = base_row;
2112 row.address_offset = u64::from(op_range) * 2;
2113 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
2114 row.line -= u64::from(neg_line_base);
2115 tests.push((
2116 row,
2117 vec![
2118 LineInstruction::AdvancePc(row.address_offset),
2119 LineInstruction::Copy,
2120 ],
2121 ));
2122
2123 let mut row = base_row;
2124 row.address_offset = u64::from(op_range) * 2;
2125 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
2126 row.line -= u64::from(neg_line_base);
2127 tests.push((
2128 row,
2129 vec![
2130 LineInstruction::AdvancePc(row.address_offset),
2131 LineInstruction::Special(OPCODE_BASE + 6),
2132 ],
2133 ));
2134
2135 let mut row = base_row;
2136 row.address_offset = 0x1234;
2137 tests.push((
2138 row,
2139 vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy],
2140 ));
2141
2142 let mut row = base_row;
2143 row.line += 0x1234;
2144 tests.push((
2145 row,
2146 vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy],
2147 ));
2148
2149 let mut row = base_row;
2150 row.file = file1_id;
2151 if version == 5 {
2152 tests.push((
2155 row,
2156 vec![LineInstruction::SetFile(file1_id), LineInstruction::Copy],
2157 ));
2158 } else {
2159 tests.push((row, vec![LineInstruction::Copy]));
2161 }
2162
2163 let mut row = base_row;
2164 row.file = file2_id;
2165 if version == 5 {
2166 tests.push((row, vec![LineInstruction::Copy]));
2167 } else {
2168 tests.push((
2169 row,
2170 vec![LineInstruction::SetFile(file2_id), LineInstruction::Copy],
2171 ));
2172 }
2173
2174 let mut row = base_row;
2175 row.column = 0x1234;
2176 tests.push((
2177 row,
2178 vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy],
2179 ));
2180
2181 let mut row = base_row;
2182 row.discriminator = 0x1234;
2183 tests.push((
2184 row,
2185 vec![
2186 LineInstruction::SetDiscriminator(0x1234),
2187 LineInstruction::Copy,
2188 ],
2189 ));
2190
2191 let mut row = base_row;
2192 row.is_statement = !row.is_statement;
2193 tests.push((
2194 row,
2195 vec![LineInstruction::NegateStatement, LineInstruction::Copy],
2196 ));
2197
2198 let mut row = base_row;
2199 row.basic_block = true;
2200 tests.push((
2201 row,
2202 vec![LineInstruction::SetBasicBlock, LineInstruction::Copy],
2203 ));
2204
2205 let mut row = base_row;
2206 row.prologue_end = true;
2207 tests.push((
2208 row,
2209 vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy],
2210 ));
2211
2212 let mut row = base_row;
2213 row.epilogue_begin = true;
2214 tests.push((
2215 row,
2216 vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy],
2217 ));
2218
2219 let mut row = base_row;
2220 row.isa = 0x1234;
2221 tests.push((
2222 row,
2223 vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy],
2224 ));
2225
2226 for test in tests {
2227 let mut program = program.clone();
2229 program.row = test.0;
2230 program.generate_row();
2231 program.end_sequence(test.0.address_offset);
2232 assert_eq!(
2233 &program.instructions
2234 [base_instructions.len()..program.instructions.len() - 1],
2235 &test.1[..]
2236 );
2237 assert_eq!(
2238 program.instructions.last(),
2239 Some(&LineInstruction::EndSequence)
2240 );
2241
2242 let mut unit = Unit::new(encoding, program);
2244 let root = unit.get_mut(unit.root());
2245 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2246
2247 let mut dwarf = Dwarf::new();
2248 dwarf.units.add(unit);
2249
2250 let mut sections = Sections::new(EndianVec::new(LittleEndian));
2251 dwarf.write(&mut sections).unwrap();
2252 let read_dwarf = sections.read(LittleEndian);
2253
2254 let convert_dwarf =
2255 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2256 .unwrap();
2257 let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
2258 let convert_program = &convert_unit.line_program;
2259
2260 assert_eq!(
2261 &convert_program.instructions
2262 [base_instructions.len()..convert_program.instructions.len() - 1],
2263 &test.1[..]
2264 );
2265 assert_eq!(
2266 convert_program.instructions.last(),
2267 Some(&LineInstruction::EndSequence)
2268 );
2269 }
2270 }
2271 }
2272 }
2273 }
2274
2275 #[test]
2276 fn test_line_instruction() {
2277 let dir1 = &b"dir1"[..];
2278 let file1 = &b"file1"[..];
2279
2280 for &version in &[2, 3, 4, 5] {
2281 for &address_size in &[4, 8] {
2282 for &format in &[Format::Dwarf32, Format::Dwarf64] {
2283 let encoding = Encoding {
2284 format,
2285 version,
2286 address_size,
2287 };
2288 let mut program = LineProgram::new(
2289 encoding,
2290 LineEncoding::default(),
2291 LineString::String(dir1.to_vec()),
2292 None,
2293 LineString::String(file1.to_vec()),
2294 None,
2295 );
2296 let dir_id = program.default_directory();
2297 let file_id =
2298 program.add_file(LineString::String(file1.to_vec()), dir_id, None);
2299
2300 for (inst, expect_inst) in &[
2301 (
2302 LineInstruction::Special(OPCODE_BASE),
2303 read::LineInstruction::Special(OPCODE_BASE),
2304 ),
2305 (
2306 LineInstruction::Special(255),
2307 read::LineInstruction::Special(255),
2308 ),
2309 (LineInstruction::Copy, read::LineInstruction::Copy),
2310 (
2311 LineInstruction::AdvancePc(0x12),
2312 read::LineInstruction::AdvancePc(0x12),
2313 ),
2314 (
2315 LineInstruction::AdvanceLine(0x12),
2316 read::LineInstruction::AdvanceLine(0x12),
2317 ),
2318 (
2319 LineInstruction::SetFile(file_id),
2320 read::LineInstruction::SetFile(file_id.raw(encoding.version)),
2321 ),
2322 (
2323 LineInstruction::SetColumn(0x12),
2324 read::LineInstruction::SetColumn(0x12),
2325 ),
2326 (
2327 LineInstruction::NegateStatement,
2328 read::LineInstruction::NegateStatement,
2329 ),
2330 (
2331 LineInstruction::SetBasicBlock,
2332 read::LineInstruction::SetBasicBlock,
2333 ),
2334 (
2335 LineInstruction::ConstAddPc,
2336 read::LineInstruction::ConstAddPc,
2337 ),
2338 (
2339 LineInstruction::SetPrologueEnd,
2340 read::LineInstruction::SetPrologueEnd,
2341 ),
2342 (
2343 LineInstruction::SetEpilogueBegin,
2344 read::LineInstruction::SetEpilogueBegin,
2345 ),
2346 (
2347 LineInstruction::SetIsa(0x12),
2348 read::LineInstruction::SetIsa(0x12),
2349 ),
2350 (
2351 LineInstruction::EndSequence,
2352 read::LineInstruction::EndSequence,
2353 ),
2354 (
2355 LineInstruction::SetAddress(Address::Constant(0x12)),
2356 read::LineInstruction::SetAddress(0x12),
2357 ),
2358 (
2359 LineInstruction::SetDiscriminator(0x12),
2360 read::LineInstruction::SetDiscriminator(0x12),
2361 ),
2362 ][..]
2363 {
2364 let mut program = program.clone();
2365 program.instructions.push(*inst);
2366
2367 let mut unit = Unit::new(encoding, program);
2368 let root = unit.get_mut(unit.root());
2369 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2370
2371 let mut dwarf = Dwarf::new();
2372 dwarf.units.add(unit);
2373 let mut sections = Sections::new(EndianVec::new(LittleEndian));
2374 dwarf.write(&mut sections).unwrap();
2375
2376 let read_dwarf = sections.read(LittleEndian);
2377 let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2378 let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2379 let read_unit = read_unit.unit_ref(&read_dwarf);
2380 let read_header = read_unit.line_program.as_ref().unwrap().header();
2381 let mut read_insts = read_header.instructions();
2382 assert_eq!(
2383 *expect_inst,
2384 read_insts.next_instruction(read_header).unwrap().unwrap()
2385 );
2386 assert_eq!(None, read_insts.next_instruction(read_header).unwrap());
2387 }
2388 }
2389 }
2390 }
2391 }
2392
2393 #[test]
2395 fn test_advance() {
2396 let encoding = Encoding {
2397 format: Format::Dwarf32,
2398 version: 4,
2399 address_size: 8,
2400 };
2401
2402 let dir1 = &b"dir1"[..];
2403 let file1 = &b"file1"[..];
2404
2405 let addresses = 0..50;
2406 let lines = -10..25i64;
2407
2408 for minimum_instruction_length in [1, 4] {
2409 for maximum_operations_per_instruction in [1, 3] {
2410 for line_base in [-5, 0] {
2411 for line_range in [10, 20] {
2412 let line_encoding = LineEncoding {
2413 minimum_instruction_length,
2414 maximum_operations_per_instruction,
2415 line_base,
2416 line_range,
2417 default_is_stmt: true,
2418 };
2419 let mut program = LineProgram::new(
2420 encoding,
2421 line_encoding,
2422 LineString::String(dir1.to_vec()),
2423 None,
2424 LineString::String(file1.to_vec()),
2425 None,
2426 );
2427 for address_advance in addresses.clone() {
2428 program.set_address(Address::Constant(0x1000));
2429 program.row().line = 0x10000;
2430 program.generate_row();
2431 for line_advance in lines.clone() {
2432 {
2433 let row = program.row();
2434 row.address_offset +=
2435 address_advance * u64::from(minimum_instruction_length);
2436 row.line = row.line.wrapping_add(line_advance as u64);
2437 }
2438 program.generate_row();
2439 }
2440 let address_offset = program.row().address_offset
2441 + u64::from(minimum_instruction_length);
2442 program.end_sequence(address_offset);
2443 }
2444
2445 let mut unit = Unit::new(encoding, program);
2446 let root = unit.get_mut(unit.root());
2447 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2448
2449 let mut dwarf = Dwarf::new();
2450 dwarf.units.add(unit);
2451 let mut sections = Sections::new(EndianVec::new(LittleEndian));
2452 dwarf.write(&mut sections).unwrap();
2453
2454 let read_dwarf = sections.read(LittleEndian);
2455 let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2456 let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2457 let read_unit = read_unit.unit_ref(&read_dwarf);
2458 let read_program = read_unit.line_program.clone().unwrap();
2459
2460 let mut rows = read_program.rows();
2461 for address_advance in addresses.clone() {
2462 let mut address;
2463 let mut line;
2464 {
2465 let row = rows.next_row().unwrap().unwrap().1;
2466 address = row.address();
2467 line = row.line().unwrap().get();
2468 }
2469 assert_eq!(address, 0x1000);
2470 assert_eq!(line, 0x10000);
2471 for line_advance in lines.clone() {
2472 let row = rows.next_row().unwrap().unwrap().1;
2473 assert_eq!(
2474 row.address() - address,
2475 address_advance * u64::from(minimum_instruction_length)
2476 );
2477 assert_eq!(
2478 (row.line().unwrap().get() as i64) - (line as i64),
2479 line_advance
2480 );
2481 address = row.address();
2482 line = row.line().unwrap().get();
2483 }
2484 let row = rows.next_row().unwrap().unwrap().1;
2485 assert!(row.end_sequence());
2486 }
2487 }
2488 }
2489 }
2490 }
2491 }
2492
2493 #[test]
2494 fn test_line_string() {
2495 let version = 5;
2496
2497 let file1 = "file1";
2498
2499 for &address_size in &[4, 8] {
2500 for &format in &[Format::Dwarf32, Format::Dwarf64] {
2501 let encoding = Encoding {
2502 format,
2503 version,
2504 address_size,
2505 };
2506
2507 let files: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [
2508 &mut |_dwarf| LineString::String(file1.as_bytes().to_vec()),
2509 &mut |dwarf| LineString::StringRef(dwarf.strings.add(file1)),
2510 &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(file1)),
2511 ];
2512
2513 for file in files {
2514 let mut dwarf = Dwarf::new();
2515 let file = file(&mut dwarf);
2516
2517 let mut program = LineProgram::new(
2518 encoding,
2519 LineEncoding::default(),
2520 LineString::String(b"dir".to_vec()),
2521 None,
2522 file.clone(),
2523 None,
2524 );
2525 program.set_address(Address::Constant(0x1000));
2526 program.row().line = 0x10000;
2527 program.generate_row();
2528
2529 let mut unit = Unit::new(encoding, program);
2530 let root = unit.get_mut(unit.root());
2531 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2532 dwarf.units.add(unit);
2533
2534 let mut sections = Sections::new(EndianVec::new(LittleEndian));
2535 dwarf.write(&mut sections).unwrap();
2536
2537 let read_dwarf = sections.read(LittleEndian);
2538 let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2539 let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2540 let read_unit = read_unit.unit_ref(&read_dwarf);
2541 let read_program = read_unit.line_program.clone().unwrap();
2542 let read_header = read_program.header();
2543 let read_file = read_header.file(0).unwrap();
2544 let read_path = read_unit.attr_string(read_file.path_name()).unwrap();
2545 assert_eq!(read_path.slice(), file1.as_bytes());
2546 }
2547 }
2548 }
2549 }
2550
2551 #[test]
2552 fn test_missing_comp_dir() {
2553 for &version in &[2, 3, 4, 5] {
2554 for &address_size in &[4, 8] {
2555 for &format in &[Format::Dwarf32, Format::Dwarf64] {
2556 let encoding = Encoding {
2557 format,
2558 version,
2559 address_size,
2560 };
2561 let mut program = LineProgram::new(
2562 encoding,
2563 LineEncoding::default(),
2564 LineString::String(Vec::new()),
2565 None,
2566 LineString::String(Vec::new()),
2567 None,
2568 );
2569 let dir_id = program.default_directory();
2571 let file_id =
2572 program.add_file(LineString::String(b"file1".to_vec()), dir_id, None);
2573 program.set_address(Address::Constant(0x1000));
2574 program.row().file = file_id;
2575 program.row().line = 0x10000;
2576 program.generate_row();
2577 program.end_sequence(20);
2578
2579 let mut unit = Unit::new(encoding, program);
2580 let root = unit.get_mut(unit.root());
2581 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2583
2584 let mut dwarf = Dwarf::new();
2585 dwarf.units.add(unit);
2586 let mut sections = Sections::new(EndianVec::new(LittleEndian));
2587 dwarf.write(&mut sections).unwrap();
2588 let read_dwarf = sections.read(LittleEndian);
2589 let _convert_dwarf =
2590 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2591 .unwrap();
2592 }
2593 }
2594 }
2595 }
2596
2597 #[test]
2598 fn test_separate_working_dir() {
2599 let working_dir = LineString::String(b"working".to_vec());
2600 let source_dir = LineString::String(b"source".to_vec());
2601 let source_file = LineString::String(b"file".to_vec());
2602
2603 for &version in &[2, 3, 4, 5] {
2604 for &address_size in &[4, 8] {
2605 for &format in &[Format::Dwarf32, Format::Dwarf64] {
2606 let encoding = Encoding {
2607 format,
2608 version,
2609 address_size,
2610 };
2611 let mut program = LineProgram::new(
2612 encoding,
2613 LineEncoding::default(),
2614 working_dir.clone(),
2615 Some(source_dir.clone()),
2616 source_file.clone(),
2617 None,
2618 );
2619
2620 assert_eq!(
2621 &working_dir,
2622 program.get_directory(program.default_directory())
2623 );
2624
2625 let dir_id = program.add_directory(source_dir.clone());
2627 let file_id = program.add_file(source_file.clone(), dir_id, None);
2628 program.begin_sequence(Some(Address::Constant(0x1000)));
2629 program.row().file = file_id;
2630 program.row().line = 0x10000;
2631 program.generate_row();
2632 program.end_sequence(20);
2633
2634 let mut unit = Unit::new(encoding, program);
2636 let root = unit.get_mut(unit.root());
2637 root.set(
2638 constants::DW_AT_comp_dir,
2639 AttributeValue::String(b"working".to_vec()),
2640 );
2641 root.set(
2642 constants::DW_AT_name,
2643 AttributeValue::String(b"source/file".to_vec()),
2644 );
2645 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2646
2647 let mut dwarf = Dwarf::new();
2648 dwarf.units.add(unit);
2649
2650 let mut sections = Sections::new(EndianVec::new(LittleEndian));
2651 dwarf.write(&mut sections).unwrap();
2652 let read_dwarf = sections.read(LittleEndian);
2653
2654 let convert_dwarf =
2655 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2656 .unwrap();
2657 let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
2658 let convert_program = &convert_unit.line_program;
2659
2660 assert_eq!(
2661 dwarf.get_line_string(&working_dir),
2662 convert_dwarf.get_line_string(
2663 convert_program.get_directory(convert_program.default_directory())
2664 ),
2665 );
2666 let (_file_id, file, dir_id) = convert_program.files().next().unwrap();
2667 let dir = convert_program.get_directory(dir_id);
2668 assert_eq!(
2669 dwarf.get_line_string(&source_file),
2670 convert_dwarf.get_line_string(file)
2671 );
2672 assert_eq!(
2673 dwarf.get_line_string(&source_dir),
2674 convert_dwarf.get_line_string(dir),
2675 );
2676 }
2677 }
2678 }
2679 }
2680
2681 #[test]
2682 fn test_file_source() {
2683 let version = 5;
2684
2685 let source1 = "source1";
2686
2687 for &address_size in &[4, 8] {
2688 for &format in &[Format::Dwarf32, Format::Dwarf64] {
2689 let encoding = Encoding {
2690 format,
2691 version,
2692 address_size,
2693 };
2694
2695 let sources: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [
2696 &mut |_dwarf| LineString::String(source1.as_bytes().to_vec()),
2697 &mut |dwarf| LineString::StringRef(dwarf.strings.add(source1)),
2698 &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(source1)),
2699 ];
2700
2701 for source in sources {
2702 let mut dwarf = Dwarf::new();
2703 let source = Some(source(&mut dwarf));
2704
2705 let mut program = LineProgram::new(
2706 encoding,
2707 LineEncoding::default(),
2708 LineString::String(b"dir".to_vec()),
2709 None,
2710 LineString::String(b"file".to_vec()),
2711 Some(FileInfo {
2712 timestamp: 0,
2713 size: 0,
2714 md5: [0; 16],
2715 source,
2716 }),
2717 );
2718 program.file_has_source = true;
2719
2720 let file_id = program.files().next().unwrap().0;
2721 program.begin_sequence(Some(Address::Constant(0x1000)));
2722 program.row().file = file_id;
2723 program.row().line = 0x10000;
2724 program.generate_row();
2725 program.end_sequence(20);
2726
2727 let mut unit = Unit::new(encoding, program);
2728 let root = unit.get_mut(unit.root());
2729 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
2730 dwarf.units.add(unit);
2731
2732 let mut sections = Sections::new(EndianVec::new(LittleEndian));
2733 dwarf.write(&mut sections).unwrap();
2734
2735 let read_dwarf = sections.read(LittleEndian);
2736 let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
2737 let read_unit = read_dwarf.unit(read_unit_header).unwrap();
2738 let read_unit = read_unit.unit_ref(&read_dwarf);
2739 let read_program = read_unit.line_program.clone().unwrap();
2740 let read_header = read_program.header();
2741 let read_file = read_header.file(0).unwrap();
2742 let read_source = read_unit.attr_string(read_file.source().unwrap()).unwrap();
2743 assert_eq!(read_source.slice(), source1.as_bytes());
2744
2745 let convert_dwarf =
2746 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
2747 .unwrap();
2748 let (_, convert_unit) = convert_dwarf.units.iter().next().unwrap();
2749 let convert_program = &convert_unit.line_program;
2750 let convert_file_id = convert_program.files().next().unwrap().0;
2751 let convert_file_info = convert_program.get_file_info(convert_file_id);
2752 assert_eq!(
2753 convert_dwarf.get_line_string(convert_file_info.source.as_ref().unwrap()),
2754 source1.as_bytes(),
2755 );
2756 }
2757 }
2758 }
2759 }
2760}