1use alloc::vec::Vec;
2use core::num::{NonZeroU64, Wrapping};
3
4use crate::common::{
5 DebugLineOffset, DebugLineStrOffset, DebugStrOffset, DebugStrOffsetsIndex, Encoding, Format,
6 LineEncoding, SectionId,
7};
8use crate::constants;
9use crate::endianity::Endianity;
10use crate::read::{
11 AttributeValue, EndianSlice, Error, Reader, ReaderAddress, ReaderOffset, Result, Section,
12};
13
14#[derive(Debug, Default, Clone, Copy)]
17pub struct DebugLine<R> {
18 debug_line_section: R,
19}
20
21impl<'input, Endian> DebugLine<EndianSlice<'input, Endian>>
22where
23 Endian: Endianity,
24{
25 pub fn new(debug_line_section: &'input [u8], endian: Endian) -> Self {
40 Self::from(EndianSlice::new(debug_line_section, endian))
41 }
42}
43
44impl<R: Reader> DebugLine<R> {
45 pub fn program(
70 &self,
71 offset: DebugLineOffset<R::Offset>,
72 address_size: u8,
73 comp_dir: Option<R>,
74 comp_name: Option<R>,
75 ) -> Result<IncompleteLineProgram<R>> {
76 let input = &mut self.debug_line_section.clone();
77 input.skip(offset.0)?;
78 let header = LineProgramHeader::parse(input, offset, address_size, comp_dir, comp_name)?;
79 let program = IncompleteLineProgram { header };
80 Ok(program)
81 }
82}
83
84impl<T> DebugLine<T> {
85 pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLine<R>
91 where
92 F: FnMut(&'a T) -> R,
93 {
94 borrow(&self.debug_line_section).into()
95 }
96}
97
98impl<R> Section<R> for DebugLine<R> {
99 fn id() -> SectionId {
100 SectionId::DebugLine
101 }
102
103 fn reader(&self) -> &R {
104 &self.debug_line_section
105 }
106}
107
108impl<R> From<R> for DebugLine<R> {
109 fn from(debug_line_section: R) -> Self {
110 DebugLine { debug_line_section }
111 }
112}
113
114#[deprecated(note = "LineNumberProgram has been renamed to LineProgram, use that instead.")]
116pub type LineNumberProgram<R, Offset> = dyn LineProgram<R, Offset>;
117
118pub trait LineProgram<R, Offset = <R as Reader>::Offset>
122where
123 R: Reader<Offset = Offset>,
124 Offset: ReaderOffset,
125{
126 fn header(&self) -> &LineProgramHeader<R, Offset>;
128 fn add_file(&mut self, file: FileEntry<R, Offset>);
130}
131
132impl<R, Offset> LineProgram<R, Offset> for IncompleteLineProgram<R, Offset>
133where
134 R: Reader<Offset = Offset>,
135 Offset: ReaderOffset,
136{
137 fn header(&self) -> &LineProgramHeader<R, Offset> {
138 &self.header
139 }
140 fn add_file(&mut self, file: FileEntry<R, Offset>) {
141 self.header.file_names.push(file);
142 }
143}
144
145impl<'program, R, Offset> LineProgram<R, Offset> for &'program CompleteLineProgram<R, Offset>
146where
147 R: Reader<Offset = Offset>,
148 Offset: ReaderOffset,
149{
150 fn header(&self) -> &LineProgramHeader<R, Offset> {
151 &self.header
152 }
153 fn add_file(&mut self, _: FileEntry<R, Offset>) {
154 }
156}
157
158#[deprecated(note = "StateMachine has been renamed to LineRows, use that instead.")]
160pub type StateMachine<R, Program, Offset> = LineRows<R, Program, Offset>;
161
162#[derive(Debug, Clone)]
168pub struct LineRows<R, Program, Offset = <R as Reader>::Offset>
169where
170 Program: LineProgram<R, Offset>,
171 R: Reader<Offset = Offset>,
172 Offset: ReaderOffset,
173{
174 program: Program,
175 row: LineRow,
176 instructions: LineInstructions<R>,
177}
178
179type OneShotLineRows<R, Offset = <R as Reader>::Offset> =
180 LineRows<R, IncompleteLineProgram<R, Offset>, Offset>;
181
182type ResumedLineRows<'program, R, Offset = <R as Reader>::Offset> =
183 LineRows<R, &'program CompleteLineProgram<R, Offset>, Offset>;
184
185impl<R, Program, Offset> LineRows<R, Program, Offset>
186where
187 Program: LineProgram<R, Offset>,
188 R: Reader<Offset = Offset>,
189 Offset: ReaderOffset,
190{
191 fn new(program: IncompleteLineProgram<R, Offset>) -> OneShotLineRows<R, Offset> {
192 let row = LineRow::new(program.header());
193 let instructions = LineInstructions {
194 input: program.header().program_buf.clone(),
195 };
196 LineRows {
197 program,
198 row,
199 instructions,
200 }
201 }
202
203 fn resume<'program>(
204 program: &'program CompleteLineProgram<R, Offset>,
205 sequence: &LineSequence<R>,
206 ) -> ResumedLineRows<'program, R, Offset> {
207 let row = LineRow::new(program.header());
208 let instructions = sequence.instructions.clone();
209 LineRows {
210 program,
211 row,
212 instructions,
213 }
214 }
215
216 #[inline]
219 pub fn header(&self) -> &LineProgramHeader<R, Offset> {
220 self.program.header()
221 }
222
223 pub fn next_row(&mut self) -> Result<Option<(&LineProgramHeader<R, Offset>, &LineRow)>> {
234 self.row.reset(self.program.header());
236
237 loop {
238 match self.instructions.next_instruction(self.program.header()) {
240 Err(err) => return Err(err),
241 Ok(None) => return Ok(None),
242 Ok(Some(instruction)) => {
243 if self.row.execute(instruction, &mut self.program)? {
244 if self.row.tombstone {
245 self.row.reset(self.program.header());
249 } else {
250 return Ok(Some((self.header(), &self.row)));
251 }
252 }
253 }
256 }
257 }
258 }
259}
260
261#[deprecated(note = "Opcode has been renamed to LineInstruction, use that instead.")]
263pub type Opcode<R> = LineInstruction<R, <R as Reader>::Offset>;
264
265#[derive(Clone, Copy, Debug, PartialEq, Eq)]
267pub enum LineInstruction<R, Offset = <R as Reader>::Offset>
268where
269 R: Reader<Offset = Offset>,
270 Offset: ReaderOffset,
271{
272 Special(u8),
296
297 Copy,
302
303 AdvancePc(u64),
307
308 AdvanceLine(i64),
311
312 SetFile(u64),
315
316 SetColumn(u64),
319
320 NegateStatement,
324
325 SetBasicBlock,
328
329 ConstAddPc,
341
342 FixedAddPc(u16),
348
349 SetPrologueEnd,
351
352 SetEpilogueBegin,
355
356 SetIsa(u64),
359
360 UnknownStandard0(constants::DwLns),
362
363 UnknownStandard1(constants::DwLns, u64),
365
366 UnknownStandardN(constants::DwLns, R),
368
369 EndSequence,
377
378 SetAddress(u64),
387
388 DefineFile(FileEntry<R, Offset>),
391
392 SetDiscriminator(u64),
396
397 UnknownExtended(constants::DwLne, R),
399}
400
401impl<R, Offset> LineInstruction<R, Offset>
402where
403 R: Reader<Offset = Offset>,
404 Offset: ReaderOffset,
405{
406 fn parse<'header>(
407 header: &'header LineProgramHeader<R>,
408 input: &mut R,
409 ) -> Result<LineInstruction<R>>
410 where
411 R: 'header,
412 {
413 let opcode = input.read_u8()?;
414 if opcode == 0 {
415 let length = input.read_uleb128().and_then(R::Offset::from_u64)?;
416 let mut instr_rest = input.split(length)?;
417 let opcode = instr_rest.read_u8()?;
418
419 match constants::DwLne(opcode) {
420 constants::DW_LNE_end_sequence => Ok(LineInstruction::EndSequence),
421
422 constants::DW_LNE_set_address => {
423 let address = instr_rest.read_address(header.address_size())?;
424 Ok(LineInstruction::SetAddress(address))
425 }
426
427 constants::DW_LNE_define_file => {
428 if header.version() <= 4 {
429 let path_name = instr_rest.read_null_terminated_slice()?;
430 let entry = FileEntry::parse(&mut instr_rest, path_name)?;
431 Ok(LineInstruction::DefineFile(entry))
432 } else {
433 Ok(LineInstruction::UnknownExtended(
434 constants::DW_LNE_define_file,
435 instr_rest,
436 ))
437 }
438 }
439
440 constants::DW_LNE_set_discriminator => {
441 let discriminator = instr_rest.read_uleb128()?;
442 Ok(LineInstruction::SetDiscriminator(discriminator))
443 }
444
445 otherwise => Ok(LineInstruction::UnknownExtended(otherwise, instr_rest)),
446 }
447 } else if opcode >= header.opcode_base {
448 Ok(LineInstruction::Special(opcode))
449 } else {
450 match constants::DwLns(opcode) {
451 constants::DW_LNS_copy => Ok(LineInstruction::Copy),
452
453 constants::DW_LNS_advance_pc => {
454 let advance = input.read_uleb128()?;
455 Ok(LineInstruction::AdvancePc(advance))
456 }
457
458 constants::DW_LNS_advance_line => {
459 let increment = input.read_sleb128()?;
460 Ok(LineInstruction::AdvanceLine(increment))
461 }
462
463 constants::DW_LNS_set_file => {
464 let file = input.read_uleb128()?;
465 Ok(LineInstruction::SetFile(file))
466 }
467
468 constants::DW_LNS_set_column => {
469 let column = input.read_uleb128()?;
470 Ok(LineInstruction::SetColumn(column))
471 }
472
473 constants::DW_LNS_negate_stmt => Ok(LineInstruction::NegateStatement),
474
475 constants::DW_LNS_set_basic_block => Ok(LineInstruction::SetBasicBlock),
476
477 constants::DW_LNS_const_add_pc => Ok(LineInstruction::ConstAddPc),
478
479 constants::DW_LNS_fixed_advance_pc => {
480 let advance = input.read_u16()?;
481 Ok(LineInstruction::FixedAddPc(advance))
482 }
483
484 constants::DW_LNS_set_prologue_end => Ok(LineInstruction::SetPrologueEnd),
485
486 constants::DW_LNS_set_epilogue_begin => Ok(LineInstruction::SetEpilogueBegin),
487
488 constants::DW_LNS_set_isa => {
489 let isa = input.read_uleb128()?;
490 Ok(LineInstruction::SetIsa(isa))
491 }
492
493 otherwise => {
494 let mut opcode_lengths = header.standard_opcode_lengths().clone();
495 opcode_lengths.skip(R::Offset::from_u8(opcode - 1))?;
496 let num_args = opcode_lengths.read_u8()? as usize;
497 match num_args {
498 0 => Ok(LineInstruction::UnknownStandard0(otherwise)),
499 1 => {
500 let arg = input.read_uleb128()?;
501 Ok(LineInstruction::UnknownStandard1(otherwise, arg))
502 }
503 _ => {
504 let mut args = input.clone();
505 for _ in 0..num_args {
506 input.read_uleb128()?;
507 }
508 let len = input.offset_from(&args);
509 args.truncate(len)?;
510 Ok(LineInstruction::UnknownStandardN(otherwise, args))
511 }
512 }
513 }
514 }
515 }
516 }
517}
518
519#[deprecated(note = "OpcodesIter has been renamed to LineInstructions, use that instead.")]
521pub type OpcodesIter<R> = LineInstructions<R>;
522
523#[derive(Clone, Debug)]
529pub struct LineInstructions<R: Reader> {
530 input: R,
531}
532
533impl<R: Reader> LineInstructions<R> {
534 fn remove_trailing(&self, other: &LineInstructions<R>) -> Result<LineInstructions<R>> {
535 let offset = other.input.offset_from(&self.input);
536 let mut input = self.input.clone();
537 input.truncate(offset)?;
538 Ok(LineInstructions { input })
539 }
540}
541
542impl<R: Reader> LineInstructions<R> {
543 #[inline(always)]
554 pub fn next_instruction(
555 &mut self,
556 header: &LineProgramHeader<R>,
557 ) -> Result<Option<LineInstruction<R>>> {
558 if self.input.is_empty() {
559 return Ok(None);
560 }
561
562 match LineInstruction::parse(header, &mut self.input) {
563 Ok(instruction) => Ok(Some(instruction)),
564 Err(e) => {
565 self.input.empty();
566 Err(e)
567 }
568 }
569 }
570}
571
572#[deprecated(note = "LineNumberRow has been renamed to LineRow, use that instead.")]
574pub type LineNumberRow = LineRow;
575
576#[derive(Clone, Copy, Debug, PartialEq, Eq)]
580pub struct LineRow {
581 tombstone: bool,
582 address: u64,
583 op_index: Wrapping<u64>,
584 file: u64,
585 line: Wrapping<u64>,
586 column: u64,
587 is_stmt: bool,
588 basic_block: bool,
589 end_sequence: bool,
590 prologue_end: bool,
591 epilogue_begin: bool,
592 isa: u64,
593 discriminator: u64,
594}
595
596impl LineRow {
597 pub fn new<R: Reader>(header: &LineProgramHeader<R>) -> Self {
599 LineRow {
600 tombstone: false,
603 address: 0,
604 op_index: Wrapping(0),
605 file: 1,
606 line: Wrapping(1),
607 column: 0,
608 is_stmt: header.line_encoding.default_is_stmt,
610 basic_block: false,
611 end_sequence: false,
612 prologue_end: false,
613 epilogue_begin: false,
614 isa: 0,
619 discriminator: 0,
620 }
621 }
622
623 #[inline]
626 pub fn address(&self) -> u64 {
627 self.address
628 }
629
630 #[inline]
638 pub fn op_index(&self) -> u64 {
639 self.op_index.0
640 }
641
642 #[inline]
645 pub fn file_index(&self) -> u64 {
646 self.file
647 }
648
649 #[inline]
651 pub fn file<'header, R: Reader>(
652 &self,
653 header: &'header LineProgramHeader<R>,
654 ) -> Option<&'header FileEntry<R>> {
655 header.file(self.file)
656 }
657
658 #[inline]
663 pub fn line(&self) -> Option<NonZeroU64> {
664 NonZeroU64::new(self.line.0)
665 }
666
667 #[inline]
671 pub fn column(&self) -> ColumnType {
672 NonZeroU64::new(self.column)
673 .map(ColumnType::Column)
674 .unwrap_or(ColumnType::LeftEdge)
675 }
676
677 #[inline]
682 pub fn is_stmt(&self) -> bool {
683 self.is_stmt
684 }
685
686 #[inline]
689 pub fn basic_block(&self) -> bool {
690 self.basic_block
691 }
692
693 #[inline]
698 pub fn end_sequence(&self) -> bool {
699 self.end_sequence
700 }
701
702 #[inline]
706 pub fn prologue_end(&self) -> bool {
707 self.prologue_end
708 }
709
710 #[inline]
714 pub fn epilogue_begin(&self) -> bool {
715 self.epilogue_begin
716 }
717
718 #[inline]
727 pub fn isa(&self) -> u64 {
728 self.isa
729 }
730
731 #[inline]
738 pub fn discriminator(&self) -> u64 {
739 self.discriminator
740 }
741
742 #[inline]
747 pub fn execute<R, Program>(
748 &mut self,
749 instruction: LineInstruction<R>,
750 program: &mut Program,
751 ) -> Result<bool>
752 where
753 Program: LineProgram<R>,
754 R: Reader,
755 {
756 Ok(match instruction {
757 LineInstruction::Special(opcode) => {
758 self.exec_special_opcode(opcode, program.header())?;
759 true
760 }
761
762 LineInstruction::Copy => true,
763
764 LineInstruction::AdvancePc(operation_advance) => {
765 self.apply_operation_advance(operation_advance, program.header())?;
766 false
767 }
768
769 LineInstruction::AdvanceLine(line_increment) => {
770 self.apply_line_advance(line_increment);
771 false
772 }
773
774 LineInstruction::SetFile(file) => {
775 self.file = file;
776 false
777 }
778
779 LineInstruction::SetColumn(column) => {
780 self.column = column;
781 false
782 }
783
784 LineInstruction::NegateStatement => {
785 self.is_stmt = !self.is_stmt;
786 false
787 }
788
789 LineInstruction::SetBasicBlock => {
790 self.basic_block = true;
791 false
792 }
793
794 LineInstruction::ConstAddPc => {
795 let adjusted = self.adjust_opcode(255, program.header());
796 let operation_advance = adjusted / program.header().line_encoding.line_range;
797 self.apply_operation_advance(u64::from(operation_advance), program.header())?;
798 false
799 }
800
801 LineInstruction::FixedAddPc(operand) => {
802 if !self.tombstone {
803 let address_size = program.header().address_size();
804 self.address = self.address.add_sized(u64::from(operand), address_size)?;
805 self.op_index.0 = 0;
806 }
807 false
808 }
809
810 LineInstruction::SetPrologueEnd => {
811 self.prologue_end = true;
812 false
813 }
814
815 LineInstruction::SetEpilogueBegin => {
816 self.epilogue_begin = true;
817 false
818 }
819
820 LineInstruction::SetIsa(isa) => {
821 self.isa = isa;
822 false
823 }
824
825 LineInstruction::EndSequence => {
826 self.end_sequence = true;
827 true
828 }
829
830 LineInstruction::SetAddress(address) => {
831 let tombstone_address = !0 >> (64 - program.header().encoding.address_size * 8);
839 self.tombstone = address < self.address || address == tombstone_address;
840 if !self.tombstone {
841 self.address = address;
842 self.op_index.0 = 0;
843 }
844 false
845 }
846
847 LineInstruction::DefineFile(entry) => {
848 program.add_file(entry);
849 false
850 }
851
852 LineInstruction::SetDiscriminator(discriminator) => {
853 self.discriminator = discriminator;
854 false
855 }
856
857 LineInstruction::UnknownStandard0(_)
859 | LineInstruction::UnknownStandard1(_, _)
860 | LineInstruction::UnknownStandardN(_, _)
861 | LineInstruction::UnknownExtended(_, _) => false,
862 })
863 }
864
865 #[inline]
867 pub fn reset<R: Reader>(&mut self, header: &LineProgramHeader<R>) {
868 if self.end_sequence {
869 *self = Self::new(header);
872 } else {
873 self.discriminator = 0;
878 self.basic_block = false;
879 self.prologue_end = false;
880 self.epilogue_begin = false;
881 }
882 }
883
884 fn apply_line_advance(&mut self, line_increment: i64) {
886 if line_increment < 0 {
887 let decrement = -line_increment as u64;
888 if decrement <= self.line.0 {
889 self.line.0 -= decrement;
890 } else {
891 self.line.0 = 0;
892 }
893 } else {
894 self.line += Wrapping(line_increment as u64);
895 }
896 }
897
898 fn apply_operation_advance<R: Reader>(
900 &mut self,
901 operation_advance: u64,
902 header: &LineProgramHeader<R>,
903 ) -> Result<()> {
904 if self.tombstone {
905 return Ok(());
906 }
907
908 let operation_advance = Wrapping(operation_advance);
909
910 let minimum_instruction_length = u64::from(header.line_encoding.minimum_instruction_length);
911 let minimum_instruction_length = Wrapping(minimum_instruction_length);
912
913 let maximum_operations_per_instruction =
914 u64::from(header.line_encoding.maximum_operations_per_instruction);
915 let maximum_operations_per_instruction = Wrapping(maximum_operations_per_instruction);
916
917 let address_advance = if maximum_operations_per_instruction.0 == 1 {
918 self.op_index.0 = 0;
919 minimum_instruction_length * operation_advance
920 } else {
921 let op_index_with_advance = self.op_index + operation_advance;
922 self.op_index = op_index_with_advance % maximum_operations_per_instruction;
923 minimum_instruction_length
924 * (op_index_with_advance / maximum_operations_per_instruction)
925 };
926 self.address = self
927 .address
928 .add_sized(address_advance.0, header.address_size())?;
929 Ok(())
930 }
931
932 #[inline]
933 fn adjust_opcode<R: Reader>(&self, opcode: u8, header: &LineProgramHeader<R>) -> u8 {
934 opcode - header.opcode_base
935 }
936
937 fn exec_special_opcode<R: Reader>(
939 &mut self,
940 opcode: u8,
941 header: &LineProgramHeader<R>,
942 ) -> Result<()> {
943 let adjusted_opcode = self.adjust_opcode(opcode, header);
944
945 let line_range = header.line_encoding.line_range;
946 let line_advance = adjusted_opcode % line_range;
947 let operation_advance = adjusted_opcode / line_range;
948
949 let line_base = i64::from(header.line_encoding.line_base);
951 self.apply_line_advance(line_base + i64::from(line_advance));
952
953 self.apply_operation_advance(u64::from(operation_advance), header)?;
955 Ok(())
956 }
957}
958
959#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
961pub enum ColumnType {
962 LeftEdge,
965 Column(NonZeroU64),
967}
968
969#[deprecated(note = "LineNumberSequence has been renamed to LineSequence, use that instead.")]
971pub type LineNumberSequence<R> = LineSequence<R>;
972
973#[derive(Clone, Debug)]
977pub struct LineSequence<R: Reader> {
978 pub start: u64,
981 pub end: u64,
984 instructions: LineInstructions<R>,
985}
986
987#[deprecated(
989 note = "LineNumberProgramHeader has been renamed to LineProgramHeader, use that instead."
990)]
991pub type LineNumberProgramHeader<R, Offset> = LineProgramHeader<R, Offset>;
992
993#[derive(Clone, Debug, Eq, PartialEq)]
996pub struct LineProgramHeader<R, Offset = <R as Reader>::Offset>
997where
998 R: Reader<Offset = Offset>,
999 Offset: ReaderOffset,
1000{
1001 encoding: Encoding,
1002 offset: DebugLineOffset<Offset>,
1003 unit_length: Offset,
1004
1005 header_length: Offset,
1006
1007 line_encoding: LineEncoding,
1008
1009 opcode_base: u8,
1011
1012 standard_opcode_lengths: R,
1017
1018 directory_entry_format: Vec<FileEntryFormat>,
1020
1021 include_directories: Vec<AttributeValue<R, Offset>>,
1030
1031 file_name_entry_format: Vec<FileEntryFormat>,
1033
1034 file_names: Vec<FileEntry<R, Offset>>,
1038
1039 program_buf: R,
1041
1042 comp_dir: Option<R>,
1044
1045 comp_file: Option<FileEntry<R, Offset>>,
1047}
1048
1049impl<R, Offset> LineProgramHeader<R, Offset>
1050where
1051 R: Reader<Offset = Offset>,
1052 Offset: ReaderOffset,
1053{
1054 pub fn offset(&self) -> DebugLineOffset<R::Offset> {
1056 self.offset
1057 }
1058
1059 pub fn unit_length(&self) -> R::Offset {
1062 self.unit_length
1063 }
1064
1065 pub fn encoding(&self) -> Encoding {
1067 self.encoding
1068 }
1069
1070 pub fn version(&self) -> u16 {
1072 self.encoding.version
1073 }
1074
1075 pub fn header_length(&self) -> R::Offset {
1078 self.header_length
1079 }
1080
1081 pub fn address_size(&self) -> u8 {
1083 self.encoding.address_size
1084 }
1085
1086 pub fn format(&self) -> Format {
1088 self.encoding.format
1089 }
1090
1091 pub fn line_encoding(&self) -> LineEncoding {
1093 self.line_encoding
1094 }
1095
1096 pub fn minimum_instruction_length(&self) -> u8 {
1099 self.line_encoding.minimum_instruction_length
1100 }
1101
1102 pub fn maximum_operations_per_instruction(&self) -> u8 {
1105 self.line_encoding.maximum_operations_per_instruction
1106 }
1107
1108 pub fn default_is_stmt(&self) -> bool {
1111 self.line_encoding.default_is_stmt
1112 }
1113
1114 pub fn line_base(&self) -> i8 {
1116 self.line_encoding.line_base
1117 }
1118
1119 pub fn line_range(&self) -> u8 {
1121 self.line_encoding.line_range
1122 }
1123
1124 pub fn opcode_base(&self) -> u8 {
1126 self.opcode_base
1127 }
1128
1129 pub fn standard_opcode_lengths(&self) -> &R {
1132 &self.standard_opcode_lengths
1133 }
1134
1135 pub fn directory_entry_format(&self) -> &[FileEntryFormat] {
1137 &self.directory_entry_format[..]
1138 }
1139
1140 pub fn include_directories(&self) -> &[AttributeValue<R, Offset>] {
1145 &self.include_directories[..]
1146 }
1147
1148 pub fn directory(&self, directory: u64) -> Option<AttributeValue<R, Offset>> {
1152 if self.encoding.version <= 4 {
1153 if directory == 0 {
1154 self.comp_dir.clone().map(AttributeValue::String)
1155 } else {
1156 let directory = directory as usize - 1;
1157 self.include_directories.get(directory).cloned()
1158 }
1159 } else {
1160 self.include_directories.get(directory as usize).cloned()
1161 }
1162 }
1163
1164 pub fn file_name_entry_format(&self) -> &[FileEntryFormat] {
1166 &self.file_name_entry_format[..]
1167 }
1168
1169 pub fn file_has_timestamp(&self) -> bool {
1174 self.encoding.version <= 4
1175 || self
1176 .file_name_entry_format
1177 .iter()
1178 .any(|x| x.content_type == constants::DW_LNCT_timestamp)
1179 }
1180
1181 pub fn file_has_size(&self) -> bool {
1186 self.encoding.version <= 4
1187 || self
1188 .file_name_entry_format
1189 .iter()
1190 .any(|x| x.content_type == constants::DW_LNCT_size)
1191 }
1192
1193 pub fn file_has_md5(&self) -> bool {
1195 self.file_name_entry_format
1196 .iter()
1197 .any(|x| x.content_type == constants::DW_LNCT_MD5)
1198 }
1199
1200 pub fn file_has_source(&self) -> bool {
1202 self.file_name_entry_format
1203 .iter()
1204 .any(|x| x.content_type == constants::DW_LNCT_LLVM_source)
1205 }
1206
1207 pub fn file_names(&self) -> &[FileEntry<R, Offset>] {
1209 &self.file_names[..]
1210 }
1211
1212 pub fn file(&self, file: u64) -> Option<&FileEntry<R, Offset>> {
1218 if self.encoding.version <= 4 {
1219 if file == 0 {
1220 self.comp_file.as_ref()
1221 } else {
1222 let file = file as usize - 1;
1223 self.file_names.get(file)
1224 }
1225 } else {
1226 self.file_names.get(file as usize)
1227 }
1228 }
1229
1230 pub fn raw_program_buf(&self) -> R {
1249 self.program_buf.clone()
1250 }
1251
1252 pub fn instructions(&self) -> LineInstructions<R> {
1255 LineInstructions {
1256 input: self.program_buf.clone(),
1257 }
1258 }
1259
1260 fn parse(
1261 input: &mut R,
1262 offset: DebugLineOffset<Offset>,
1263 mut address_size: u8,
1264 mut comp_dir: Option<R>,
1265 comp_name: Option<R>,
1266 ) -> Result<LineProgramHeader<R, Offset>> {
1267 let (unit_length, format) = input.read_initial_length()?;
1268 let rest = &mut input.split(unit_length)?;
1269
1270 let version = rest.read_u16()?;
1271 if version < 2 || version > 5 {
1272 return Err(Error::UnknownVersion(u64::from(version)));
1273 }
1274
1275 if version >= 5 {
1276 address_size = rest.read_address_size()?;
1277 let segment_selector_size = rest.read_u8()?;
1278 if segment_selector_size != 0 {
1279 return Err(Error::UnsupportedSegmentSize);
1280 }
1281 }
1282
1283 let encoding = Encoding {
1284 format,
1285 version,
1286 address_size,
1287 };
1288
1289 let header_length = rest.read_length(format)?;
1290
1291 let mut program_buf = rest.clone();
1292 program_buf.skip(header_length)?;
1293 rest.truncate(header_length)?;
1294
1295 let minimum_instruction_length = rest.read_u8()?;
1296 if minimum_instruction_length == 0 {
1297 return Err(Error::MinimumInstructionLengthZero);
1298 }
1299
1300 let maximum_operations_per_instruction = if version >= 4 { rest.read_u8()? } else { 1 };
1303 if maximum_operations_per_instruction == 0 {
1304 return Err(Error::MaximumOperationsPerInstructionZero);
1305 }
1306
1307 let default_is_stmt = rest.read_u8()? != 0;
1308 let line_base = rest.read_i8()?;
1309 let line_range = rest.read_u8()?;
1310 if line_range == 0 {
1311 return Err(Error::LineRangeZero);
1312 }
1313 let line_encoding = LineEncoding {
1314 minimum_instruction_length,
1315 maximum_operations_per_instruction,
1316 default_is_stmt,
1317 line_base,
1318 line_range,
1319 };
1320
1321 let opcode_base = rest.read_u8()?;
1322 if opcode_base == 0 {
1323 return Err(Error::OpcodeBaseZero);
1324 }
1325
1326 let standard_opcode_count = R::Offset::from_u8(opcode_base - 1);
1327 let standard_opcode_lengths = rest.split(standard_opcode_count)?;
1328
1329 let directory_entry_format;
1330 let mut include_directories = Vec::new();
1331 if version <= 4 {
1332 directory_entry_format = Vec::new();
1333 loop {
1334 let directory = rest.read_null_terminated_slice()?;
1335 if directory.is_empty() {
1336 break;
1337 }
1338 include_directories.push(AttributeValue::String(directory));
1339 }
1340 } else {
1341 comp_dir = None;
1342 directory_entry_format = FileEntryFormat::parse(rest)?;
1343 let count = rest.read_uleb128()?;
1344 for _ in 0..count {
1345 include_directories.push(parse_directory_v5(
1346 rest,
1347 encoding,
1348 &directory_entry_format,
1349 )?);
1350 }
1351 }
1352
1353 let comp_file;
1354 let file_name_entry_format;
1355 let mut file_names = Vec::new();
1356 if version <= 4 {
1357 comp_file = comp_name.map(|name| FileEntry {
1358 path_name: AttributeValue::String(name),
1359 directory_index: 0,
1360 timestamp: 0,
1361 size: 0,
1362 md5: [0; 16],
1363 source: None,
1364 });
1365
1366 file_name_entry_format = Vec::new();
1367 loop {
1368 let path_name = rest.read_null_terminated_slice()?;
1369 if path_name.is_empty() {
1370 break;
1371 }
1372 file_names.push(FileEntry::parse(rest, path_name)?);
1373 }
1374 } else {
1375 comp_file = None;
1376 file_name_entry_format = FileEntryFormat::parse(rest)?;
1377 let count = rest.read_uleb128()?;
1378 for _ in 0..count {
1379 file_names.push(parse_file_v5(rest, encoding, &file_name_entry_format)?);
1380 }
1381 }
1382
1383 let header = LineProgramHeader {
1384 encoding,
1385 offset,
1386 unit_length,
1387 header_length,
1388 line_encoding,
1389 opcode_base,
1390 standard_opcode_lengths,
1391 directory_entry_format,
1392 include_directories,
1393 file_name_entry_format,
1394 file_names,
1395 program_buf,
1396 comp_dir,
1397 comp_file,
1398 };
1399 Ok(header)
1400 }
1401}
1402
1403#[deprecated(
1405 note = "IncompleteLineNumberProgram has been renamed to IncompleteLineProgram, use that instead."
1406)]
1407pub type IncompleteLineNumberProgram<R, Offset> = IncompleteLineProgram<R, Offset>;
1408
1409#[derive(Clone, Debug, Eq, PartialEq)]
1411pub struct IncompleteLineProgram<R, Offset = <R as Reader>::Offset>
1412where
1413 R: Reader<Offset = Offset>,
1414 Offset: ReaderOffset,
1415{
1416 header: LineProgramHeader<R, Offset>,
1417}
1418
1419impl<R, Offset> IncompleteLineProgram<R, Offset>
1420where
1421 R: Reader<Offset = Offset>,
1422 Offset: ReaderOffset,
1423{
1424 pub fn header(&self) -> &LineProgramHeader<R, Offset> {
1426 &self.header
1427 }
1428
1429 pub fn rows(self) -> OneShotLineRows<R, Offset> {
1432 OneShotLineRows::new(self)
1433 }
1434
1435 #[allow(clippy::type_complexity)]
1456 pub fn sequences(self) -> Result<(CompleteLineProgram<R, Offset>, Vec<LineSequence<R>>)> {
1457 let mut sequences = Vec::new();
1458 let mut rows = self.rows();
1459 let mut instructions = rows.instructions.clone();
1460 let mut sequence_start_addr = None;
1461 loop {
1462 let sequence_end_addr;
1463 if rows.next_row()?.is_none() {
1464 break;
1465 }
1466
1467 let row = &rows.row;
1468 if row.end_sequence() {
1469 sequence_end_addr = row.address();
1470 } else if sequence_start_addr.is_none() {
1471 sequence_start_addr = Some(row.address());
1472 continue;
1473 } else {
1474 continue;
1475 }
1476
1477 sequences.push(LineSequence {
1479 start: sequence_start_addr.unwrap_or(0),
1482 end: sequence_end_addr,
1483 instructions: instructions.remove_trailing(&rows.instructions)?,
1484 });
1485 sequence_start_addr = None;
1486 instructions = rows.instructions.clone();
1487 }
1488
1489 let program = CompleteLineProgram {
1490 header: rows.program.header,
1491 };
1492 Ok((program, sequences))
1493 }
1494}
1495
1496#[deprecated(
1498 note = "CompleteLineNumberProgram has been renamed to CompleteLineProgram, use that instead."
1499)]
1500pub type CompleteLineNumberProgram<R, Offset> = CompleteLineProgram<R, Offset>;
1501
1502#[derive(Clone, Debug, Eq, PartialEq)]
1504pub struct CompleteLineProgram<R, Offset = <R as Reader>::Offset>
1505where
1506 R: Reader<Offset = Offset>,
1507 Offset: ReaderOffset,
1508{
1509 header: LineProgramHeader<R, Offset>,
1510}
1511
1512impl<R, Offset> CompleteLineProgram<R, Offset>
1513where
1514 R: Reader<Offset = Offset>,
1515 Offset: ReaderOffset,
1516{
1517 pub fn header(&self) -> &LineProgramHeader<R, Offset> {
1519 &self.header
1520 }
1521
1522 pub fn resume_from<'program>(
1544 &'program self,
1545 sequence: &LineSequence<R>,
1546 ) -> ResumedLineRows<'program, R, Offset> {
1547 ResumedLineRows::resume(self, sequence)
1548 }
1549}
1550
1551#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1553pub struct FileEntry<R, Offset = <R as Reader>::Offset>
1554where
1555 R: Reader<Offset = Offset>,
1556 Offset: ReaderOffset,
1557{
1558 path_name: AttributeValue<R, Offset>,
1559 directory_index: u64,
1560 timestamp: u64,
1561 size: u64,
1562 md5: [u8; 16],
1563 source: Option<AttributeValue<R, Offset>>,
1564}
1565
1566impl<R, Offset> FileEntry<R, Offset>
1567where
1568 R: Reader<Offset = Offset>,
1569 Offset: ReaderOffset,
1570{
1571 fn parse(input: &mut R, path_name: R) -> Result<FileEntry<R, Offset>> {
1573 let directory_index = input.read_uleb128()?;
1574 let timestamp = input.read_uleb128()?;
1575 let size = input.read_uleb128()?;
1576
1577 let entry = FileEntry {
1578 path_name: AttributeValue::String(path_name),
1579 directory_index,
1580 timestamp,
1581 size,
1582 md5: [0; 16],
1583 source: None,
1584 };
1585
1586 Ok(entry)
1587 }
1588
1589 pub fn path_name(&self) -> AttributeValue<R, Offset> {
1595 self.path_name.clone()
1596 }
1597
1598 pub fn directory_index(&self) -> u64 {
1610 self.directory_index
1611 }
1612
1613 pub fn directory(&self, header: &LineProgramHeader<R>) -> Option<AttributeValue<R, Offset>> {
1617 header.directory(self.directory_index)
1618 }
1619
1620 pub fn timestamp(&self) -> u64 {
1623 self.timestamp
1624 }
1625
1626 #[doc(hidden)]
1630 pub fn last_modification(&self) -> u64 {
1631 self.timestamp
1632 }
1633
1634 pub fn size(&self) -> u64 {
1636 self.size
1637 }
1638
1639 #[doc(hidden)]
1643 pub fn length(&self) -> u64 {
1644 self.size
1645 }
1646
1647 pub fn md5(&self) -> &[u8; 16] {
1651 &self.md5
1652 }
1653
1654 pub fn source(&self) -> Option<AttributeValue<R, Offset>> {
1661 self.source.clone()
1662 }
1663}
1664
1665#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1667pub struct FileEntryFormat {
1668 pub content_type: constants::DwLnct,
1670
1671 pub form: constants::DwForm,
1673}
1674
1675impl FileEntryFormat {
1676 fn parse<R: Reader>(input: &mut R) -> Result<Vec<FileEntryFormat>> {
1677 let format_count = input.read_u8()? as usize;
1678 let mut format = Vec::with_capacity(format_count);
1679 let mut path_count = 0;
1680 for _ in 0..format_count {
1681 let content_type = input.read_uleb128()?;
1682 let content_type = if content_type > u64::from(u16::MAX) {
1683 constants::DwLnct(u16::MAX)
1684 } else {
1685 constants::DwLnct(content_type as u16)
1686 };
1687 if content_type == constants::DW_LNCT_path {
1688 path_count += 1;
1689 }
1690
1691 let form = constants::DwForm(input.read_uleb128_u16()?);
1692
1693 format.push(FileEntryFormat { content_type, form });
1694 }
1695 if path_count != 1 {
1696 return Err(Error::MissingFileEntryFormatPath);
1697 }
1698 Ok(format)
1699 }
1700}
1701
1702fn parse_directory_v5<R: Reader>(
1703 input: &mut R,
1704 encoding: Encoding,
1705 formats: &[FileEntryFormat],
1706) -> Result<AttributeValue<R>> {
1707 let mut path_name = None;
1708
1709 for format in formats {
1710 let value = parse_attribute(input, encoding, format.form)?;
1711 if format.content_type == constants::DW_LNCT_path {
1712 path_name = Some(value);
1713 }
1714 }
1715
1716 Ok(path_name.unwrap())
1717}
1718
1719fn parse_file_v5<R: Reader>(
1720 input: &mut R,
1721 encoding: Encoding,
1722 formats: &[FileEntryFormat],
1723) -> Result<FileEntry<R>> {
1724 let mut path_name = None;
1725 let mut directory_index = 0;
1726 let mut timestamp = 0;
1727 let mut size = 0;
1728 let mut md5 = [0; 16];
1729 let mut source = None;
1730
1731 for format in formats {
1732 let value = parse_attribute(input, encoding, format.form)?;
1733 match format.content_type {
1734 constants::DW_LNCT_path => path_name = Some(value),
1735 constants::DW_LNCT_directory_index => {
1736 if let Some(value) = value.udata_value() {
1737 directory_index = value;
1738 }
1739 }
1740 constants::DW_LNCT_timestamp => {
1741 if let Some(value) = value.udata_value() {
1742 timestamp = value;
1743 }
1744 }
1745 constants::DW_LNCT_size => {
1746 if let Some(value) = value.udata_value() {
1747 size = value;
1748 }
1749 }
1750 constants::DW_LNCT_MD5 => {
1751 if let AttributeValue::Block(mut value) = value {
1752 if value.len().into_u64() == 16 {
1753 md5 = value.read_u8_array()?;
1754 }
1755 }
1756 }
1757 constants::DW_LNCT_LLVM_source => {
1758 source = Some(value);
1759 }
1760 _ => {}
1762 }
1763 }
1764
1765 Ok(FileEntry {
1766 path_name: path_name.unwrap(),
1767 directory_index,
1768 timestamp,
1769 size,
1770 md5,
1771 source,
1772 })
1773}
1774
1775fn parse_attribute<R: Reader>(
1777 input: &mut R,
1778 encoding: Encoding,
1779 form: constants::DwForm,
1780) -> Result<AttributeValue<R>> {
1781 Ok(match form {
1782 constants::DW_FORM_block1 => {
1783 let len = input.read_u8().map(R::Offset::from_u8)?;
1784 let block = input.split(len)?;
1785 AttributeValue::Block(block)
1786 }
1787 constants::DW_FORM_block2 => {
1788 let len = input.read_u16().map(R::Offset::from_u16)?;
1789 let block = input.split(len)?;
1790 AttributeValue::Block(block)
1791 }
1792 constants::DW_FORM_block4 => {
1793 let len = input.read_u32().map(R::Offset::from_u32)?;
1794 let block = input.split(len)?;
1795 AttributeValue::Block(block)
1796 }
1797 constants::DW_FORM_block => {
1798 let len = input.read_uleb128().and_then(R::Offset::from_u64)?;
1799 let block = input.split(len)?;
1800 AttributeValue::Block(block)
1801 }
1802 constants::DW_FORM_data1 => {
1803 let data = input.read_u8()?;
1804 AttributeValue::Data1(data)
1805 }
1806 constants::DW_FORM_data2 => {
1807 let data = input.read_u16()?;
1808 AttributeValue::Data2(data)
1809 }
1810 constants::DW_FORM_data4 => {
1811 let data = input.read_u32()?;
1812 AttributeValue::Data4(data)
1813 }
1814 constants::DW_FORM_data8 => {
1815 let data = input.read_u64()?;
1816 AttributeValue::Data8(data)
1817 }
1818 constants::DW_FORM_data16 => {
1819 let block = input.split(R::Offset::from_u8(16))?;
1820 AttributeValue::Block(block)
1821 }
1822 constants::DW_FORM_udata => {
1823 let data = input.read_uleb128()?;
1824 AttributeValue::Udata(data)
1825 }
1826 constants::DW_FORM_sdata => {
1827 let data = input.read_sleb128()?;
1828 AttributeValue::Sdata(data)
1829 }
1830 constants::DW_FORM_flag => {
1831 let present = input.read_u8()?;
1832 AttributeValue::Flag(present != 0)
1833 }
1834 constants::DW_FORM_sec_offset => {
1835 let offset = input.read_offset(encoding.format)?;
1836 AttributeValue::SecOffset(offset)
1837 }
1838 constants::DW_FORM_string => {
1839 let string = input.read_null_terminated_slice()?;
1840 AttributeValue::String(string)
1841 }
1842 constants::DW_FORM_strp => {
1843 let offset = input.read_offset(encoding.format)?;
1844 AttributeValue::DebugStrRef(DebugStrOffset(offset))
1845 }
1846 constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt => {
1847 let offset = input.read_offset(encoding.format)?;
1848 AttributeValue::DebugStrRefSup(DebugStrOffset(offset))
1849 }
1850 constants::DW_FORM_line_strp => {
1851 let offset = input.read_offset(encoding.format)?;
1852 AttributeValue::DebugLineStrRef(DebugLineStrOffset(offset))
1853 }
1854 constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index => {
1855 let index = input.read_uleb128().and_then(R::Offset::from_u64)?;
1856 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1857 }
1858 constants::DW_FORM_strx1 => {
1859 let index = input.read_u8().map(R::Offset::from_u8)?;
1860 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1861 }
1862 constants::DW_FORM_strx2 => {
1863 let index = input.read_u16().map(R::Offset::from_u16)?;
1864 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1865 }
1866 constants::DW_FORM_strx3 => {
1867 let index = input.read_uint(3).and_then(R::Offset::from_u64)?;
1868 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1869 }
1870 constants::DW_FORM_strx4 => {
1871 let index = input.read_u32().map(R::Offset::from_u32)?;
1872 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1873 }
1874 _ => {
1875 return Err(Error::UnknownForm(form));
1876 }
1877 })
1878}
1879
1880#[cfg(test)]
1881mod tests {
1882 use super::*;
1883 use crate::constants;
1884 use crate::endianity::LittleEndian;
1885 use crate::read::{EndianSlice, Error};
1886 use crate::test_util::GimliSectionMethods;
1887 use test_assembler::{Endian, Label, LabelMaker, Section};
1888
1889 #[test]
1890 fn test_parse_debug_line_32_ok() {
1891 #[rustfmt::skip]
1892 let buf = [
1893 0x3e, 0x00, 0x00, 0x00,
1895 0x04, 0x00,
1897 0x28, 0x00, 0x00, 0x00,
1899 0x01,
1901 0x01,
1903 0x01,
1905 0x00,
1907 0x01,
1909 0x03,
1911 0x01, 0x02,
1913 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
1915 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
1918 0x00,
1919 0x00,
1920 0x00,
1921 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
1923 0x01,
1924 0x00,
1925 0x00,
1926 0x00,
1928
1929 0x00, 0x00, 0x00, 0x00,
1931 0x00, 0x00, 0x00, 0x00,
1932 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x00, 0x00, 0x00,
1934
1935 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x00, 0x00, 0x00,
1940 ];
1941
1942 let rest = &mut EndianSlice::new(&buf, LittleEndian);
1943 let comp_dir = EndianSlice::new(b"/comp_dir", LittleEndian);
1944 let comp_name = EndianSlice::new(b"/comp_name", LittleEndian);
1945
1946 let header =
1947 LineProgramHeader::parse(rest, DebugLineOffset(0), 4, Some(comp_dir), Some(comp_name))
1948 .expect("should parse header ok");
1949
1950 assert_eq!(
1951 *rest,
1952 EndianSlice::new(&buf[buf.len() - 16..], LittleEndian)
1953 );
1954
1955 assert_eq!(header.offset, DebugLineOffset(0));
1956 assert_eq!(header.version(), 4);
1957 assert_eq!(header.minimum_instruction_length(), 1);
1958 assert_eq!(header.maximum_operations_per_instruction(), 1);
1959 assert!(header.default_is_stmt());
1960 assert_eq!(header.line_base(), 0);
1961 assert_eq!(header.line_range(), 1);
1962 assert_eq!(header.opcode_base(), 3);
1963 assert_eq!(header.directory(0), Some(AttributeValue::String(comp_dir)));
1964 assert_eq!(
1965 header.file(0).unwrap().path_name,
1966 AttributeValue::String(comp_name)
1967 );
1968
1969 let expected_lengths = [1, 2];
1970 assert_eq!(header.standard_opcode_lengths().slice(), &expected_lengths);
1971
1972 let expected_include_directories = [
1973 AttributeValue::String(EndianSlice::new(b"/inc", LittleEndian)),
1974 AttributeValue::String(EndianSlice::new(b"/inc2", LittleEndian)),
1975 ];
1976 assert_eq!(header.include_directories(), &expected_include_directories);
1977
1978 let expected_file_names = [
1979 FileEntry {
1980 path_name: AttributeValue::String(EndianSlice::new(b"foo.rs", LittleEndian)),
1981 directory_index: 0,
1982 timestamp: 0,
1983 size: 0,
1984 md5: [0; 16],
1985 source: None,
1986 },
1987 FileEntry {
1988 path_name: AttributeValue::String(EndianSlice::new(b"bar.h", LittleEndian)),
1989 directory_index: 1,
1990 timestamp: 0,
1991 size: 0,
1992 md5: [0; 16],
1993 source: None,
1994 },
1995 ];
1996 assert_eq!(header.file_names(), &expected_file_names);
1997 }
1998
1999 #[test]
2000 fn test_parse_debug_line_header_length_too_short() {
2001 #[rustfmt::skip]
2002 let buf = [
2003 0x3e, 0x00, 0x00, 0x00,
2005 0x04, 0x00,
2007 0x15, 0x00, 0x00, 0x00,
2009 0x01,
2011 0x01,
2013 0x01,
2015 0x00,
2017 0x01,
2019 0x03,
2021 0x01, 0x02,
2023 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
2025 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
2028 0x00,
2029 0x00,
2030 0x00,
2031 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
2033 0x01,
2034 0x00,
2035 0x00,
2036 0x00,
2038
2039 0x00, 0x00, 0x00, 0x00,
2041 0x00, 0x00, 0x00, 0x00,
2042 0x00, 0x00, 0x00, 0x00,
2043 0x00, 0x00, 0x00, 0x00,
2044
2045 0x00, 0x00, 0x00, 0x00,
2047 0x00, 0x00, 0x00, 0x00,
2048 0x00, 0x00, 0x00, 0x00,
2049 0x00, 0x00, 0x00, 0x00,
2050 ];
2051
2052 let input = &mut EndianSlice::new(&buf, LittleEndian);
2053
2054 match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) {
2055 Err(Error::UnexpectedEof(_)) => {}
2056 otherwise => panic!("Unexpected result: {:?}", otherwise),
2057 }
2058 }
2059
2060 #[test]
2061 fn test_parse_debug_line_unit_length_too_short() {
2062 #[rustfmt::skip]
2063 let buf = [
2064 0x28, 0x00, 0x00, 0x00,
2066 0x04, 0x00,
2068 0x28, 0x00, 0x00, 0x00,
2070 0x01,
2072 0x01,
2074 0x01,
2076 0x00,
2078 0x01,
2080 0x03,
2082 0x01, 0x02,
2084 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
2086 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
2089 0x00,
2090 0x00,
2091 0x00,
2092 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
2094 0x01,
2095 0x00,
2096 0x00,
2097 0x00,
2099
2100 0x00, 0x00, 0x00, 0x00,
2102 0x00, 0x00, 0x00, 0x00,
2103 0x00, 0x00, 0x00, 0x00,
2104 0x00, 0x00, 0x00, 0x00,
2105
2106 0x00, 0x00, 0x00, 0x00,
2108 0x00, 0x00, 0x00, 0x00,
2109 0x00, 0x00, 0x00, 0x00,
2110 0x00, 0x00, 0x00, 0x00,
2111 ];
2112
2113 let input = &mut EndianSlice::new(&buf, LittleEndian);
2114
2115 match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) {
2116 Err(Error::UnexpectedEof(_)) => {}
2117 otherwise => panic!("Unexpected result: {:?}", otherwise),
2118 }
2119 }
2120
2121 const OPCODE_BASE: u8 = 13;
2122 const STANDARD_OPCODE_LENGTHS: &[u8] = &[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1];
2123
2124 fn make_test_header(
2125 buf: EndianSlice<'_, LittleEndian>,
2126 ) -> LineProgramHeader<EndianSlice<'_, LittleEndian>> {
2127 let encoding = Encoding {
2128 format: Format::Dwarf32,
2129 version: 4,
2130 address_size: 8,
2131 };
2132 let line_encoding = LineEncoding {
2133 line_base: -3,
2134 line_range: 12,
2135 ..Default::default()
2136 };
2137 LineProgramHeader {
2138 encoding,
2139 offset: DebugLineOffset(0),
2140 unit_length: 1,
2141 header_length: 1,
2142 line_encoding,
2143 opcode_base: OPCODE_BASE,
2144 standard_opcode_lengths: EndianSlice::new(STANDARD_OPCODE_LENGTHS, LittleEndian),
2145 file_names: vec![
2146 FileEntry {
2147 path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)),
2148 directory_index: 0,
2149 timestamp: 0,
2150 size: 0,
2151 md5: [0; 16],
2152 source: None,
2153 },
2154 FileEntry {
2155 path_name: AttributeValue::String(EndianSlice::new(b"bar.rs", LittleEndian)),
2156 directory_index: 0,
2157 timestamp: 0,
2158 size: 0,
2159 md5: [0; 16],
2160 source: None,
2161 },
2162 ],
2163 include_directories: vec![],
2164 directory_entry_format: vec![],
2165 file_name_entry_format: vec![],
2166 program_buf: buf,
2167 comp_dir: None,
2168 comp_file: None,
2169 }
2170 }
2171
2172 fn make_test_program(
2173 buf: EndianSlice<'_, LittleEndian>,
2174 ) -> IncompleteLineProgram<EndianSlice<'_, LittleEndian>> {
2175 IncompleteLineProgram {
2176 header: make_test_header(buf),
2177 }
2178 }
2179
2180 #[test]
2181 fn test_parse_special_opcodes() {
2182 for i in OPCODE_BASE..u8::MAX {
2183 let input = [i, 0, 0, 0];
2184 let input = EndianSlice::new(&input, LittleEndian);
2185 let header = make_test_header(input);
2186
2187 let mut rest = input;
2188 let opcode =
2189 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2190
2191 assert_eq!(*rest, *input.range_from(1..));
2192 assert_eq!(opcode, LineInstruction::Special(i));
2193 }
2194 }
2195
2196 #[test]
2197 fn test_parse_standard_opcodes() {
2198 fn test<Operands>(
2199 raw: constants::DwLns,
2200 operands: Operands,
2201 expected: LineInstruction<EndianSlice<'_, LittleEndian>>,
2202 ) where
2203 Operands: AsRef<[u8]>,
2204 {
2205 let mut input = Vec::new();
2206 input.push(raw.0);
2207 input.extend_from_slice(operands.as_ref());
2208
2209 let expected_rest = [0, 1, 2, 3, 4];
2210 input.extend_from_slice(&expected_rest);
2211
2212 let input = EndianSlice::new(&input, LittleEndian);
2213 let header = make_test_header(input);
2214
2215 let mut rest = input;
2216 let opcode =
2217 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2218
2219 assert_eq!(opcode, expected);
2220 assert_eq!(*rest, expected_rest);
2221 }
2222
2223 test(constants::DW_LNS_copy, [], LineInstruction::Copy);
2224 test(
2225 constants::DW_LNS_advance_pc,
2226 [42],
2227 LineInstruction::AdvancePc(42),
2228 );
2229 test(
2230 constants::DW_LNS_advance_line,
2231 [9],
2232 LineInstruction::AdvanceLine(9),
2233 );
2234 test(constants::DW_LNS_set_file, [7], LineInstruction::SetFile(7));
2235 test(
2236 constants::DW_LNS_set_column,
2237 [1],
2238 LineInstruction::SetColumn(1),
2239 );
2240 test(
2241 constants::DW_LNS_negate_stmt,
2242 [],
2243 LineInstruction::NegateStatement,
2244 );
2245 test(
2246 constants::DW_LNS_set_basic_block,
2247 [],
2248 LineInstruction::SetBasicBlock,
2249 );
2250 test(
2251 constants::DW_LNS_const_add_pc,
2252 [],
2253 LineInstruction::ConstAddPc,
2254 );
2255 test(
2256 constants::DW_LNS_fixed_advance_pc,
2257 [42, 0],
2258 LineInstruction::FixedAddPc(42),
2259 );
2260 test(
2261 constants::DW_LNS_set_prologue_end,
2262 [],
2263 LineInstruction::SetPrologueEnd,
2264 );
2265 test(
2266 constants::DW_LNS_set_isa,
2267 [57 + 0x80, 100],
2268 LineInstruction::SetIsa(12857),
2269 );
2270 }
2271
2272 #[test]
2273 fn test_parse_unknown_standard_opcode_no_args() {
2274 let input = [OPCODE_BASE, 1, 2, 3];
2275 let input = EndianSlice::new(&input, LittleEndian);
2276 let mut standard_opcode_lengths = Vec::new();
2277 let mut header = make_test_header(input);
2278 standard_opcode_lengths.extend(header.standard_opcode_lengths.slice());
2279 standard_opcode_lengths.push(0);
2280 header.opcode_base += 1;
2281 header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian);
2282
2283 let mut rest = input;
2284 let opcode =
2285 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2286
2287 assert_eq!(
2288 opcode,
2289 LineInstruction::UnknownStandard0(constants::DwLns(OPCODE_BASE))
2290 );
2291 assert_eq!(*rest, *input.range_from(1..));
2292 }
2293
2294 #[test]
2295 fn test_parse_unknown_standard_opcode_one_arg() {
2296 let input = [OPCODE_BASE, 1, 2, 3];
2297 let input = EndianSlice::new(&input, LittleEndian);
2298 let mut standard_opcode_lengths = Vec::new();
2299 let mut header = make_test_header(input);
2300 standard_opcode_lengths.extend(header.standard_opcode_lengths.slice());
2301 standard_opcode_lengths.push(1);
2302 header.opcode_base += 1;
2303 header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian);
2304
2305 let mut rest = input;
2306 let opcode =
2307 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2308
2309 assert_eq!(
2310 opcode,
2311 LineInstruction::UnknownStandard1(constants::DwLns(OPCODE_BASE), 1)
2312 );
2313 assert_eq!(*rest, *input.range_from(2..));
2314 }
2315
2316 #[test]
2317 fn test_parse_unknown_standard_opcode_many_args() {
2318 let input = [OPCODE_BASE, 1, 2, 3];
2319 let input = EndianSlice::new(&input, LittleEndian);
2320 let args = input.range_from(1..);
2321 let mut standard_opcode_lengths = Vec::new();
2322 let mut header = make_test_header(input);
2323 standard_opcode_lengths.extend(header.standard_opcode_lengths.slice());
2324 standard_opcode_lengths.push(3);
2325 header.opcode_base += 1;
2326 header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian);
2327
2328 let mut rest = input;
2329 let opcode =
2330 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2331
2332 assert_eq!(
2333 opcode,
2334 LineInstruction::UnknownStandardN(constants::DwLns(OPCODE_BASE), args)
2335 );
2336 assert_eq!(*rest, []);
2337 }
2338
2339 #[test]
2340 fn test_parse_extended_opcodes() {
2341 fn test<Operands>(
2342 raw: constants::DwLne,
2343 operands: Operands,
2344 expected: LineInstruction<EndianSlice<'_, LittleEndian>>,
2345 ) where
2346 Operands: AsRef<[u8]>,
2347 {
2348 let mut input = Vec::new();
2349 input.push(0);
2350
2351 let operands = operands.as_ref();
2352 input.push(1 + operands.len() as u8);
2353
2354 input.push(raw.0);
2355 input.extend_from_slice(operands);
2356
2357 let expected_rest = [0, 1, 2, 3, 4];
2358 input.extend_from_slice(&expected_rest);
2359
2360 let input = EndianSlice::new(&input, LittleEndian);
2361 let header = make_test_header(input);
2362
2363 let mut rest = input;
2364 let opcode =
2365 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2366
2367 assert_eq!(opcode, expected);
2368 assert_eq!(*rest, expected_rest);
2369 }
2370
2371 test(
2372 constants::DW_LNE_end_sequence,
2373 [],
2374 LineInstruction::EndSequence,
2375 );
2376 test(
2377 constants::DW_LNE_set_address,
2378 [1, 2, 3, 4, 5, 6, 7, 8],
2379 LineInstruction::SetAddress(578_437_695_752_307_201),
2380 );
2381 test(
2382 constants::DW_LNE_set_discriminator,
2383 [42],
2384 LineInstruction::SetDiscriminator(42),
2385 );
2386
2387 let mut file = Vec::new();
2388 let path_name = [b'f', b'o', b'o', b'.', b'c', 0];
2390 file.extend_from_slice(&path_name);
2391 file.push(0);
2393 file.push(1);
2395 file.push(2);
2397
2398 test(
2399 constants::DW_LNE_define_file,
2400 file,
2401 LineInstruction::DefineFile(FileEntry {
2402 path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)),
2403 directory_index: 0,
2404 timestamp: 1,
2405 size: 2,
2406 md5: [0; 16],
2407 source: None,
2408 }),
2409 );
2410
2411 let operands = [1, 2, 3, 4, 5, 6];
2413 let opcode = constants::DwLne(99);
2414 test(
2415 opcode,
2416 operands,
2417 LineInstruction::UnknownExtended(opcode, EndianSlice::new(&operands, LittleEndian)),
2418 );
2419 }
2420
2421 #[test]
2422 fn test_file_entry_directory() {
2423 let path_name = [b'f', b'o', b'o', b'.', b'r', b's', 0];
2424
2425 let mut file = FileEntry {
2426 path_name: AttributeValue::String(EndianSlice::new(&path_name, LittleEndian)),
2427 directory_index: 1,
2428 timestamp: 0,
2429 size: 0,
2430 md5: [0; 16],
2431 source: None,
2432 };
2433
2434 let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
2435
2436 let dir = AttributeValue::String(EndianSlice::new(b"dir", LittleEndian));
2437 header.include_directories.push(dir);
2438
2439 assert_eq!(file.directory(&header), Some(dir));
2440
2441 file.directory_index = 0;
2443 assert_eq!(file.directory(&header), None);
2444 }
2445
2446 fn assert_exec_opcode<'input>(
2447 header: LineProgramHeader<EndianSlice<'input, LittleEndian>>,
2448 mut registers: LineRow,
2449 opcode: LineInstruction<EndianSlice<'input, LittleEndian>>,
2450 expected_registers: LineRow,
2451 expect_new_row: bool,
2452 ) {
2453 let mut program = IncompleteLineProgram { header };
2454 let is_new_row = registers.execute(opcode, &mut program);
2455
2456 assert_eq!(is_new_row, Ok(expect_new_row));
2457 assert_eq!(registers, expected_registers);
2458 }
2459
2460 #[test]
2461 fn test_exec_special_noop() {
2462 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2463
2464 let initial_registers = LineRow::new(&header);
2465 let opcode = LineInstruction::Special(16);
2466 let expected_registers = initial_registers;
2467
2468 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2469 }
2470
2471 #[test]
2472 fn test_exec_special_negative_line_advance() {
2473 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2474
2475 let mut initial_registers = LineRow::new(&header);
2476 initial_registers.line.0 = 10;
2477
2478 let opcode = LineInstruction::Special(13);
2479
2480 let mut expected_registers = initial_registers;
2481 expected_registers.line.0 -= 3;
2482
2483 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2484 }
2485
2486 #[test]
2487 fn test_exec_special_positive_line_advance() {
2488 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2489
2490 let initial_registers = LineRow::new(&header);
2491
2492 let opcode = LineInstruction::Special(19);
2493
2494 let mut expected_registers = initial_registers;
2495 expected_registers.line.0 += 3;
2496
2497 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2498 }
2499
2500 #[test]
2501 fn test_exec_special_positive_address_advance() {
2502 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2503
2504 let initial_registers = LineRow::new(&header);
2505
2506 let opcode = LineInstruction::Special(52);
2507
2508 let mut expected_registers = initial_registers;
2509 expected_registers.address += 3;
2510
2511 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2512 }
2513
2514 #[test]
2515 fn test_exec_special_positive_address_and_line_advance() {
2516 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2517
2518 let initial_registers = LineRow::new(&header);
2519
2520 let opcode = LineInstruction::Special(55);
2521
2522 let mut expected_registers = initial_registers;
2523 expected_registers.address += 3;
2524 expected_registers.line.0 += 3;
2525
2526 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2527 }
2528
2529 #[test]
2530 fn test_exec_special_positive_address_and_negative_line_advance() {
2531 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2532
2533 let mut initial_registers = LineRow::new(&header);
2534 initial_registers.line.0 = 10;
2535
2536 let opcode = LineInstruction::Special(49);
2537
2538 let mut expected_registers = initial_registers;
2539 expected_registers.address += 3;
2540 expected_registers.line.0 -= 3;
2541
2542 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2543 }
2544
2545 #[test]
2546 fn test_exec_special_line_underflow() {
2547 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2548
2549 let mut initial_registers = LineRow::new(&header);
2550 initial_registers.line.0 = 2;
2551
2552 let opcode = LineInstruction::Special(13);
2554
2555 let mut expected_registers = initial_registers;
2556 expected_registers.line.0 = 0;
2559
2560 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2561 }
2562
2563 #[test]
2564 fn test_exec_copy() {
2565 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2566
2567 let mut initial_registers = LineRow::new(&header);
2568 initial_registers.address = 1337;
2569 initial_registers.line.0 = 42;
2570
2571 let opcode = LineInstruction::Copy;
2572
2573 let expected_registers = initial_registers;
2574
2575 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2576 }
2577
2578 #[test]
2579 fn test_exec_advance_pc() {
2580 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2581 let initial_registers = LineRow::new(&header);
2582 let opcode = LineInstruction::AdvancePc(42);
2583
2584 let mut expected_registers = initial_registers;
2585 expected_registers.address += 42;
2586
2587 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2588 }
2589
2590 #[test]
2591 fn test_exec_advance_pc_overflow_32() {
2592 let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
2593 header.encoding.address_size = 4;
2594 let mut registers = LineRow::new(&header);
2595 registers.address = u32::MAX.into();
2596 let opcode = LineInstruction::AdvancePc(42);
2597 let mut program = IncompleteLineProgram { header };
2598 let result = registers.execute(opcode, &mut program);
2599 assert_eq!(result, Err(Error::AddressOverflow));
2600 }
2601
2602 #[test]
2603 fn test_exec_advance_pc_overflow_64() {
2604 let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
2605 header.encoding.address_size = 8;
2606 let mut registers = LineRow::new(&header);
2607 registers.address = u64::MAX;
2608 let opcode = LineInstruction::AdvancePc(42);
2609 let mut program = IncompleteLineProgram { header };
2610 let result = registers.execute(opcode, &mut program);
2611 assert_eq!(result, Err(Error::AddressOverflow));
2612 }
2613
2614 #[test]
2615 fn test_exec_advance_line() {
2616 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2617 let initial_registers = LineRow::new(&header);
2618 let opcode = LineInstruction::AdvanceLine(42);
2619
2620 let mut expected_registers = initial_registers;
2621 expected_registers.line.0 += 42;
2622
2623 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2624 }
2625
2626 #[test]
2627 fn test_exec_advance_line_overflow() {
2628 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2629 let opcode = LineInstruction::AdvanceLine(42);
2630
2631 let mut initial_registers = LineRow::new(&header);
2632 initial_registers.line.0 = u64::MAX;
2633
2634 let mut expected_registers = initial_registers;
2635 expected_registers.line.0 = 41;
2636
2637 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2638 }
2639
2640 #[test]
2641 fn test_exec_set_file_in_bounds() {
2642 for file_idx in 1..3 {
2643 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2644 let initial_registers = LineRow::new(&header);
2645 let opcode = LineInstruction::SetFile(file_idx);
2646
2647 let mut expected_registers = initial_registers;
2648 expected_registers.file = file_idx;
2649
2650 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2651 }
2652 }
2653
2654 #[test]
2655 fn test_exec_set_file_out_of_bounds() {
2656 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2657 let initial_registers = LineRow::new(&header);
2658 let opcode = LineInstruction::SetFile(100);
2659
2660 let mut expected_registers = initial_registers;
2667 expected_registers.file = 100;
2668
2669 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2670 }
2671
2672 #[test]
2673 fn test_file_entry_file_index_out_of_bounds() {
2674 let out_of_bounds_indices = [0, 100];
2677
2678 for file_idx in &out_of_bounds_indices[..] {
2679 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2680 let mut row = LineRow::new(&header);
2681
2682 row.file = *file_idx;
2683
2684 assert_eq!(row.file(&header), None);
2685 }
2686 }
2687
2688 #[test]
2689 fn test_file_entry_file_index_in_bounds() {
2690 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2691 let mut row = LineRow::new(&header);
2692
2693 row.file = 2;
2694
2695 assert_eq!(row.file(&header), Some(&header.file_names()[1]));
2696 }
2697
2698 #[test]
2699 fn test_exec_set_column() {
2700 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2701 let initial_registers = LineRow::new(&header);
2702 let opcode = LineInstruction::SetColumn(42);
2703
2704 let mut expected_registers = initial_registers;
2705 expected_registers.column = 42;
2706
2707 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2708 }
2709
2710 #[test]
2711 fn test_exec_negate_statement() {
2712 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2713 let initial_registers = LineRow::new(&header);
2714 let opcode = LineInstruction::NegateStatement;
2715
2716 let mut expected_registers = initial_registers;
2717 expected_registers.is_stmt = !initial_registers.is_stmt;
2718
2719 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2720 }
2721
2722 #[test]
2723 fn test_exec_set_basic_block() {
2724 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2725
2726 let mut initial_registers = LineRow::new(&header);
2727 initial_registers.basic_block = false;
2728
2729 let opcode = LineInstruction::SetBasicBlock;
2730
2731 let mut expected_registers = initial_registers;
2732 expected_registers.basic_block = true;
2733
2734 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2735 }
2736
2737 #[test]
2738 fn test_exec_const_add_pc() {
2739 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2740 let initial_registers = LineRow::new(&header);
2741 let opcode = LineInstruction::ConstAddPc;
2742
2743 let mut expected_registers = initial_registers;
2744 expected_registers.address += 20;
2745
2746 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2747 }
2748
2749 #[test]
2750 fn test_exec_const_add_pc_overflow() {
2751 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2752 let mut registers = LineRow::new(&header);
2753 registers.address = u64::MAX;
2754 let opcode = LineInstruction::ConstAddPc;
2755 let mut program = IncompleteLineProgram { header };
2756 let result = registers.execute(opcode, &mut program);
2757 assert_eq!(result, Err(Error::AddressOverflow));
2758 }
2759
2760 #[test]
2761 fn test_exec_fixed_add_pc() {
2762 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2763
2764 let mut initial_registers = LineRow::new(&header);
2765 initial_registers.op_index.0 = 1;
2766
2767 let opcode = LineInstruction::FixedAddPc(10);
2768
2769 let mut expected_registers = initial_registers;
2770 expected_registers.address += 10;
2771 expected_registers.op_index.0 = 0;
2772
2773 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2774 }
2775
2776 #[test]
2777 fn test_exec_fixed_add_pc_overflow() {
2778 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2779 let mut registers = LineRow::new(&header);
2780 registers.address = u64::MAX;
2781 registers.op_index.0 = 1;
2782 let opcode = LineInstruction::FixedAddPc(10);
2783 let mut program = IncompleteLineProgram { header };
2784 let result = registers.execute(opcode, &mut program);
2785 assert_eq!(result, Err(Error::AddressOverflow));
2786 }
2787
2788 #[test]
2789 fn test_exec_set_prologue_end() {
2790 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2791
2792 let mut initial_registers = LineRow::new(&header);
2793 initial_registers.prologue_end = false;
2794
2795 let opcode = LineInstruction::SetPrologueEnd;
2796
2797 let mut expected_registers = initial_registers;
2798 expected_registers.prologue_end = true;
2799
2800 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2801 }
2802
2803 #[test]
2804 fn test_exec_set_isa() {
2805 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2806 let initial_registers = LineRow::new(&header);
2807 let opcode = LineInstruction::SetIsa(1993);
2808
2809 let mut expected_registers = initial_registers;
2810 expected_registers.isa = 1993;
2811
2812 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2813 }
2814
2815 #[test]
2816 fn test_exec_unknown_standard_0() {
2817 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2818 let initial_registers = LineRow::new(&header);
2819 let opcode = LineInstruction::UnknownStandard0(constants::DwLns(111));
2820 let expected_registers = initial_registers;
2821 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2822 }
2823
2824 #[test]
2825 fn test_exec_unknown_standard_1() {
2826 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2827 let initial_registers = LineRow::new(&header);
2828 let opcode = LineInstruction::UnknownStandard1(constants::DwLns(111), 2);
2829 let expected_registers = initial_registers;
2830 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2831 }
2832
2833 #[test]
2834 fn test_exec_unknown_standard_n() {
2835 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2836 let initial_registers = LineRow::new(&header);
2837 let opcode = LineInstruction::UnknownStandardN(
2838 constants::DwLns(111),
2839 EndianSlice::new(&[2, 2, 2], LittleEndian),
2840 );
2841 let expected_registers = initial_registers;
2842 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2843 }
2844
2845 #[test]
2846 fn test_exec_end_sequence() {
2847 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2848 let initial_registers = LineRow::new(&header);
2849 let opcode = LineInstruction::EndSequence;
2850
2851 let mut expected_registers = initial_registers;
2852 expected_registers.end_sequence = true;
2853
2854 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2855 }
2856
2857 #[test]
2858 fn test_exec_set_address() {
2859 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2860 let initial_registers = LineRow::new(&header);
2861 let opcode = LineInstruction::SetAddress(3030);
2862
2863 let mut expected_registers = initial_registers;
2864 expected_registers.address = 3030;
2865
2866 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2867 }
2868
2869 #[test]
2870 fn test_exec_set_address_tombstone() {
2871 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2872 let initial_registers = LineRow::new(&header);
2873 let opcode = LineInstruction::SetAddress(!0);
2874
2875 let mut expected_registers = initial_registers;
2876 expected_registers.tombstone = true;
2877
2878 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2879 }
2880
2881 #[test]
2882 fn test_exec_set_address_backwards() {
2883 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2884 let mut initial_registers = LineRow::new(&header);
2885 initial_registers.address = 1;
2886 let opcode = LineInstruction::SetAddress(0);
2887
2888 let mut expected_registers = initial_registers;
2889 expected_registers.tombstone = true;
2890
2891 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2892 }
2893
2894 #[test]
2895 fn test_exec_define_file() {
2896 let mut program = make_test_program(EndianSlice::new(&[], LittleEndian));
2897 let mut row = LineRow::new(program.header());
2898
2899 let file = FileEntry {
2900 path_name: AttributeValue::String(EndianSlice::new(b"test.cpp", LittleEndian)),
2901 directory_index: 0,
2902 timestamp: 0,
2903 size: 0,
2904 md5: [0; 16],
2905 source: None,
2906 };
2907
2908 let opcode = LineInstruction::DefineFile(file);
2909 let is_new_row = row.execute(opcode, &mut program).unwrap();
2910
2911 assert!(!is_new_row);
2912 assert_eq!(Some(&file), program.header().file_names.last());
2913 }
2914
2915 #[test]
2916 fn test_exec_set_discriminator() {
2917 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2918 let initial_registers = LineRow::new(&header);
2919 let opcode = LineInstruction::SetDiscriminator(9);
2920
2921 let mut expected_registers = initial_registers;
2922 expected_registers.discriminator = 9;
2923
2924 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2925 }
2926
2927 #[test]
2928 fn test_exec_unknown_extended() {
2929 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2930 let initial_registers = LineRow::new(&header);
2931 let opcode = LineInstruction::UnknownExtended(
2932 constants::DwLne(74),
2933 EndianSlice::new(&[], LittleEndian),
2934 );
2935 let expected_registers = initial_registers;
2936 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2937 }
2938
2939 #[allow(dead_code, unreachable_code, unused_variables)]
2942 #[allow(clippy::diverging_sub_expression)]
2943 fn test_line_rows_variance<'a, 'b>(_: &'a [u8], _: &'b [u8])
2944 where
2945 'a: 'b,
2946 {
2947 let a: &OneShotLineRows<EndianSlice<'a, LittleEndian>> = unimplemented!();
2948 let _: &OneShotLineRows<EndianSlice<'b, LittleEndian>> = a;
2949 }
2950
2951 #[test]
2952 fn test_parse_debug_line_v5_ok() {
2953 let expected_lengths = &[1, 2];
2954 let expected_program = &[0, 1, 2, 3, 4];
2955 let expected_rest = &[5, 6, 7, 8, 9];
2956 let expected_include_directories = [
2957 AttributeValue::String(EndianSlice::new(b"dir1", LittleEndian)),
2958 AttributeValue::String(EndianSlice::new(b"dir2", LittleEndian)),
2959 ];
2960 let expected_file_names = [
2961 FileEntry {
2962 path_name: AttributeValue::String(EndianSlice::new(b"file1", LittleEndian)),
2963 directory_index: 0,
2964 timestamp: 0,
2965 size: 0,
2966 md5: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
2967 source: Some(AttributeValue::String(EndianSlice::new(
2968 b"foobar",
2969 LittleEndian,
2970 ))),
2971 },
2972 FileEntry {
2973 path_name: AttributeValue::String(EndianSlice::new(b"file2", LittleEndian)),
2974 directory_index: 1,
2975 timestamp: 0,
2976 size: 0,
2977 md5: [
2978 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
2979 ],
2980 source: Some(AttributeValue::String(EndianSlice::new(
2981 b"quux",
2982 LittleEndian,
2983 ))),
2984 },
2985 ];
2986
2987 for format in [Format::Dwarf32, Format::Dwarf64] {
2988 let length = Label::new();
2989 let header_length = Label::new();
2990 let start = Label::new();
2991 let header_start = Label::new();
2992 let end = Label::new();
2993 let header_end = Label::new();
2994 let section = Section::with_endian(Endian::Little)
2995 .initial_length(format, &length, &start)
2996 .D16(5)
2997 .D8(4)
2999 .D8(0)
3001 .word_label(format.word_size(), &header_length)
3002 .mark(&header_start)
3003 .D8(1)
3005 .D8(1)
3007 .D8(1)
3009 .D8(0)
3011 .D8(1)
3013 .D8(expected_lengths.len() as u8 + 1)
3015 .append_bytes(expected_lengths)
3017 .D8(1)
3019 .uleb(constants::DW_LNCT_path.0 as u64)
3020 .uleb(constants::DW_FORM_string.0 as u64)
3021 .D8(2)
3023 .append_bytes(b"dir1\0")
3024 .append_bytes(b"dir2\0")
3025 .D8(4)
3027 .uleb(constants::DW_LNCT_path.0 as u64)
3028 .uleb(constants::DW_FORM_string.0 as u64)
3029 .uleb(constants::DW_LNCT_directory_index.0 as u64)
3030 .uleb(constants::DW_FORM_data1.0 as u64)
3031 .uleb(constants::DW_LNCT_MD5.0 as u64)
3032 .uleb(constants::DW_FORM_data16.0 as u64)
3033 .uleb(constants::DW_LNCT_LLVM_source.0 as u64)
3034 .uleb(constants::DW_FORM_string.0 as u64)
3035 .D8(2)
3037 .append_bytes(b"file1\0")
3038 .D8(0)
3039 .append_bytes(&expected_file_names[0].md5)
3040 .append_bytes(b"foobar\0")
3041 .append_bytes(b"file2\0")
3042 .D8(1)
3043 .append_bytes(&expected_file_names[1].md5)
3044 .append_bytes(b"quux\0")
3045 .mark(&header_end)
3046 .append_bytes(expected_program)
3048 .mark(&end)
3049 .append_bytes(expected_rest);
3051 length.set_const((&end - &start) as u64);
3052 header_length.set_const((&header_end - &header_start) as u64);
3053 let section = section.get_contents().unwrap();
3054
3055 let input = &mut EndianSlice::new(§ion, LittleEndian);
3056
3057 let header = LineProgramHeader::parse(input, DebugLineOffset(0), 0, None, None)
3058 .expect("should parse header ok");
3059
3060 assert_eq!(header.raw_program_buf().slice(), expected_program);
3061 assert_eq!(input.slice(), expected_rest);
3062
3063 assert_eq!(header.offset, DebugLineOffset(0));
3064 assert_eq!(header.version(), 5);
3065 assert_eq!(header.address_size(), 4);
3066 assert_eq!(header.minimum_instruction_length(), 1);
3067 assert_eq!(header.maximum_operations_per_instruction(), 1);
3068 assert!(header.default_is_stmt());
3069 assert_eq!(header.line_base(), 0);
3070 assert_eq!(header.line_range(), 1);
3071 assert_eq!(header.opcode_base(), expected_lengths.len() as u8 + 1);
3072 assert_eq!(header.standard_opcode_lengths().slice(), expected_lengths);
3073 assert_eq!(
3074 header.directory_entry_format(),
3075 &[FileEntryFormat {
3076 content_type: constants::DW_LNCT_path,
3077 form: constants::DW_FORM_string,
3078 }]
3079 );
3080 assert_eq!(header.include_directories(), expected_include_directories);
3081 assert_eq!(header.directory(0), Some(expected_include_directories[0]));
3082 assert_eq!(
3083 header.file_name_entry_format(),
3084 &[
3085 FileEntryFormat {
3086 content_type: constants::DW_LNCT_path,
3087 form: constants::DW_FORM_string,
3088 },
3089 FileEntryFormat {
3090 content_type: constants::DW_LNCT_directory_index,
3091 form: constants::DW_FORM_data1,
3092 },
3093 FileEntryFormat {
3094 content_type: constants::DW_LNCT_MD5,
3095 form: constants::DW_FORM_data16,
3096 },
3097 FileEntryFormat {
3098 content_type: constants::DW_LNCT_LLVM_source,
3099 form: constants::DW_FORM_string,
3100 }
3101 ]
3102 );
3103 assert_eq!(header.file_names(), expected_file_names);
3104 assert_eq!(header.file(0), Some(&expected_file_names[0]));
3105 }
3106 }
3107
3108 #[test]
3109 fn test_sequences() {
3110 #[rustfmt::skip]
3111 let buf = [
3112 94, 0x00, 0x00, 0x00,
3114 0x04, 0x00,
3116 0x28, 0x00, 0x00, 0x00,
3118 0x01,
3120 0x01,
3122 0x01,
3124 0x00,
3126 0x01,
3128 0x03,
3130 0x01, 0x02,
3132 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
3134 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
3137 0x00,
3138 0x00,
3139 0x00,
3140 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
3142 0x01,
3143 0x00,
3144 0x00,
3145 0x00,
3147
3148 0, 5, constants::DW_LNE_set_address.0, 1, 0, 0, 0,
3149 constants::DW_LNS_copy.0,
3150 constants::DW_LNS_advance_pc.0, 1,
3151 constants::DW_LNS_copy.0,
3152 constants::DW_LNS_advance_pc.0, 2,
3153 0, 1, constants::DW_LNE_end_sequence.0,
3154
3155 0, 5, constants::DW_LNE_set_address.0, 0xff, 0xff, 0xff, 0xff,
3157 constants::DW_LNS_copy.0,
3158 constants::DW_LNS_advance_pc.0, 1,
3159 constants::DW_LNS_copy.0,
3160 constants::DW_LNS_advance_pc.0, 2,
3161 0, 1, constants::DW_LNE_end_sequence.0,
3162
3163 0, 5, constants::DW_LNE_set_address.0, 11, 0, 0, 0,
3164 constants::DW_LNS_copy.0,
3165 constants::DW_LNS_advance_pc.0, 1,
3166 constants::DW_LNS_copy.0,
3167 constants::DW_LNS_advance_pc.0, 2,
3168 0, 1, constants::DW_LNE_end_sequence.0,
3169 ];
3170 assert_eq!(buf[0] as usize, buf.len() - 4);
3171
3172 let rest = &mut EndianSlice::new(&buf, LittleEndian);
3173
3174 let header = LineProgramHeader::parse(rest, DebugLineOffset(0), 4, None, None)
3175 .expect("should parse header ok");
3176 let program = IncompleteLineProgram { header };
3177
3178 let sequences = program.sequences().unwrap().1;
3179 assert_eq!(sequences.len(), 2);
3180 assert_eq!(sequences[0].start, 1);
3181 assert_eq!(sequences[0].end, 4);
3182 assert_eq!(sequences[1].start, 11);
3183 assert_eq!(sequences[1].end, 14);
3184 }
3185}