1use alloc::vec::Vec;
2use indexmap::{IndexMap, IndexSet};
3use std::ops::{Deref, DerefMut};
4
5use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId};
6use crate::constants;
7use crate::leb128;
8use crate::write::{
9 Address, DebugLineStrOffsets, DebugStrOffsets, Error, LineStringId, LineStringTable, Result,
10 Section, StringId, Writer,
11};
12
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(
99 encoding: Encoding,
100 line_encoding: LineEncoding,
101 comp_dir: LineString,
102 comp_file: LineString,
103 comp_file_info: Option<FileInfo>,
104 ) -> LineProgram {
105 assert!(line_encoding.line_base <= 0);
108 assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0);
109 let mut program = LineProgram {
110 none: false,
111 encoding,
112 line_encoding,
113 directories: IndexSet::new(),
114 files: IndexMap::new(),
115 prev_row: LineRow::initial_state(encoding, line_encoding),
116 row: LineRow::initial_state(encoding, line_encoding),
117 instructions: Vec::new(),
118 in_sequence: false,
119 file_has_timestamp: false,
120 file_has_size: false,
121 file_has_md5: false,
122 file_has_source: false,
123 };
124 let comp_dir_id = program.add_directory(comp_dir);
128 if encoding.version >= 5 {
130 program.add_file(comp_file, comp_dir_id, comp_file_info);
131 }
132 program
133 }
134
135 pub fn none() -> Self {
142 let encoding = Encoding {
143 format: Format::Dwarf32,
144 version: 2,
145 address_size: 0,
146 };
147 let line_encoding = LineEncoding::default();
148 LineProgram {
149 none: true,
150 encoding,
151 line_encoding,
152 directories: IndexSet::new(),
153 files: IndexMap::new(),
154 prev_row: LineRow::initial_state(encoding, line_encoding),
155 row: LineRow::initial_state(encoding, line_encoding),
156 instructions: Vec::new(),
157 in_sequence: false,
158 file_has_timestamp: false,
159 file_has_size: false,
160 file_has_md5: false,
161 file_has_source: false,
162 }
163 }
164
165 #[inline]
167 pub fn is_none(&self) -> bool {
168 self.none
169 }
170
171 #[inline]
173 pub fn encoding(&self) -> Encoding {
174 self.encoding
175 }
176
177 #[inline]
179 pub fn version(&self) -> u16 {
180 self.encoding.version
181 }
182
183 #[inline]
185 pub fn address_size(&self) -> u8 {
186 self.encoding.address_size
187 }
188
189 #[inline]
191 pub fn format(&self) -> Format {
192 self.encoding.format
193 }
194
195 #[inline]
197 pub fn default_directory(&self) -> DirectoryId {
198 DirectoryId(0)
199 }
200
201 pub fn add_directory(&mut self, directory: LineString) -> DirectoryId {
212 if let LineString::String(ref val) = directory {
213 if self.encoding.version <= 4 && !self.directories.is_empty() {
216 assert!(!val.is_empty());
217 }
218 assert!(!val.contains(&0));
219 }
220 let (index, _) = self.directories.insert_full(directory);
221 DirectoryId(index)
222 }
223
224 pub fn get_directory(&self, id: DirectoryId) -> &LineString {
230 self.directories.get_index(id.0).unwrap()
231 }
232
233 pub fn add_file(
251 &mut self,
252 file: LineString,
253 directory: DirectoryId,
254 info: Option<FileInfo>,
255 ) -> FileId {
256 if let LineString::String(ref val) = file {
257 if self.encoding.version <= 4 {
258 assert!(!val.is_empty());
259 }
260 assert!(!val.contains(&0));
261 }
262
263 let key = (file, directory);
264 let index = if let Some(info) = info {
265 let (index, _) = self.files.insert_full(key, info);
266 index
267 } else {
268 let entry = self.files.entry(key);
269 let index = entry.index();
270 entry.or_default();
271 index
272 };
273 FileId::new(index)
274 }
275
276 pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) {
282 self.files
283 .get_index(id.index())
284 .map(|entry| (&(entry.0).0, (entry.0).1))
285 .unwrap()
286 }
287
288 pub fn get_file_info(&self, id: FileId) -> &FileInfo {
294 self.files
295 .get_index(id.index())
296 .map(|entry| entry.1)
297 .unwrap()
298 }
299
300 pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo {
306 self.files
307 .get_index_mut(id.index())
308 .map(|entry| entry.1)
309 .unwrap()
310 }
311
312 pub fn begin_sequence(&mut self, address: Option<Address>) {
318 assert!(!self.in_sequence);
319 self.in_sequence = true;
320 if let Some(address) = address {
321 self.instructions.push(LineInstruction::SetAddress(address));
322 }
323 }
324
325 pub fn end_sequence(&mut self, address_offset: u64) {
333 assert!(self.in_sequence);
334 self.in_sequence = false;
335 self.row.address_offset = address_offset;
336 let op_advance = self.op_advance();
337 if op_advance != 0 {
338 self.instructions
339 .push(LineInstruction::AdvancePc(op_advance));
340 }
341 self.instructions.push(LineInstruction::EndSequence);
342 self.prev_row = LineRow::initial_state(self.encoding, self.line_encoding);
343 self.row = LineRow::initial_state(self.encoding, self.line_encoding);
344 }
345
346 #[inline]
348 pub fn in_sequence(&self) -> bool {
349 self.in_sequence
350 }
351
352 #[inline]
354 pub fn row(&mut self) -> &mut LineRow {
355 &mut self.row
356 }
357
358 pub fn generate_row(&mut self) {
368 assert!(self.in_sequence);
369
370 if self.row.discriminator != 0 {
372 self.instructions
373 .push(LineInstruction::SetDiscriminator(self.row.discriminator));
374 self.row.discriminator = 0;
375 }
376 if self.row.basic_block {
377 self.instructions.push(LineInstruction::SetBasicBlock);
378 self.row.basic_block = false;
379 }
380 if self.row.prologue_end {
381 self.instructions.push(LineInstruction::SetPrologueEnd);
382 self.row.prologue_end = false;
383 }
384 if self.row.epilogue_begin {
385 self.instructions.push(LineInstruction::SetEpilogueBegin);
386 self.row.epilogue_begin = false;
387 }
388
389 if self.row.is_statement != self.prev_row.is_statement {
391 self.instructions.push(LineInstruction::NegateStatement);
392 }
393 if self.row.file != self.prev_row.file {
394 self.instructions
395 .push(LineInstruction::SetFile(self.row.file));
396 }
397 if self.row.column != self.prev_row.column {
398 self.instructions
399 .push(LineInstruction::SetColumn(self.row.column));
400 }
401 if self.row.isa != self.prev_row.isa {
402 self.instructions
403 .push(LineInstruction::SetIsa(self.row.isa));
404 }
405
406 let line_base = i64::from(self.line_encoding.line_base) as u64;
408 let line_range = u64::from(self.line_encoding.line_range);
409 let line_advance = self.row.line as i64 - self.prev_row.line as i64;
410 let op_advance = self.op_advance();
411
412 let special_base = u64::from(OPCODE_BASE);
414 debug_assert!(self.line_encoding.line_base <= 0);
416 debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0);
417 let special_default = special_base.wrapping_sub(line_base);
418 let mut special = special_default;
419 let mut use_special = false;
420
421 if line_advance != 0 {
422 let special_line = (line_advance as u64).wrapping_sub(line_base);
423 if special_line < line_range {
424 special = special_base + special_line;
425 use_special = true;
426 } else {
427 self.instructions
428 .push(LineInstruction::AdvanceLine(line_advance));
429 }
430 }
431
432 if op_advance != 0 {
433 let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 {
435 (op_advance, false)
436 } else {
437 let op_range = (255 - special_base) / line_range;
438 (op_advance - op_range, true)
439 };
440
441 let special_op = special_op_advance * line_range;
442 if special + special_op <= 255 {
443 special += special_op;
444 use_special = true;
445 if const_add_pc {
446 self.instructions.push(LineInstruction::ConstAddPc);
447 }
448 } else {
449 self.instructions
450 .push(LineInstruction::AdvancePc(op_advance));
451 }
452 }
453
454 if use_special && special != special_default {
455 debug_assert!(special >= special_base);
456 debug_assert!(special <= 255);
457 self.instructions
458 .push(LineInstruction::Special(special as u8));
459 } else {
460 self.instructions.push(LineInstruction::Copy);
461 }
462
463 self.prev_row = self.row;
464 }
465
466 fn op_advance(&self) -> u64 {
467 debug_assert!(self.row.address_offset >= self.prev_row.address_offset);
468 let mut address_advance = self.row.address_offset - self.prev_row.address_offset;
469 if self.line_encoding.minimum_instruction_length != 1 {
470 debug_assert_eq!(
471 self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length),
472 0
473 );
474 address_advance /= u64::from(self.line_encoding.minimum_instruction_length);
475 }
476 address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction)
477 + self.row.op_index
478 - self.prev_row.op_index
479 }
480
481 #[inline]
485 pub fn is_empty(&self) -> bool {
486 self.instructions.is_empty()
487 }
488
489 pub fn write<W: Writer>(
495 &self,
496 w: &mut DebugLine<W>,
497 encoding: Encoding,
498 debug_line_str_offsets: &DebugLineStrOffsets,
499 debug_str_offsets: &DebugStrOffsets,
500 ) -> Result<DebugLineOffset> {
501 assert!(!self.is_none());
502
503 if encoding.version < self.version()
504 || encoding.format != self.format()
505 || encoding.address_size != self.address_size()
506 {
507 return Err(Error::IncompatibleLineProgramEncoding);
508 }
509
510 let offset = w.offset();
511
512 let length_offset = w.write_initial_length(self.format())?;
513 let length_base = w.len();
514
515 if self.version() < 2 || self.version() > 5 {
516 return Err(Error::UnsupportedVersion(self.version()));
517 }
518 w.write_u16(self.version())?;
519
520 if self.version() >= 5 {
521 w.write_u8(self.address_size())?;
522 w.write_u8(0)?;
524 }
525
526 let header_length_offset = w.len();
527 w.write_udata(0, self.format().word_size())?;
528 let header_length_base = w.len();
529
530 w.write_u8(self.line_encoding.minimum_instruction_length)?;
531 if self.version() >= 4 {
532 w.write_u8(self.line_encoding.maximum_operations_per_instruction)?;
533 } else if self.line_encoding.maximum_operations_per_instruction != 1 {
534 return Err(Error::NeedVersion(4));
535 };
536 w.write_u8(if self.line_encoding.default_is_stmt {
537 1
538 } else {
539 0
540 })?;
541 w.write_u8(self.line_encoding.line_base as u8)?;
542 w.write_u8(self.line_encoding.line_range)?;
543 w.write_u8(OPCODE_BASE)?;
544 w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?;
545
546 if self.version() <= 4 {
547 for dir in self.directories.iter().skip(1) {
549 dir.write(
550 w,
551 constants::DW_FORM_string,
552 self.encoding,
553 debug_line_str_offsets,
554 debug_str_offsets,
555 )?;
556 }
557 w.write_u8(0)?;
558
559 for ((file, dir), info) in self.files.iter() {
560 file.write(
561 w,
562 constants::DW_FORM_string,
563 self.encoding,
564 debug_line_str_offsets,
565 debug_str_offsets,
566 )?;
567 w.write_uleb128(dir.0 as u64)?;
568 w.write_uleb128(info.timestamp)?;
569 w.write_uleb128(info.size)?;
570 }
571 w.write_u8(0)?;
572 } else {
573 w.write_u8(1)?;
575 w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
576 let dir_form = self.directories.get_index(0).unwrap().form();
577 w.write_uleb128(dir_form.0.into())?;
578
579 w.write_uleb128(self.directories.len() as u64)?;
581 for dir in self.directories.iter() {
582 dir.write(
583 w,
584 dir_form,
585 self.encoding,
586 debug_line_str_offsets,
587 debug_str_offsets,
588 )?;
589 }
590
591 let count = 2
593 + if self.file_has_timestamp { 1 } else { 0 }
594 + if self.file_has_size { 1 } else { 0 }
595 + if self.file_has_md5 { 1 } else { 0 }
596 + if self.file_has_source { 1 } else { 0 };
597 w.write_u8(count)?;
598 w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
599 let file_form = (self.files.get_index(0).unwrap().0).0.form();
600 w.write_uleb128(file_form.0.into())?;
601 w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?;
602 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
603 if self.file_has_timestamp {
604 w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?;
605 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
606 }
607 if self.file_has_size {
608 w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?;
609 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
610 }
611 if self.file_has_md5 {
612 w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?;
613 w.write_uleb128(constants::DW_FORM_data16.0.into())?;
614 }
615 if self.file_has_source {
616 w.write_uleb128(u64::from(constants::DW_LNCT_LLVM_source.0))?;
617 w.write_uleb128(constants::DW_FORM_string.0.into())?;
618 }
619
620 w.write_uleb128(self.files.len() as u64)?;
622 let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| {
623 file.write(
624 w,
625 file_form,
626 self.encoding,
627 debug_line_str_offsets,
628 debug_str_offsets,
629 )?;
630 w.write_uleb128(dir.0 as u64)?;
631 if self.file_has_timestamp {
632 w.write_uleb128(info.timestamp)?;
633 }
634 if self.file_has_size {
635 w.write_uleb128(info.size)?;
636 }
637 if self.file_has_md5 {
638 w.write(&info.md5)?;
639 }
640 if self.file_has_source {
641 let empty_str = LineString::String(Vec::new());
645 let source = info.source.as_ref().unwrap_or(&empty_str);
646 source.write(
647 w,
648 constants::DW_FORM_string,
649 self.encoding,
650 debug_line_str_offsets,
651 debug_str_offsets,
652 )?;
653 }
654 Ok(())
655 };
656 for ((file, dir), info) in self.files.iter() {
657 write_file(file, *dir, info)?;
658 }
659 }
660
661 let header_length = (w.len() - header_length_base) as u64;
662 w.write_udata_at(
663 header_length_offset,
664 header_length,
665 self.format().word_size(),
666 )?;
667
668 for instruction in &self.instructions {
669 instruction.write(w, self.encoding)?;
670 }
671
672 let length = (w.len() - length_base) as u64;
673 w.write_initial_length_at(length_offset, length, self.format())?;
674
675 Ok(offset)
676 }
677}
678
679#[derive(Debug, Clone, Copy)]
681pub struct LineRow {
682 pub address_offset: u64,
684 pub op_index: u64,
689
690 pub file: FileId,
692 pub line: u64,
696 pub column: u64,
700 pub discriminator: u64,
703
704 pub is_statement: bool,
706 pub basic_block: bool,
708 pub prologue_end: bool,
711 pub epilogue_begin: bool,
714
715 pub isa: u64,
719}
720
721impl LineRow {
722 fn initial_state(encoding: Encoding, line_encoding: LineEncoding) -> Self {
724 LineRow {
725 address_offset: 0,
726 op_index: 0,
727
728 file: FileId::initial_state(encoding.version),
729 line: 1,
730 column: 0,
731 discriminator: 0,
732
733 is_statement: line_encoding.default_is_stmt,
734 basic_block: false,
735 prologue_end: false,
736 epilogue_begin: false,
737
738 isa: 0,
739 }
740 }
741}
742
743#[derive(Debug, Clone, Copy, PartialEq, Eq)]
745enum LineInstruction {
746 Special(u8),
748
749 Copy,
751 AdvancePc(u64),
752 AdvanceLine(i64),
753 SetFile(FileId),
754 SetColumn(u64),
755 NegateStatement,
756 SetBasicBlock,
757 ConstAddPc,
758 SetPrologueEnd,
760 SetEpilogueBegin,
761 SetIsa(u64),
762
763 EndSequence,
765 SetAddress(Address),
767 SetDiscriminator(u64),
769}
770
771impl LineInstruction {
772 fn write<W: Writer>(self, w: &mut DebugLine<W>, encoding: Encoding) -> Result<()> {
774 use self::LineInstruction::*;
775 match self {
776 Special(val) => w.write_u8(val)?,
777 Copy => w.write_u8(constants::DW_LNS_copy.0)?,
778 AdvancePc(val) => {
779 w.write_u8(constants::DW_LNS_advance_pc.0)?;
780 w.write_uleb128(val)?;
781 }
782 AdvanceLine(val) => {
783 w.write_u8(constants::DW_LNS_advance_line.0)?;
784 w.write_sleb128(val)?;
785 }
786 SetFile(val) => {
787 w.write_u8(constants::DW_LNS_set_file.0)?;
788 w.write_uleb128(val.raw(encoding.version))?;
789 }
790 SetColumn(val) => {
791 w.write_u8(constants::DW_LNS_set_column.0)?;
792 w.write_uleb128(val)?;
793 }
794 NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?,
795 SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?,
796 ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?,
797 SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?,
798 SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?,
799 SetIsa(val) => {
800 w.write_u8(constants::DW_LNS_set_isa.0)?;
801 w.write_uleb128(val)?;
802 }
803 EndSequence => {
804 w.write_u8(0)?;
805 w.write_uleb128(1)?;
806 w.write_u8(constants::DW_LNE_end_sequence.0)?;
807 }
808 SetAddress(address) => {
809 w.write_u8(0)?;
810 w.write_uleb128(1 + u64::from(encoding.address_size))?;
811 w.write_u8(constants::DW_LNE_set_address.0)?;
812 w.write_address(address, encoding.address_size)?;
813 }
814 SetDiscriminator(val) => {
815 let mut bytes = [0u8; 10];
816 let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap();
818 w.write_u8(0)?;
819 w.write_uleb128(1 + len as u64)?;
820 w.write_u8(constants::DW_LNE_set_discriminator.0)?;
821 w.write(&bytes[..len])?;
822 }
823 }
824 Ok(())
825 }
826}
827
828#[derive(Debug, Clone, PartialEq, Eq, Hash)]
830pub enum LineString {
831 String(Vec<u8>),
834
835 StringRef(StringId),
837
838 LineStringRef(LineStringId),
840}
841
842impl LineString {
843 pub fn new<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self
845 where
846 T: Into<Vec<u8>>,
847 {
848 let val = val.into();
849 if encoding.version <= 4 {
850 LineString::String(val)
851 } else {
852 LineString::LineStringRef(line_strings.add(val))
853 }
854 }
855
856 fn form(&self) -> constants::DwForm {
857 match *self {
858 LineString::String(..) => constants::DW_FORM_string,
859 LineString::StringRef(..) => constants::DW_FORM_strp,
860 LineString::LineStringRef(..) => constants::DW_FORM_line_strp,
861 }
862 }
863
864 fn write<W: Writer>(
865 &self,
866 w: &mut DebugLine<W>,
867 form: constants::DwForm,
868 encoding: Encoding,
869 debug_line_str_offsets: &DebugLineStrOffsets,
870 debug_str_offsets: &DebugStrOffsets,
871 ) -> Result<()> {
872 if form != self.form() {
873 return Err(Error::LineStringFormMismatch);
874 }
875
876 match *self {
877 LineString::String(ref val) => {
878 if encoding.version <= 4 {
879 debug_assert!(!val.is_empty());
880 }
881 w.write(val)?;
882 w.write_u8(0)?;
883 }
884 LineString::StringRef(val) => {
885 if encoding.version < 5 {
886 return Err(Error::NeedVersion(5));
887 }
888 w.write_offset(
889 debug_str_offsets.get(val).0,
890 SectionId::DebugStr,
891 encoding.format.word_size(),
892 )?;
893 }
894 LineString::LineStringRef(val) => {
895 if encoding.version < 5 {
896 return Err(Error::NeedVersion(5));
897 }
898 w.write_offset(
899 debug_line_str_offsets.get(val).0,
900 SectionId::DebugLineStr,
901 encoding.format.word_size(),
902 )?;
903 }
904 }
905 Ok(())
906 }
907}
908
909#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
913pub struct DirectoryId(usize);
914
915mod id {
917 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
922 pub struct FileId(usize);
923
924 impl FileId {
925 pub(crate) fn new(index: usize) -> Self {
927 FileId(index)
928 }
929
930 pub(super) fn index(self) -> usize {
932 self.0
933 }
934
935 pub(super) fn initial_state(version: u16) -> Self {
937 if version == 5 {
938 FileId(1)
941 } else {
942 FileId(0)
947 }
948 }
949
950 pub(crate) fn raw(self, version: u16) -> u64 {
954 if version <= 4 {
955 self.0 as u64 + 1
956 } else {
957 self.0 as u64
958 }
959 }
960 }
961}
962pub use self::id::*;
963
964#[derive(Debug, Default, Clone, PartialEq, Eq)]
966pub struct FileInfo {
967 pub timestamp: u64,
970
971 pub size: u64,
973
974 pub md5: [u8; 16],
978
979 pub source: Option<LineString>,
987}
988
989define_section!(
990 DebugLine,
991 DebugLineOffset,
992 "A writable `.debug_line` section."
993);
994
995#[cfg(feature = "read")]
996mod convert {
997 use super::*;
998 use crate::read::{self, Reader};
999 use crate::write::{self, ConvertError, ConvertResult};
1000
1001 impl LineProgram {
1002 pub fn from<R: Reader<Offset = usize>>(
1006 mut from_program: read::IncompleteLineProgram<R>,
1007 dwarf: &read::Dwarf<R>,
1008 line_strings: &mut write::LineStringTable,
1009 strings: &mut write::StringTable,
1010 convert_address: &dyn Fn(u64) -> Option<Address>,
1011 ) -> ConvertResult<(LineProgram, Vec<FileId>)> {
1012 let mut dirs = Vec::new();
1014 let mut files = Vec::new();
1015
1016 let mut program = {
1017 let from_header = from_program.header();
1018 let encoding = from_header.encoding();
1019
1020 let comp_dir = match from_header.directory(0) {
1021 Some(comp_dir) => LineString::from(comp_dir, dwarf, line_strings, strings)?,
1022 None => LineString::new(&[][..], encoding, line_strings),
1023 };
1024
1025 let comp_name = match from_header.file(0) {
1026 Some(comp_file) => {
1027 if comp_file.directory_index() != 0 {
1028 return Err(ConvertError::InvalidDirectoryIndex);
1029 }
1030 LineString::from(comp_file.path_name(), dwarf, line_strings, strings)?
1031 }
1032 None => LineString::new(&[][..], encoding, line_strings),
1033 };
1034
1035 if from_header.line_base() > 0 {
1036 return Err(ConvertError::InvalidLineBase);
1037 }
1038 let mut program = LineProgram::new(
1039 encoding,
1040 from_header.line_encoding(),
1041 comp_dir,
1042 comp_name,
1043 None, );
1045
1046 if from_header.version() <= 4 {
1047 dirs.push(DirectoryId(0));
1049 files.push(FileId::new(0));
1052 }
1053
1054 for from_dir in from_header.include_directories() {
1055 let from_dir =
1056 LineString::from(from_dir.clone(), dwarf, line_strings, strings)?;
1057 dirs.push(program.add_directory(from_dir));
1058 }
1059
1060 program.file_has_timestamp = from_header.file_has_timestamp();
1061 program.file_has_size = from_header.file_has_size();
1062 program.file_has_md5 = from_header.file_has_md5();
1063 program.file_has_source = from_header.file_has_source();
1064 for from_file in from_header.file_names().iter() {
1065 let from_name =
1066 LineString::from(from_file.path_name(), dwarf, line_strings, strings)?;
1067 let from_dir = from_file.directory_index();
1068 if from_dir >= dirs.len() as u64 {
1069 return Err(ConvertError::InvalidDirectoryIndex);
1070 }
1071 let from_dir = dirs[from_dir as usize];
1072 let from_info = Some(FileInfo {
1073 timestamp: from_file.timestamp(),
1074 size: from_file.size(),
1075 md5: *from_file.md5(),
1076 source: match from_file.source() {
1077 Some(source) => {
1078 Some(LineString::from(source, dwarf, line_strings, strings)?)
1079 }
1080 None => None,
1081 },
1082 });
1083 files.push(program.add_file(from_name, from_dir, from_info));
1084 }
1085
1086 program
1087 };
1088
1089 let mut from_row = read::LineRow::new(from_program.header());
1092 let mut instructions = from_program.header().instructions();
1093 let mut address = None;
1094 while let Some(instruction) = instructions.next_instruction(from_program.header())? {
1095 match instruction {
1096 read::LineInstruction::SetAddress(val) => {
1097 if program.in_sequence() {
1098 return Err(ConvertError::UnsupportedLineInstruction);
1099 }
1100 match convert_address(val) {
1101 Some(val) => address = Some(val),
1102 None => return Err(ConvertError::InvalidAddress),
1103 }
1104 from_row
1105 .execute(read::LineInstruction::SetAddress(0), &mut from_program)?;
1106 }
1107 read::LineInstruction::DefineFile(_) => {
1108 return Err(ConvertError::UnsupportedLineInstruction);
1109 }
1110 _ => {
1111 if from_row.execute(instruction, &mut from_program)? {
1112 if !program.in_sequence() {
1113 program.begin_sequence(address);
1114 address = None;
1115 }
1116 if from_row.end_sequence() {
1117 program.end_sequence(from_row.address());
1118 } else {
1119 program.row().address_offset = from_row.address();
1120 program.row().op_index = from_row.op_index();
1121 program.row().file = {
1122 let file = from_row.file_index();
1123 if file >= files.len() as u64 {
1124 return Err(ConvertError::InvalidFileIndex);
1125 }
1126 if file == 0 && program.version() <= 4 {
1127 return Err(ConvertError::InvalidFileIndex);
1128 }
1129 files[file as usize]
1130 };
1131 program.row().line = match from_row.line() {
1132 Some(line) => line.get(),
1133 None => 0,
1134 };
1135 program.row().column = match from_row.column() {
1136 read::ColumnType::LeftEdge => 0,
1137 read::ColumnType::Column(val) => val.get(),
1138 };
1139 program.row().discriminator = from_row.discriminator();
1140 program.row().is_statement = from_row.is_stmt();
1141 program.row().basic_block = from_row.basic_block();
1142 program.row().prologue_end = from_row.prologue_end();
1143 program.row().epilogue_begin = from_row.epilogue_begin();
1144 program.row().isa = from_row.isa();
1145 program.generate_row();
1146 }
1147 from_row.reset(from_program.header());
1148 }
1149 }
1150 };
1151 }
1152 Ok((program, files))
1153 }
1154 }
1155
1156 impl LineString {
1157 fn from<R: Reader<Offset = usize>>(
1158 from_attr: read::AttributeValue<R>,
1159 dwarf: &read::Dwarf<R>,
1160 line_strings: &mut write::LineStringTable,
1161 strings: &mut write::StringTable,
1162 ) -> ConvertResult<LineString> {
1163 Ok(match from_attr {
1164 read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()),
1165 read::AttributeValue::DebugStrRef(offset) => {
1166 let r = dwarf.debug_str.get_str(offset)?;
1167 let id = strings.add(r.to_slice()?);
1168 LineString::StringRef(id)
1169 }
1170 read::AttributeValue::DebugLineStrRef(offset) => {
1171 let r = dwarf.debug_line_str.get_str(offset)?;
1172 let id = line_strings.add(r.to_slice()?);
1173 LineString::LineStringRef(id)
1174 }
1175 _ => return Err(ConvertError::UnsupportedLineStringForm),
1176 })
1177 }
1178 }
1179}
1180
1181#[cfg(test)]
1182#[cfg(feature = "read")]
1183mod tests {
1184 use super::*;
1185 use crate::read;
1186 use crate::write::{AttributeValue, Dwarf, EndianVec, Sections, Unit};
1187 use crate::LittleEndian;
1188
1189 #[test]
1190 fn test_line_program() {
1191 let dir1 = LineString::String(b"dir1".to_vec());
1192 let file1 = LineString::String(b"file1".to_vec());
1193 let dir2 = LineString::String(b"dir2".to_vec());
1194 let file2 = LineString::String(b"file2".to_vec());
1195
1196 let mut dwarf = Dwarf::new();
1197
1198 for &version in &[2, 3, 4, 5] {
1199 for &address_size in &[4, 8] {
1200 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1201 let encoding = Encoding {
1202 format,
1203 version,
1204 address_size,
1205 };
1206 let mut program = LineProgram::new(
1207 encoding,
1208 LineEncoding::default(),
1209 dir1.clone(),
1210 file1.clone(),
1211 None,
1212 );
1213
1214 assert_eq!(&dir1, program.get_directory(program.default_directory()));
1215 program.file_has_timestamp = true;
1216 program.file_has_size = true;
1217 if encoding.version >= 5 {
1218 program.file_has_md5 = true;
1219 }
1220
1221 if encoding.version >= 5 {
1225 program.file_has_source = true;
1226 }
1227
1228 let dir_id = program.add_directory(dir2.clone());
1229 assert_eq!(&dir2, program.get_directory(dir_id));
1230 assert_eq!(dir_id, program.add_directory(dir2.clone()));
1231
1232 let file_info = FileInfo {
1233 timestamp: 1,
1234 size: 2,
1235 md5: if encoding.version >= 5 {
1236 [3; 16]
1237 } else {
1238 [0; 16]
1239 },
1240 source: (encoding.version >= 5)
1241 .then(|| LineString::String(b"the source code\n".to_vec())),
1242 };
1243 let file_id = program.add_file(file2.clone(), dir_id, Some(file_info.clone()));
1244 assert_eq!((&file2, dir_id), program.get_file(file_id));
1245 assert_eq!(file_info, *program.get_file_info(file_id));
1246
1247 program.get_file_info_mut(file_id).size = 3;
1248 assert_ne!(file_info, *program.get_file_info(file_id));
1249 assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None));
1250 assert_ne!(file_info, *program.get_file_info(file_id));
1251 assert_eq!(
1252 file_id,
1253 program.add_file(file2.clone(), dir_id, Some(file_info.clone()))
1254 );
1255 assert_eq!(file_info, *program.get_file_info(file_id));
1256
1257 let mut unit = Unit::new(encoding, program);
1258 let root = unit.get_mut(unit.root());
1259 root.set(
1260 constants::DW_AT_comp_dir,
1261 AttributeValue::String(b"dir1".to_vec()),
1262 );
1263 root.set(
1264 constants::DW_AT_name,
1265 AttributeValue::String(b"file1".to_vec()),
1266 );
1267 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1268 root.set(
1269 constants::DW_AT_decl_file,
1270 AttributeValue::FileIndex(Some(file_id)),
1271 );
1272
1273 let mut dwarf = Dwarf::new();
1274 dwarf.units.add(unit);
1275 }
1276 }
1277 }
1278
1279 let mut sections = Sections::new(EndianVec::new(LittleEndian));
1280 dwarf.write(&mut sections).unwrap();
1281 let read_dwarf = sections.read(LittleEndian);
1282 let convert_dwarf =
1283 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address))).unwrap();
1284
1285 let mut convert_units = convert_dwarf.units.iter();
1286 for (_, unit) in dwarf.units.iter() {
1287 let program = &unit.line_program;
1288 let root = unit.get(unit.root());
1289 let Some(AttributeValue::FileIndex(Some(file_id))) =
1290 root.get(constants::DW_AT_decl_file)
1291 else {
1292 panic!("missing DW_AT_decl_file");
1293 };
1294
1295 let (_, convert_unit) = convert_units.next().unwrap();
1296 let convert_program = &convert_unit.line_program;
1297 let convert_root = convert_unit.get(convert_unit.root());
1298 let Some(AttributeValue::FileIndex(Some(convert_file_id))) =
1299 convert_root.get(constants::DW_AT_decl_file)
1300 else {
1301 panic!("missing DW_AT_decl_file");
1302 };
1303
1304 assert_eq!(convert_program.version(), program.version());
1305 assert_eq!(convert_program.address_size(), program.address_size());
1306 assert_eq!(convert_program.format(), program.format());
1307
1308 let (file, dir) = program.get_file(*file_id);
1309 let (convert_file, convert_dir) = convert_program.get_file(*convert_file_id);
1310 assert_eq!(file, convert_file);
1311 assert_eq!(
1312 program.get_directory(dir),
1313 convert_program.get_directory(convert_dir)
1314 );
1315 assert_eq!(
1316 program.get_file_info(*file_id),
1317 convert_program.get_file_info(*convert_file_id)
1318 );
1319 }
1320 }
1321
1322 #[test]
1323 fn test_line_row() {
1324 let dir1 = &b"dir1"[..];
1325 let file1 = &b"file1"[..];
1326 let file2 = &b"file2"[..];
1327
1328 for &version in &[2, 3, 4, 5] {
1329 for &address_size in &[4, 8] {
1330 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1331 let encoding = Encoding {
1332 format,
1333 version,
1334 address_size,
1335 };
1336 let line_base = -5;
1337 let line_range = 14;
1338 let neg_line_base = (-line_base) as u8;
1339 let mut program = LineProgram::new(
1340 encoding,
1341 LineEncoding {
1342 line_base,
1343 line_range,
1344 ..Default::default()
1345 },
1346 LineString::String(dir1.to_vec()),
1347 LineString::String(file1.to_vec()),
1348 None,
1349 );
1350 let dir_id = program.default_directory();
1351 let file1_id =
1352 program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1353 let file2_id =
1354 program.add_file(LineString::String(file2.to_vec()), dir_id, None);
1355
1356 {
1358 let mut program = program.clone();
1359 let address = Address::Constant(0x12);
1360 program.begin_sequence(Some(address));
1361 assert_eq!(
1362 program.instructions,
1363 vec![LineInstruction::SetAddress(address)]
1364 );
1365 }
1366
1367 {
1368 let mut program = program.clone();
1369 program.begin_sequence(None);
1370 assert_eq!(program.instructions, Vec::new());
1371 }
1372
1373 {
1374 let mut program = program.clone();
1375 program.begin_sequence(None);
1376 program.end_sequence(0x1234);
1377 assert_eq!(
1378 program.instructions,
1379 vec![
1380 LineInstruction::AdvancePc(0x1234),
1381 LineInstruction::EndSequence
1382 ]
1383 );
1384 }
1385
1386 program.begin_sequence(None);
1388 program.row.line = 0x1000;
1389 program.generate_row();
1390 let base_row = program.row;
1391 let base_instructions = program.instructions.clone();
1392
1393 let mut tests = Vec::new();
1395
1396 let row = base_row;
1397 tests.push((row, vec![LineInstruction::Copy]));
1398
1399 let mut row = base_row;
1400 row.line -= u64::from(neg_line_base);
1401 tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)]));
1402
1403 let mut row = base_row;
1404 row.line += u64::from(line_range) - 1;
1405 row.line -= u64::from(neg_line_base);
1406 tests.push((
1407 row,
1408 vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)],
1409 ));
1410
1411 let mut row = base_row;
1412 row.line += u64::from(line_range);
1413 row.line -= u64::from(neg_line_base);
1414 tests.push((
1415 row,
1416 vec![
1417 LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)),
1418 LineInstruction::Copy,
1419 ],
1420 ));
1421
1422 let mut row = base_row;
1423 row.address_offset = 1;
1424 row.line -= u64::from(neg_line_base);
1425 tests.push((
1426 row,
1427 vec![LineInstruction::Special(OPCODE_BASE + line_range)],
1428 ));
1429
1430 let op_range = (255 - OPCODE_BASE) / line_range;
1431 let mut row = base_row;
1432 row.address_offset = u64::from(op_range);
1433 row.line -= u64::from(neg_line_base);
1434 tests.push((
1435 row,
1436 vec![LineInstruction::Special(
1437 OPCODE_BASE + op_range * line_range,
1438 )],
1439 ));
1440
1441 let mut row = base_row;
1442 row.address_offset = u64::from(op_range);
1443 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
1444 row.line -= u64::from(neg_line_base);
1445 tests.push((row, vec![LineInstruction::Special(255)]));
1446
1447 let mut row = base_row;
1448 row.address_offset = u64::from(op_range);
1449 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
1450 row.line -= u64::from(neg_line_base);
1451 tests.push((
1452 row,
1453 vec![LineInstruction::ConstAddPc, LineInstruction::Copy],
1454 ));
1455
1456 let mut row = base_row;
1457 row.address_offset = u64::from(op_range);
1458 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
1459 row.line -= u64::from(neg_line_base);
1460 tests.push((
1461 row,
1462 vec![
1463 LineInstruction::ConstAddPc,
1464 LineInstruction::Special(OPCODE_BASE + 6),
1465 ],
1466 ));
1467
1468 let mut row = base_row;
1469 row.address_offset = u64::from(op_range) * 2;
1470 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
1471 row.line -= u64::from(neg_line_base);
1472 tests.push((
1473 row,
1474 vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)],
1475 ));
1476
1477 let mut row = base_row;
1478 row.address_offset = u64::from(op_range) * 2;
1479 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
1480 row.line -= u64::from(neg_line_base);
1481 tests.push((
1482 row,
1483 vec![
1484 LineInstruction::AdvancePc(row.address_offset),
1485 LineInstruction::Copy,
1486 ],
1487 ));
1488
1489 let mut row = base_row;
1490 row.address_offset = u64::from(op_range) * 2;
1491 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
1492 row.line -= u64::from(neg_line_base);
1493 tests.push((
1494 row,
1495 vec![
1496 LineInstruction::AdvancePc(row.address_offset),
1497 LineInstruction::Special(OPCODE_BASE + 6),
1498 ],
1499 ));
1500
1501 let mut row = base_row;
1502 row.address_offset = 0x1234;
1503 tests.push((
1504 row,
1505 vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy],
1506 ));
1507
1508 let mut row = base_row;
1509 row.line += 0x1234;
1510 tests.push((
1511 row,
1512 vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy],
1513 ));
1514
1515 let mut row = base_row;
1516 row.file = file1_id;
1517 if version == 5 {
1518 tests.push((
1521 row,
1522 vec![LineInstruction::SetFile(file1_id), LineInstruction::Copy],
1523 ));
1524 } else {
1525 tests.push((row, vec![LineInstruction::Copy]));
1527 }
1528
1529 let mut row = base_row;
1530 row.file = file2_id;
1531 if version == 5 {
1532 tests.push((row, vec![LineInstruction::Copy]));
1533 } else {
1534 tests.push((
1535 row,
1536 vec![LineInstruction::SetFile(file2_id), LineInstruction::Copy],
1537 ));
1538 }
1539
1540 let mut row = base_row;
1541 row.column = 0x1234;
1542 tests.push((
1543 row,
1544 vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy],
1545 ));
1546
1547 let mut row = base_row;
1548 row.discriminator = 0x1234;
1549 tests.push((
1550 row,
1551 vec![
1552 LineInstruction::SetDiscriminator(0x1234),
1553 LineInstruction::Copy,
1554 ],
1555 ));
1556
1557 let mut row = base_row;
1558 row.is_statement = !row.is_statement;
1559 tests.push((
1560 row,
1561 vec![LineInstruction::NegateStatement, LineInstruction::Copy],
1562 ));
1563
1564 let mut row = base_row;
1565 row.basic_block = true;
1566 tests.push((
1567 row,
1568 vec![LineInstruction::SetBasicBlock, LineInstruction::Copy],
1569 ));
1570
1571 let mut row = base_row;
1572 row.prologue_end = true;
1573 tests.push((
1574 row,
1575 vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy],
1576 ));
1577
1578 let mut row = base_row;
1579 row.epilogue_begin = true;
1580 tests.push((
1581 row,
1582 vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy],
1583 ));
1584
1585 let mut row = base_row;
1586 row.isa = 0x1234;
1587 tests.push((
1588 row,
1589 vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy],
1590 ));
1591
1592 for test in tests {
1593 let mut program = program.clone();
1595 program.row = test.0;
1596 program.generate_row();
1597 assert_eq!(
1598 &program.instructions[base_instructions.len()..],
1599 &test.1[..]
1600 );
1601
1602 let mut unit = Unit::new(encoding, program);
1604 let root = unit.get_mut(unit.root());
1605 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1606
1607 let mut dwarf = Dwarf::new();
1608 dwarf.units.add(unit);
1609
1610 let mut sections = Sections::new(EndianVec::new(LittleEndian));
1611 dwarf.write(&mut sections).unwrap();
1612 let read_dwarf = sections.read(LittleEndian);
1613
1614 let convert_dwarf =
1615 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
1616 .unwrap();
1617 let convert_unit = convert_dwarf.units.iter().next().unwrap().1;
1618 let convert_program = &convert_unit.line_program;
1619
1620 assert_eq!(
1621 &convert_program.instructions[base_instructions.len()..],
1622 &test.1[..]
1623 );
1624 }
1625 }
1626 }
1627 }
1628 }
1629
1630 #[test]
1631 fn test_line_instruction() {
1632 let dir1 = &b"dir1"[..];
1633 let file1 = &b"file1"[..];
1634
1635 for &version in &[2, 3, 4, 5] {
1636 for &address_size in &[4, 8] {
1637 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1638 let encoding = Encoding {
1639 format,
1640 version,
1641 address_size,
1642 };
1643 let mut program = LineProgram::new(
1644 encoding,
1645 LineEncoding::default(),
1646 LineString::String(dir1.to_vec()),
1647 LineString::String(file1.to_vec()),
1648 None,
1649 );
1650 let dir_id = program.default_directory();
1651 let file_id =
1652 program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1653
1654 for (inst, expect_inst) in &[
1655 (
1656 LineInstruction::Special(OPCODE_BASE),
1657 read::LineInstruction::Special(OPCODE_BASE),
1658 ),
1659 (
1660 LineInstruction::Special(255),
1661 read::LineInstruction::Special(255),
1662 ),
1663 (LineInstruction::Copy, read::LineInstruction::Copy),
1664 (
1665 LineInstruction::AdvancePc(0x12),
1666 read::LineInstruction::AdvancePc(0x12),
1667 ),
1668 (
1669 LineInstruction::AdvanceLine(0x12),
1670 read::LineInstruction::AdvanceLine(0x12),
1671 ),
1672 (
1673 LineInstruction::SetFile(file_id),
1674 read::LineInstruction::SetFile(file_id.raw(encoding.version)),
1675 ),
1676 (
1677 LineInstruction::SetColumn(0x12),
1678 read::LineInstruction::SetColumn(0x12),
1679 ),
1680 (
1681 LineInstruction::NegateStatement,
1682 read::LineInstruction::NegateStatement,
1683 ),
1684 (
1685 LineInstruction::SetBasicBlock,
1686 read::LineInstruction::SetBasicBlock,
1687 ),
1688 (
1689 LineInstruction::ConstAddPc,
1690 read::LineInstruction::ConstAddPc,
1691 ),
1692 (
1693 LineInstruction::SetPrologueEnd,
1694 read::LineInstruction::SetPrologueEnd,
1695 ),
1696 (
1697 LineInstruction::SetEpilogueBegin,
1698 read::LineInstruction::SetEpilogueBegin,
1699 ),
1700 (
1701 LineInstruction::SetIsa(0x12),
1702 read::LineInstruction::SetIsa(0x12),
1703 ),
1704 (
1705 LineInstruction::EndSequence,
1706 read::LineInstruction::EndSequence,
1707 ),
1708 (
1709 LineInstruction::SetAddress(Address::Constant(0x12)),
1710 read::LineInstruction::SetAddress(0x12),
1711 ),
1712 (
1713 LineInstruction::SetDiscriminator(0x12),
1714 read::LineInstruction::SetDiscriminator(0x12),
1715 ),
1716 ][..]
1717 {
1718 let mut program = program.clone();
1719 program.instructions.push(*inst);
1720
1721 let mut unit = Unit::new(encoding, program);
1722 let root = unit.get_mut(unit.root());
1723 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1724
1725 let mut dwarf = Dwarf::new();
1726 dwarf.units.add(unit);
1727 let mut sections = Sections::new(EndianVec::new(LittleEndian));
1728 dwarf.write(&mut sections).unwrap();
1729
1730 let read_dwarf = sections.read(LittleEndian);
1731 let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
1732 let read_unit = read_dwarf.unit(read_unit_header).unwrap();
1733 let read_unit = read_unit.unit_ref(&read_dwarf);
1734 let read_header = read_unit.line_program.as_ref().unwrap().header();
1735 let mut read_insts = read_header.instructions();
1736 assert_eq!(
1737 *expect_inst,
1738 read_insts.next_instruction(read_header).unwrap().unwrap()
1739 );
1740 assert_eq!(None, read_insts.next_instruction(read_header).unwrap());
1741 }
1742 }
1743 }
1744 }
1745 }
1746
1747 #[test]
1749 fn test_advance() {
1750 let encoding = Encoding {
1751 format: Format::Dwarf32,
1752 version: 4,
1753 address_size: 8,
1754 };
1755
1756 let dir1 = &b"dir1"[..];
1757 let file1 = &b"file1"[..];
1758
1759 let addresses = 0..50;
1760 let lines = -10..25i64;
1761
1762 for minimum_instruction_length in [1, 4] {
1763 for maximum_operations_per_instruction in [1, 3] {
1764 for line_base in [-5, 0] {
1765 for line_range in [10, 20] {
1766 let line_encoding = LineEncoding {
1767 minimum_instruction_length,
1768 maximum_operations_per_instruction,
1769 line_base,
1770 line_range,
1771 default_is_stmt: true,
1772 };
1773 let mut program = LineProgram::new(
1774 encoding,
1775 line_encoding,
1776 LineString::String(dir1.to_vec()),
1777 LineString::String(file1.to_vec()),
1778 None,
1779 );
1780 for address_advance in addresses.clone() {
1781 program.begin_sequence(Some(Address::Constant(0x1000)));
1782 program.row().line = 0x10000;
1783 program.generate_row();
1784 for line_advance in lines.clone() {
1785 {
1786 let row = program.row();
1787 row.address_offset +=
1788 address_advance * u64::from(minimum_instruction_length);
1789 row.line = row.line.wrapping_add(line_advance as u64);
1790 }
1791 program.generate_row();
1792 }
1793 let address_offset = program.row().address_offset
1794 + u64::from(minimum_instruction_length);
1795 program.end_sequence(address_offset);
1796 }
1797
1798 let mut unit = Unit::new(encoding, program);
1799 let root = unit.get_mut(unit.root());
1800 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1801
1802 let mut dwarf = Dwarf::new();
1803 dwarf.units.add(unit);
1804 let mut sections = Sections::new(EndianVec::new(LittleEndian));
1805 dwarf.write(&mut sections).unwrap();
1806
1807 let read_dwarf = sections.read(LittleEndian);
1808 let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
1809 let read_unit = read_dwarf.unit(read_unit_header).unwrap();
1810 let read_unit = read_unit.unit_ref(&read_dwarf);
1811 let read_program = read_unit.line_program.clone().unwrap();
1812
1813 let mut rows = read_program.rows();
1814 for address_advance in addresses.clone() {
1815 let mut address;
1816 let mut line;
1817 {
1818 let row = rows.next_row().unwrap().unwrap().1;
1819 address = row.address();
1820 line = row.line().unwrap().get();
1821 }
1822 assert_eq!(address, 0x1000);
1823 assert_eq!(line, 0x10000);
1824 for line_advance in lines.clone() {
1825 let row = rows.next_row().unwrap().unwrap().1;
1826 assert_eq!(
1827 row.address() - address,
1828 address_advance * u64::from(minimum_instruction_length)
1829 );
1830 assert_eq!(
1831 (row.line().unwrap().get() as i64) - (line as i64),
1832 line_advance
1833 );
1834 address = row.address();
1835 line = row.line().unwrap().get();
1836 }
1837 let row = rows.next_row().unwrap().unwrap().1;
1838 assert!(row.end_sequence());
1839 }
1840 }
1841 }
1842 }
1843 }
1844 }
1845
1846 #[test]
1847 fn test_line_string() {
1848 let version = 5;
1849
1850 let file1 = "file1";
1851
1852 for &address_size in &[4, 8] {
1853 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1854 let encoding = Encoding {
1855 format,
1856 version,
1857 address_size,
1858 };
1859
1860 let files: &mut [&mut dyn Fn(&mut Dwarf) -> LineString] = &mut [
1861 &mut |_dwarf| LineString::String(file1.as_bytes().to_vec()),
1862 &mut |dwarf| LineString::StringRef(dwarf.strings.add(file1)),
1863 &mut |dwarf| LineString::LineStringRef(dwarf.line_strings.add(file1)),
1864 ];
1865
1866 for file in files {
1867 let mut dwarf = Dwarf::new();
1868 let file = file(&mut dwarf);
1869
1870 let mut program = LineProgram::new(
1871 encoding,
1872 LineEncoding::default(),
1873 LineString::String(b"dir".to_vec()),
1874 file.clone(),
1875 None,
1876 );
1877 program.begin_sequence(Some(Address::Constant(0x1000)));
1878 program.row().line = 0x10000;
1879 program.generate_row();
1880
1881 let mut unit = Unit::new(encoding, program);
1882 let root = unit.get_mut(unit.root());
1883 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1884 dwarf.units.add(unit);
1885
1886 let mut sections = Sections::new(EndianVec::new(LittleEndian));
1887 dwarf.write(&mut sections).unwrap();
1888
1889 let read_dwarf = sections.read(LittleEndian);
1890 let read_unit_header = read_dwarf.units().next().unwrap().unwrap();
1891 let read_unit = read_dwarf.unit(read_unit_header).unwrap();
1892 let read_unit = read_unit.unit_ref(&read_dwarf);
1893 let read_program = read_unit.line_program.clone().unwrap();
1894 let read_header = read_program.header();
1895 let read_file = read_header.file(0).unwrap();
1896 let read_path = read_unit.attr_string(read_file.path_name()).unwrap();
1897 assert_eq!(read_path.slice(), file1.as_bytes());
1898 }
1899 }
1900 }
1901 }
1902
1903 #[test]
1904 fn test_missing_comp_dir() {
1905 for &version in &[2, 3, 4, 5] {
1906 for &address_size in &[4, 8] {
1907 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1908 let encoding = Encoding {
1909 format,
1910 version,
1911 address_size,
1912 };
1913 let mut program = LineProgram::new(
1914 encoding,
1915 LineEncoding::default(),
1916 LineString::String(Vec::new()),
1917 LineString::String(Vec::new()),
1918 None,
1919 );
1920 let dir_id = program.default_directory();
1922 let file_id =
1923 program.add_file(LineString::String(b"file1".to_vec()), dir_id, None);
1924 program.begin_sequence(Some(Address::Constant(0x1000)));
1925 program.row().file = file_id;
1926 program.row().line = 0x10000;
1927 program.generate_row();
1928
1929 let mut unit = Unit::new(encoding, program);
1930 let root = unit.get_mut(unit.root());
1931 root.set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef);
1933
1934 let mut dwarf = Dwarf::new();
1935 dwarf.units.add(unit);
1936 let mut sections = Sections::new(EndianVec::new(LittleEndian));
1937 dwarf.write(&mut sections).unwrap();
1938 let read_dwarf = sections.read(LittleEndian);
1939 let _convert_dwarf =
1940 Dwarf::from(&read_dwarf, &|address| Some(Address::Constant(address)))
1941 .unwrap();
1942 }
1943 }
1944 }
1945 }
1946}