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
114pub trait LineProgram<R, Offset = <R as Reader>::Offset>
118where
119 R: Reader<Offset = Offset>,
120 Offset: ReaderOffset,
121{
122 fn header(&self) -> &LineProgramHeader<R, Offset>;
124 fn add_file(&mut self, file: FileEntry<R, Offset>);
126}
127
128impl<R, Offset> LineProgram<R, Offset> for IncompleteLineProgram<R, Offset>
129where
130 R: Reader<Offset = Offset>,
131 Offset: ReaderOffset,
132{
133 fn header(&self) -> &LineProgramHeader<R, Offset> {
134 &self.header
135 }
136 fn add_file(&mut self, file: FileEntry<R, Offset>) {
137 self.header.file_names.push(file);
138 }
139}
140
141impl<'program, R, Offset> LineProgram<R, Offset> for &'program CompleteLineProgram<R, Offset>
142where
143 R: Reader<Offset = Offset>,
144 Offset: ReaderOffset,
145{
146 fn header(&self) -> &LineProgramHeader<R, Offset> {
147 &self.header
148 }
149 fn add_file(&mut self, _: FileEntry<R, Offset>) {
150 }
152}
153
154#[derive(Debug, Clone)]
160pub struct LineRows<R, Program, Offset = <R as Reader>::Offset>
161where
162 Program: LineProgram<R, Offset>,
163 R: Reader<Offset = Offset>,
164 Offset: ReaderOffset,
165{
166 program: Program,
167 row: LineRow,
168 instructions: LineInstructions<R>,
169}
170
171type OneShotLineRows<R, Offset = <R as Reader>::Offset> =
172 LineRows<R, IncompleteLineProgram<R, Offset>, Offset>;
173
174type ResumedLineRows<'program, R, Offset = <R as Reader>::Offset> =
175 LineRows<R, &'program CompleteLineProgram<R, Offset>, Offset>;
176
177impl<R, Program, Offset> LineRows<R, Program, Offset>
178where
179 Program: LineProgram<R, Offset>,
180 R: Reader<Offset = Offset>,
181 Offset: ReaderOffset,
182{
183 fn new(program: IncompleteLineProgram<R, Offset>) -> OneShotLineRows<R, Offset> {
184 let row = LineRow::new(program.header());
185 let instructions = LineInstructions {
186 input: program.header().program_buf.clone(),
187 };
188 LineRows {
189 program,
190 row,
191 instructions,
192 }
193 }
194
195 fn resume<'program>(
196 program: &'program CompleteLineProgram<R, Offset>,
197 sequence: &LineSequence<R>,
198 ) -> ResumedLineRows<'program, R, Offset> {
199 let row = LineRow::new(program.header());
200 let instructions = sequence.instructions.clone();
201 LineRows {
202 program,
203 row,
204 instructions,
205 }
206 }
207
208 #[inline]
211 pub fn header(&self) -> &LineProgramHeader<R, Offset> {
212 self.program.header()
213 }
214
215 pub fn next_row(&mut self) -> Result<Option<(&LineProgramHeader<R, Offset>, &LineRow)>> {
226 self.row.reset(self.program.header());
228
229 loop {
230 match self.instructions.next_instruction(self.program.header()) {
232 Err(err) => return Err(err),
233 Ok(None) => return Ok(None),
234 Ok(Some(instruction)) => {
235 if self.row.execute(instruction, &mut self.program)? {
236 if self.row.tombstone {
237 self.row.reset(self.program.header());
241 } else {
242 return Ok(Some((self.header(), &self.row)));
243 }
244 }
245 }
248 }
249 }
250 }
251}
252
253#[derive(Clone, Copy, Debug, PartialEq, Eq)]
255pub enum LineInstruction<R, Offset = <R as Reader>::Offset>
256where
257 R: Reader<Offset = Offset>,
258 Offset: ReaderOffset,
259{
260 Special(u8),
284
285 Copy,
290
291 AdvancePc(u64),
295
296 AdvanceLine(i64),
299
300 SetFile(u64),
303
304 SetColumn(u64),
307
308 NegateStatement,
312
313 SetBasicBlock,
316
317 ConstAddPc,
329
330 FixedAddPc(u16),
336
337 SetPrologueEnd,
339
340 SetEpilogueBegin,
343
344 SetIsa(u64),
347
348 UnknownStandard0(constants::DwLns),
350
351 UnknownStandard1(constants::DwLns, u64),
353
354 UnknownStandardN(constants::DwLns, R),
356
357 EndSequence,
365
366 SetAddress(u64),
375
376 DefineFile(FileEntry<R, Offset>),
379
380 SetDiscriminator(u64),
384
385 UnknownExtended(constants::DwLne, R),
387}
388
389impl<R, Offset> LineInstruction<R, Offset>
390where
391 R: Reader<Offset = Offset>,
392 Offset: ReaderOffset,
393{
394 fn parse<'header>(
395 header: &'header LineProgramHeader<R>,
396 input: &mut R,
397 ) -> Result<LineInstruction<R>>
398 where
399 R: 'header,
400 {
401 let opcode = input.read_u8()?;
402 if opcode == 0 {
403 let length = input.read_uleb128().and_then(R::Offset::from_u64)?;
404 let mut instr_rest = input.split(length)?;
405 let opcode = instr_rest.read_u8()?;
406
407 match constants::DwLne(opcode) {
408 constants::DW_LNE_end_sequence => Ok(LineInstruction::EndSequence),
409
410 constants::DW_LNE_set_address => {
411 let address = instr_rest.read_address(header.address_size())?;
412 Ok(LineInstruction::SetAddress(address))
413 }
414
415 constants::DW_LNE_define_file => {
416 if header.version() <= 4 {
417 let path_name = instr_rest.read_null_terminated_slice()?;
418 let entry = FileEntry::parse(&mut instr_rest, path_name)?;
419 Ok(LineInstruction::DefineFile(entry))
420 } else {
421 Ok(LineInstruction::UnknownExtended(
422 constants::DW_LNE_define_file,
423 instr_rest,
424 ))
425 }
426 }
427
428 constants::DW_LNE_set_discriminator => {
429 let discriminator = instr_rest.read_uleb128()?;
430 Ok(LineInstruction::SetDiscriminator(discriminator))
431 }
432
433 otherwise => Ok(LineInstruction::UnknownExtended(otherwise, instr_rest)),
434 }
435 } else if opcode >= header.opcode_base {
436 Ok(LineInstruction::Special(opcode))
437 } else {
438 match constants::DwLns(opcode) {
439 constants::DW_LNS_copy => Ok(LineInstruction::Copy),
440
441 constants::DW_LNS_advance_pc => {
442 let advance = input.read_uleb128()?;
443 Ok(LineInstruction::AdvancePc(advance))
444 }
445
446 constants::DW_LNS_advance_line => {
447 let increment = input.read_sleb128()?;
448 Ok(LineInstruction::AdvanceLine(increment))
449 }
450
451 constants::DW_LNS_set_file => {
452 let file = input.read_uleb128()?;
453 Ok(LineInstruction::SetFile(file))
454 }
455
456 constants::DW_LNS_set_column => {
457 let column = input.read_uleb128()?;
458 Ok(LineInstruction::SetColumn(column))
459 }
460
461 constants::DW_LNS_negate_stmt => Ok(LineInstruction::NegateStatement),
462
463 constants::DW_LNS_set_basic_block => Ok(LineInstruction::SetBasicBlock),
464
465 constants::DW_LNS_const_add_pc => Ok(LineInstruction::ConstAddPc),
466
467 constants::DW_LNS_fixed_advance_pc => {
468 let advance = input.read_u16()?;
469 Ok(LineInstruction::FixedAddPc(advance))
470 }
471
472 constants::DW_LNS_set_prologue_end => Ok(LineInstruction::SetPrologueEnd),
473
474 constants::DW_LNS_set_epilogue_begin => Ok(LineInstruction::SetEpilogueBegin),
475
476 constants::DW_LNS_set_isa => {
477 let isa = input.read_uleb128()?;
478 Ok(LineInstruction::SetIsa(isa))
479 }
480
481 otherwise => {
482 let mut opcode_lengths = header.standard_opcode_lengths().clone();
483 opcode_lengths.skip(R::Offset::from_u8(opcode - 1))?;
484 let num_args = opcode_lengths.read_u8()? as usize;
485 match num_args {
486 0 => Ok(LineInstruction::UnknownStandard0(otherwise)),
487 1 => {
488 let arg = input.read_uleb128()?;
489 Ok(LineInstruction::UnknownStandard1(otherwise, arg))
490 }
491 _ => {
492 let mut args = input.clone();
493 for _ in 0..num_args {
494 input.read_uleb128()?;
495 }
496 let len = input.offset_from(&args);
497 args.truncate(len)?;
498 Ok(LineInstruction::UnknownStandardN(otherwise, args))
499 }
500 }
501 }
502 }
503 }
504 }
505}
506
507#[derive(Clone, Debug)]
513pub struct LineInstructions<R: Reader> {
514 input: R,
515}
516
517impl<R: Reader> LineInstructions<R> {
518 fn remove_trailing(&self, other: &LineInstructions<R>) -> Result<LineInstructions<R>> {
519 let offset = other.input.offset_from(&self.input);
520 let mut input = self.input.clone();
521 input.truncate(offset)?;
522 Ok(LineInstructions { input })
523 }
524}
525
526impl<R: Reader> LineInstructions<R> {
527 #[inline(always)]
538 pub fn next_instruction(
539 &mut self,
540 header: &LineProgramHeader<R>,
541 ) -> Result<Option<LineInstruction<R>>> {
542 if self.input.is_empty() {
543 return Ok(None);
544 }
545
546 match LineInstruction::parse(header, &mut self.input) {
547 Ok(instruction) => Ok(Some(instruction)),
548 Err(e) => {
549 self.input.empty();
550 Err(e)
551 }
552 }
553 }
554}
555
556#[derive(Clone, Copy, Debug, PartialEq, Eq)]
560pub struct LineRow {
561 tombstone: bool,
562 address: u64,
563 op_index: Wrapping<u64>,
564 file: u64,
565 line: Wrapping<u64>,
566 column: u64,
567 is_stmt: bool,
568 basic_block: bool,
569 end_sequence: bool,
570 prologue_end: bool,
571 epilogue_begin: bool,
572 isa: u64,
573 discriminator: u64,
574}
575
576impl LineRow {
577 pub fn new<R: Reader>(header: &LineProgramHeader<R>) -> Self {
579 LineRow {
580 tombstone: false,
583 address: 0,
584 op_index: Wrapping(0),
585 file: 1,
586 line: Wrapping(1),
587 column: 0,
588 is_stmt: header.line_encoding.default_is_stmt,
590 basic_block: false,
591 end_sequence: false,
592 prologue_end: false,
593 epilogue_begin: false,
594 isa: 0,
599 discriminator: 0,
600 }
601 }
602
603 #[inline]
606 pub fn address(&self) -> u64 {
607 self.address
608 }
609
610 #[inline]
618 pub fn op_index(&self) -> u64 {
619 self.op_index.0
620 }
621
622 #[inline]
625 pub fn file_index(&self) -> u64 {
626 self.file
627 }
628
629 #[inline]
631 pub fn file<'header, R: Reader>(
632 &self,
633 header: &'header LineProgramHeader<R>,
634 ) -> Option<&'header FileEntry<R>> {
635 header.file(self.file)
636 }
637
638 #[inline]
643 pub fn line(&self) -> Option<NonZeroU64> {
644 NonZeroU64::new(self.line.0)
645 }
646
647 #[inline]
651 pub fn column(&self) -> ColumnType {
652 NonZeroU64::new(self.column)
653 .map(ColumnType::Column)
654 .unwrap_or(ColumnType::LeftEdge)
655 }
656
657 #[inline]
662 pub fn is_stmt(&self) -> bool {
663 self.is_stmt
664 }
665
666 #[inline]
669 pub fn basic_block(&self) -> bool {
670 self.basic_block
671 }
672
673 #[inline]
678 pub fn end_sequence(&self) -> bool {
679 self.end_sequence
680 }
681
682 #[inline]
686 pub fn prologue_end(&self) -> bool {
687 self.prologue_end
688 }
689
690 #[inline]
694 pub fn epilogue_begin(&self) -> bool {
695 self.epilogue_begin
696 }
697
698 #[inline]
707 pub fn isa(&self) -> u64 {
708 self.isa
709 }
710
711 #[inline]
718 pub fn discriminator(&self) -> u64 {
719 self.discriminator
720 }
721
722 #[inline]
727 pub fn execute<R, Program>(
728 &mut self,
729 instruction: LineInstruction<R>,
730 program: &mut Program,
731 ) -> Result<bool>
732 where
733 Program: LineProgram<R>,
734 R: Reader,
735 {
736 Ok(match instruction {
737 LineInstruction::Special(opcode) => {
738 self.exec_special_opcode(opcode, program.header())?;
739 true
740 }
741
742 LineInstruction::Copy => true,
743
744 LineInstruction::AdvancePc(operation_advance) => {
745 self.apply_operation_advance(operation_advance, program.header())?;
746 false
747 }
748
749 LineInstruction::AdvanceLine(line_increment) => {
750 self.apply_line_advance(line_increment);
751 false
752 }
753
754 LineInstruction::SetFile(file) => {
755 self.file = file;
756 false
757 }
758
759 LineInstruction::SetColumn(column) => {
760 self.column = column;
761 false
762 }
763
764 LineInstruction::NegateStatement => {
765 self.is_stmt = !self.is_stmt;
766 false
767 }
768
769 LineInstruction::SetBasicBlock => {
770 self.basic_block = true;
771 false
772 }
773
774 LineInstruction::ConstAddPc => {
775 let adjusted = self.adjust_opcode(255, program.header());
776 let operation_advance = adjusted / program.header().line_encoding.line_range;
777 self.apply_operation_advance(u64::from(operation_advance), program.header())?;
778 false
779 }
780
781 LineInstruction::FixedAddPc(operand) => {
782 if !self.tombstone {
783 let address_size = program.header().address_size();
784 self.address = self.address.add_sized(u64::from(operand), address_size)?;
785 self.op_index.0 = 0;
786 }
787 false
788 }
789
790 LineInstruction::SetPrologueEnd => {
791 self.prologue_end = true;
792 false
793 }
794
795 LineInstruction::SetEpilogueBegin => {
796 self.epilogue_begin = true;
797 false
798 }
799
800 LineInstruction::SetIsa(isa) => {
801 self.isa = isa;
802 false
803 }
804
805 LineInstruction::EndSequence => {
806 self.end_sequence = true;
807 true
808 }
809
810 LineInstruction::SetAddress(address) => {
811 self.tombstone = address < self.address
819 || address >= u64::min_tombstone(program.header().encoding.address_size);
820 if !self.tombstone {
821 self.address = address;
822 self.op_index.0 = 0;
823 }
824 false
825 }
826
827 LineInstruction::DefineFile(entry) => {
828 program.add_file(entry);
829 false
830 }
831
832 LineInstruction::SetDiscriminator(discriminator) => {
833 self.discriminator = discriminator;
834 false
835 }
836
837 LineInstruction::UnknownStandard0(_)
839 | LineInstruction::UnknownStandard1(_, _)
840 | LineInstruction::UnknownStandardN(_, _)
841 | LineInstruction::UnknownExtended(_, _) => false,
842 })
843 }
844
845 #[inline]
847 pub fn reset<R: Reader>(&mut self, header: &LineProgramHeader<R>) {
848 if self.end_sequence {
849 *self = Self::new(header);
852 } else {
853 self.discriminator = 0;
858 self.basic_block = false;
859 self.prologue_end = false;
860 self.epilogue_begin = false;
861 }
862 }
863
864 fn apply_line_advance(&mut self, line_increment: i64) {
866 if line_increment < 0 {
867 let decrement = -line_increment as u64;
868 if decrement <= self.line.0 {
869 self.line.0 -= decrement;
870 } else {
871 self.line.0 = 0;
872 }
873 } else {
874 self.line += Wrapping(line_increment as u64);
875 }
876 }
877
878 fn apply_operation_advance<R: Reader>(
880 &mut self,
881 operation_advance: u64,
882 header: &LineProgramHeader<R>,
883 ) -> Result<()> {
884 if self.tombstone {
885 return Ok(());
886 }
887
888 let operation_advance = Wrapping(operation_advance);
889
890 let minimum_instruction_length = u64::from(header.line_encoding.minimum_instruction_length);
891 let minimum_instruction_length = Wrapping(minimum_instruction_length);
892
893 let maximum_operations_per_instruction =
894 u64::from(header.line_encoding.maximum_operations_per_instruction);
895 let maximum_operations_per_instruction = Wrapping(maximum_operations_per_instruction);
896
897 let address_advance = if maximum_operations_per_instruction.0 == 1 {
898 self.op_index.0 = 0;
899 minimum_instruction_length * operation_advance
900 } else {
901 let op_index_with_advance = self.op_index + operation_advance;
902 self.op_index = op_index_with_advance % maximum_operations_per_instruction;
903 minimum_instruction_length
904 * (op_index_with_advance / maximum_operations_per_instruction)
905 };
906 self.address = self
907 .address
908 .add_sized(address_advance.0, header.address_size())?;
909 Ok(())
910 }
911
912 #[inline]
913 fn adjust_opcode<R: Reader>(&self, opcode: u8, header: &LineProgramHeader<R>) -> u8 {
914 opcode - header.opcode_base
915 }
916
917 fn exec_special_opcode<R: Reader>(
919 &mut self,
920 opcode: u8,
921 header: &LineProgramHeader<R>,
922 ) -> Result<()> {
923 let adjusted_opcode = self.adjust_opcode(opcode, header);
924
925 let line_range = header.line_encoding.line_range;
926 let line_advance = adjusted_opcode % line_range;
927 let operation_advance = adjusted_opcode / line_range;
928
929 let line_base = i64::from(header.line_encoding.line_base);
931 self.apply_line_advance(line_base + i64::from(line_advance));
932
933 self.apply_operation_advance(u64::from(operation_advance), header)?;
935 Ok(())
936 }
937}
938
939#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
941pub enum ColumnType {
942 LeftEdge,
945 Column(NonZeroU64),
947}
948
949#[derive(Clone, Debug)]
953pub struct LineSequence<R: Reader> {
954 pub start: u64,
957 pub end: u64,
960 instructions: LineInstructions<R>,
961}
962
963#[derive(Clone, Debug, Eq, PartialEq)]
966pub struct LineProgramHeader<R, Offset = <R as Reader>::Offset>
967where
968 R: Reader<Offset = Offset>,
969 Offset: ReaderOffset,
970{
971 encoding: Encoding,
972 offset: DebugLineOffset<Offset>,
973 unit_length: Offset,
974
975 header_length: Offset,
976
977 line_encoding: LineEncoding,
978
979 opcode_base: u8,
981
982 standard_opcode_lengths: R,
987
988 directory_entry_format: Vec<FileEntryFormat>,
990
991 include_directories: Vec<AttributeValue<R, Offset>>,
1000
1001 file_name_entry_format: Vec<FileEntryFormat>,
1003
1004 file_names: Vec<FileEntry<R, Offset>>,
1008
1009 program_buf: R,
1011
1012 comp_dir: Option<R>,
1014
1015 comp_file: Option<FileEntry<R, Offset>>,
1017}
1018
1019impl<R, Offset> LineProgramHeader<R, Offset>
1020where
1021 R: Reader<Offset = Offset>,
1022 Offset: ReaderOffset,
1023{
1024 pub fn offset(&self) -> DebugLineOffset<R::Offset> {
1026 self.offset
1027 }
1028
1029 pub fn unit_length(&self) -> R::Offset {
1032 self.unit_length
1033 }
1034
1035 pub fn encoding(&self) -> Encoding {
1037 self.encoding
1038 }
1039
1040 pub fn version(&self) -> u16 {
1042 self.encoding.version
1043 }
1044
1045 pub fn header_length(&self) -> R::Offset {
1048 self.header_length
1049 }
1050
1051 pub fn address_size(&self) -> u8 {
1053 self.encoding.address_size
1054 }
1055
1056 pub fn format(&self) -> Format {
1058 self.encoding.format
1059 }
1060
1061 pub fn line_encoding(&self) -> LineEncoding {
1063 self.line_encoding
1064 }
1065
1066 pub fn minimum_instruction_length(&self) -> u8 {
1069 self.line_encoding.minimum_instruction_length
1070 }
1071
1072 pub fn maximum_operations_per_instruction(&self) -> u8 {
1075 self.line_encoding.maximum_operations_per_instruction
1076 }
1077
1078 pub fn default_is_stmt(&self) -> bool {
1081 self.line_encoding.default_is_stmt
1082 }
1083
1084 pub fn line_base(&self) -> i8 {
1086 self.line_encoding.line_base
1087 }
1088
1089 pub fn line_range(&self) -> u8 {
1091 self.line_encoding.line_range
1092 }
1093
1094 pub fn opcode_base(&self) -> u8 {
1096 self.opcode_base
1097 }
1098
1099 pub fn standard_opcode_lengths(&self) -> &R {
1102 &self.standard_opcode_lengths
1103 }
1104
1105 pub fn directory_entry_format(&self) -> &[FileEntryFormat] {
1107 &self.directory_entry_format[..]
1108 }
1109
1110 pub fn include_directories(&self) -> &[AttributeValue<R, Offset>] {
1115 &self.include_directories[..]
1116 }
1117
1118 pub fn directory(&self, directory: u64) -> Option<AttributeValue<R, Offset>> {
1122 if self.encoding.version <= 4 {
1123 if directory == 0 {
1124 self.comp_dir.clone().map(AttributeValue::String)
1125 } else {
1126 let directory = directory as usize - 1;
1127 self.include_directories.get(directory).cloned()
1128 }
1129 } else {
1130 self.include_directories.get(directory as usize).cloned()
1131 }
1132 }
1133
1134 pub fn file_name_entry_format(&self) -> &[FileEntryFormat] {
1136 &self.file_name_entry_format[..]
1137 }
1138
1139 pub fn file_has_timestamp(&self) -> bool {
1144 self.encoding.version <= 4
1145 || self
1146 .file_name_entry_format
1147 .iter()
1148 .any(|x| x.content_type == constants::DW_LNCT_timestamp)
1149 }
1150
1151 pub fn file_has_size(&self) -> bool {
1156 self.encoding.version <= 4
1157 || self
1158 .file_name_entry_format
1159 .iter()
1160 .any(|x| x.content_type == constants::DW_LNCT_size)
1161 }
1162
1163 pub fn file_has_md5(&self) -> bool {
1165 self.file_name_entry_format
1166 .iter()
1167 .any(|x| x.content_type == constants::DW_LNCT_MD5)
1168 }
1169
1170 pub fn file_has_source(&self) -> bool {
1172 self.file_name_entry_format
1173 .iter()
1174 .any(|x| x.content_type == constants::DW_LNCT_LLVM_source)
1175 }
1176
1177 pub fn file_names(&self) -> &[FileEntry<R, Offset>] {
1179 &self.file_names[..]
1180 }
1181
1182 pub fn file(&self, file: u64) -> Option<&FileEntry<R, Offset>> {
1188 if self.encoding.version <= 4 {
1189 if file == 0 {
1190 self.comp_file.as_ref()
1191 } else {
1192 let file = file as usize - 1;
1193 self.file_names.get(file)
1194 }
1195 } else {
1196 self.file_names.get(file as usize)
1197 }
1198 }
1199
1200 pub fn raw_program_buf(&self) -> R {
1219 self.program_buf.clone()
1220 }
1221
1222 pub fn instructions(&self) -> LineInstructions<R> {
1225 LineInstructions {
1226 input: self.program_buf.clone(),
1227 }
1228 }
1229
1230 fn parse(
1231 input: &mut R,
1232 offset: DebugLineOffset<Offset>,
1233 mut address_size: u8,
1234 mut comp_dir: Option<R>,
1235 comp_name: Option<R>,
1236 ) -> Result<LineProgramHeader<R, Offset>> {
1237 let (unit_length, format) = input.read_initial_length()?;
1238 let rest = &mut input.split(unit_length)?;
1239
1240 let version = rest.read_u16()?;
1241 if version < 2 || version > 5 {
1242 return Err(Error::UnknownVersion(u64::from(version)));
1243 }
1244
1245 if version >= 5 {
1246 address_size = rest.read_address_size()?;
1247 let segment_selector_size = rest.read_u8()?;
1248 if segment_selector_size != 0 {
1249 return Err(Error::UnsupportedSegmentSize(segment_selector_size));
1250 }
1251 }
1252
1253 let encoding = Encoding {
1254 format,
1255 version,
1256 address_size,
1257 };
1258
1259 let header_length = rest.read_length(format)?;
1260
1261 let mut program_buf = rest.clone();
1262 program_buf.skip(header_length)?;
1263 rest.truncate(header_length)?;
1264
1265 let minimum_instruction_length = rest.read_u8()?;
1266 if minimum_instruction_length == 0 {
1267 return Err(Error::MinimumInstructionLengthZero);
1268 }
1269
1270 let maximum_operations_per_instruction = if version >= 4 { rest.read_u8()? } else { 1 };
1273 if maximum_operations_per_instruction == 0 {
1274 return Err(Error::MaximumOperationsPerInstructionZero);
1275 }
1276
1277 let default_is_stmt = rest.read_u8()? != 0;
1278 let line_base = rest.read_i8()?;
1279 let line_range = rest.read_u8()?;
1280 if line_range == 0 {
1281 return Err(Error::LineRangeZero);
1282 }
1283 let line_encoding = LineEncoding {
1284 minimum_instruction_length,
1285 maximum_operations_per_instruction,
1286 default_is_stmt,
1287 line_base,
1288 line_range,
1289 };
1290
1291 let opcode_base = rest.read_u8()?;
1292 if opcode_base == 0 {
1293 return Err(Error::OpcodeBaseZero);
1294 }
1295
1296 let standard_opcode_count = R::Offset::from_u8(opcode_base - 1);
1297 let standard_opcode_lengths = rest.split(standard_opcode_count)?;
1298
1299 let directory_entry_format;
1300 let mut include_directories = Vec::new();
1301 if version <= 4 {
1302 directory_entry_format = Vec::new();
1303 loop {
1304 let directory = rest.read_null_terminated_slice()?;
1305 if directory.is_empty() {
1306 break;
1307 }
1308 include_directories.push(AttributeValue::String(directory));
1309 }
1310 } else {
1311 comp_dir = None;
1312 directory_entry_format = FileEntryFormat::parse(rest)?;
1313 let count = rest.read_uleb128()?;
1314 for _ in 0..count {
1315 include_directories.push(parse_directory_v5(
1316 rest,
1317 encoding,
1318 &directory_entry_format,
1319 )?);
1320 }
1321 }
1322
1323 let comp_file;
1324 let file_name_entry_format;
1325 let mut file_names = Vec::new();
1326 if version <= 4 {
1327 comp_file = comp_name.map(|name| FileEntry {
1328 path_name: AttributeValue::String(name),
1329 directory_index: 0,
1330 timestamp: 0,
1331 size: 0,
1332 md5: [0; 16],
1333 source: None,
1334 });
1335
1336 file_name_entry_format = Vec::new();
1337 loop {
1338 let path_name = rest.read_null_terminated_slice()?;
1339 if path_name.is_empty() {
1340 break;
1341 }
1342 file_names.push(FileEntry::parse(rest, path_name)?);
1343 }
1344 } else {
1345 comp_file = None;
1346 file_name_entry_format = FileEntryFormat::parse(rest)?;
1347 let count = rest.read_uleb128()?;
1348 for _ in 0..count {
1349 file_names.push(parse_file_v5(rest, encoding, &file_name_entry_format)?);
1350 }
1351 }
1352
1353 let header = LineProgramHeader {
1354 encoding,
1355 offset,
1356 unit_length,
1357 header_length,
1358 line_encoding,
1359 opcode_base,
1360 standard_opcode_lengths,
1361 directory_entry_format,
1362 include_directories,
1363 file_name_entry_format,
1364 file_names,
1365 program_buf,
1366 comp_dir,
1367 comp_file,
1368 };
1369 Ok(header)
1370 }
1371}
1372
1373#[derive(Clone, Debug, Eq, PartialEq)]
1375pub struct IncompleteLineProgram<R, Offset = <R as Reader>::Offset>
1376where
1377 R: Reader<Offset = Offset>,
1378 Offset: ReaderOffset,
1379{
1380 header: LineProgramHeader<R, Offset>,
1381}
1382
1383impl<R, Offset> IncompleteLineProgram<R, Offset>
1384where
1385 R: Reader<Offset = Offset>,
1386 Offset: ReaderOffset,
1387{
1388 pub fn header(&self) -> &LineProgramHeader<R, Offset> {
1390 &self.header
1391 }
1392
1393 pub fn rows(self) -> OneShotLineRows<R, Offset> {
1396 OneShotLineRows::new(self)
1397 }
1398
1399 #[allow(clippy::type_complexity)]
1420 pub fn sequences(self) -> Result<(CompleteLineProgram<R, Offset>, Vec<LineSequence<R>>)> {
1421 let mut sequences = Vec::new();
1422 let mut rows = self.rows();
1423 let mut instructions = rows.instructions.clone();
1424 let mut sequence_start_addr = None;
1425 loop {
1426 let sequence_end_addr;
1427 if rows.next_row()?.is_none() {
1428 break;
1429 }
1430
1431 let row = &rows.row;
1432 if row.end_sequence() {
1433 sequence_end_addr = row.address();
1434 } else if sequence_start_addr.is_none() {
1435 sequence_start_addr = Some(row.address());
1436 continue;
1437 } else {
1438 continue;
1439 }
1440
1441 sequences.push(LineSequence {
1443 start: sequence_start_addr.unwrap_or(0),
1446 end: sequence_end_addr,
1447 instructions: instructions.remove_trailing(&rows.instructions)?,
1448 });
1449 sequence_start_addr = None;
1450 instructions = rows.instructions.clone();
1451 }
1452
1453 let program = CompleteLineProgram {
1454 header: rows.program.header,
1455 };
1456 Ok((program, sequences))
1457 }
1458}
1459
1460#[derive(Clone, Debug, Eq, PartialEq)]
1462pub struct CompleteLineProgram<R, Offset = <R as Reader>::Offset>
1463where
1464 R: Reader<Offset = Offset>,
1465 Offset: ReaderOffset,
1466{
1467 header: LineProgramHeader<R, Offset>,
1468}
1469
1470impl<R, Offset> CompleteLineProgram<R, Offset>
1471where
1472 R: Reader<Offset = Offset>,
1473 Offset: ReaderOffset,
1474{
1475 pub fn header(&self) -> &LineProgramHeader<R, Offset> {
1477 &self.header
1478 }
1479
1480 pub fn resume_from<'program>(
1502 &'program self,
1503 sequence: &LineSequence<R>,
1504 ) -> ResumedLineRows<'program, R, Offset> {
1505 ResumedLineRows::resume(self, sequence)
1506 }
1507}
1508
1509#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1511pub struct FileEntry<R, Offset = <R as Reader>::Offset>
1512where
1513 R: Reader<Offset = Offset>,
1514 Offset: ReaderOffset,
1515{
1516 path_name: AttributeValue<R, Offset>,
1517 directory_index: u64,
1518 timestamp: u64,
1519 size: u64,
1520 md5: [u8; 16],
1521 source: Option<AttributeValue<R, Offset>>,
1522}
1523
1524impl<R, Offset> FileEntry<R, Offset>
1525where
1526 R: Reader<Offset = Offset>,
1527 Offset: ReaderOffset,
1528{
1529 fn parse(input: &mut R, path_name: R) -> Result<FileEntry<R, Offset>> {
1531 let directory_index = input.read_uleb128()?;
1532 let timestamp = input.read_uleb128()?;
1533 let size = input.read_uleb128()?;
1534
1535 let entry = FileEntry {
1536 path_name: AttributeValue::String(path_name),
1537 directory_index,
1538 timestamp,
1539 size,
1540 md5: [0; 16],
1541 source: None,
1542 };
1543
1544 Ok(entry)
1545 }
1546
1547 pub fn path_name(&self) -> AttributeValue<R, Offset> {
1553 self.path_name.clone()
1554 }
1555
1556 pub fn directory_index(&self) -> u64 {
1568 self.directory_index
1569 }
1570
1571 pub fn directory(&self, header: &LineProgramHeader<R>) -> Option<AttributeValue<R, Offset>> {
1575 header.directory(self.directory_index)
1576 }
1577
1578 pub fn timestamp(&self) -> u64 {
1581 self.timestamp
1582 }
1583
1584 #[doc(hidden)]
1588 pub fn last_modification(&self) -> u64 {
1589 self.timestamp
1590 }
1591
1592 pub fn size(&self) -> u64 {
1594 self.size
1595 }
1596
1597 #[doc(hidden)]
1601 pub fn length(&self) -> u64 {
1602 self.size
1603 }
1604
1605 pub fn md5(&self) -> &[u8; 16] {
1609 &self.md5
1610 }
1611
1612 pub fn source(&self) -> Option<AttributeValue<R, Offset>> {
1619 self.source.clone()
1620 }
1621}
1622
1623#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1625pub struct FileEntryFormat {
1626 pub content_type: constants::DwLnct,
1628
1629 pub form: constants::DwForm,
1631}
1632
1633impl FileEntryFormat {
1634 fn parse<R: Reader>(input: &mut R) -> Result<Vec<FileEntryFormat>> {
1635 let format_count = input.read_u8()? as usize;
1636 let mut format = Vec::with_capacity(format_count);
1637 let mut path_count = 0;
1638 for _ in 0..format_count {
1639 let content_type = input.read_uleb128()?;
1640 let content_type = if content_type > u64::from(u16::MAX) {
1641 constants::DwLnct(u16::MAX)
1642 } else {
1643 constants::DwLnct(content_type as u16)
1644 };
1645 if content_type == constants::DW_LNCT_path {
1646 path_count += 1;
1647 }
1648
1649 let form = constants::DwForm(input.read_uleb128_u16()?);
1650
1651 format.push(FileEntryFormat { content_type, form });
1652 }
1653 if path_count != 1 {
1654 return Err(Error::MissingFileEntryFormatPath);
1655 }
1656 Ok(format)
1657 }
1658}
1659
1660fn parse_directory_v5<R: Reader>(
1661 input: &mut R,
1662 encoding: Encoding,
1663 formats: &[FileEntryFormat],
1664) -> Result<AttributeValue<R>> {
1665 let mut path_name = None;
1666
1667 for format in formats {
1668 let value = parse_attribute(input, encoding, format.form)?;
1669 if format.content_type == constants::DW_LNCT_path {
1670 path_name = Some(value);
1671 }
1672 }
1673
1674 Ok(path_name.unwrap())
1675}
1676
1677fn parse_file_v5<R: Reader>(
1678 input: &mut R,
1679 encoding: Encoding,
1680 formats: &[FileEntryFormat],
1681) -> Result<FileEntry<R>> {
1682 let mut path_name = None;
1683 let mut directory_index = 0;
1684 let mut timestamp = 0;
1685 let mut size = 0;
1686 let mut md5 = [0; 16];
1687 let mut source = None;
1688
1689 for format in formats {
1690 let value = parse_attribute(input, encoding, format.form)?;
1691 match format.content_type {
1692 constants::DW_LNCT_path => path_name = Some(value),
1693 constants::DW_LNCT_directory_index => {
1694 if let Some(value) = value.udata_value() {
1695 directory_index = value;
1696 }
1697 }
1698 constants::DW_LNCT_timestamp => {
1699 if let Some(value) = value.udata_value() {
1700 timestamp = value;
1701 }
1702 }
1703 constants::DW_LNCT_size => {
1704 if let Some(value) = value.udata_value() {
1705 size = value;
1706 }
1707 }
1708 constants::DW_LNCT_MD5 => {
1709 if let AttributeValue::Block(mut value) = value
1710 && value.len().into_u64() == 16
1711 {
1712 md5 = value.read_u8_array()?;
1713 }
1714 }
1715 constants::DW_LNCT_LLVM_source => {
1716 source = Some(value);
1717 }
1718 _ => {}
1720 }
1721 }
1722
1723 Ok(FileEntry {
1724 path_name: path_name.unwrap(),
1725 directory_index,
1726 timestamp,
1727 size,
1728 md5,
1729 source,
1730 })
1731}
1732
1733fn parse_attribute<R: Reader>(
1735 input: &mut R,
1736 encoding: Encoding,
1737 form: constants::DwForm,
1738) -> Result<AttributeValue<R>> {
1739 Ok(match form {
1740 constants::DW_FORM_block1 => {
1741 let len = input.read_u8().map(R::Offset::from_u8)?;
1742 let block = input.split(len)?;
1743 AttributeValue::Block(block)
1744 }
1745 constants::DW_FORM_block2 => {
1746 let len = input.read_u16().map(R::Offset::from_u16)?;
1747 let block = input.split(len)?;
1748 AttributeValue::Block(block)
1749 }
1750 constants::DW_FORM_block4 => {
1751 let len = input.read_u32().map(R::Offset::from_u32)?;
1752 let block = input.split(len)?;
1753 AttributeValue::Block(block)
1754 }
1755 constants::DW_FORM_block => {
1756 let len = input.read_uleb128().and_then(R::Offset::from_u64)?;
1757 let block = input.split(len)?;
1758 AttributeValue::Block(block)
1759 }
1760 constants::DW_FORM_data1 => {
1761 let data = input.read_u8()?;
1762 AttributeValue::Data1(data)
1763 }
1764 constants::DW_FORM_data2 => {
1765 let data = input.read_u16()?;
1766 AttributeValue::Data2(data)
1767 }
1768 constants::DW_FORM_data4 => {
1769 let data = input.read_u32()?;
1770 AttributeValue::Data4(data)
1771 }
1772 constants::DW_FORM_data8 => {
1773 let data = input.read_u64()?;
1774 AttributeValue::Data8(data)
1775 }
1776 constants::DW_FORM_data16 => {
1777 let block = input.split(R::Offset::from_u8(16))?;
1778 AttributeValue::Block(block)
1779 }
1780 constants::DW_FORM_udata => {
1781 let data = input.read_uleb128()?;
1782 AttributeValue::Udata(data)
1783 }
1784 constants::DW_FORM_sdata => {
1785 let data = input.read_sleb128()?;
1786 AttributeValue::Sdata(data)
1787 }
1788 constants::DW_FORM_flag => {
1789 let present = input.read_u8()?;
1790 AttributeValue::Flag(present != 0)
1791 }
1792 constants::DW_FORM_sec_offset => {
1793 let offset = input.read_offset(encoding.format)?;
1794 AttributeValue::SecOffset(offset)
1795 }
1796 constants::DW_FORM_string => {
1797 let string = input.read_null_terminated_slice()?;
1798 AttributeValue::String(string)
1799 }
1800 constants::DW_FORM_strp => {
1801 let offset = input.read_offset(encoding.format)?;
1802 AttributeValue::DebugStrRef(DebugStrOffset(offset))
1803 }
1804 constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt => {
1805 let offset = input.read_offset(encoding.format)?;
1806 AttributeValue::DebugStrRefSup(DebugStrOffset(offset))
1807 }
1808 constants::DW_FORM_line_strp => {
1809 let offset = input.read_offset(encoding.format)?;
1810 AttributeValue::DebugLineStrRef(DebugLineStrOffset(offset))
1811 }
1812 constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index => {
1813 let index = input.read_uleb128().and_then(R::Offset::from_u64)?;
1814 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1815 }
1816 constants::DW_FORM_strx1 => {
1817 let index = input.read_u8().map(R::Offset::from_u8)?;
1818 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1819 }
1820 constants::DW_FORM_strx2 => {
1821 let index = input.read_u16().map(R::Offset::from_u16)?;
1822 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1823 }
1824 constants::DW_FORM_strx3 => {
1825 let index = input.read_uint(3).and_then(R::Offset::from_u64)?;
1826 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1827 }
1828 constants::DW_FORM_strx4 => {
1829 let index = input.read_u32().map(R::Offset::from_u32)?;
1830 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1831 }
1832 _ => {
1833 return Err(Error::UnknownForm(form));
1834 }
1835 })
1836}
1837
1838#[cfg(test)]
1839mod tests {
1840 use super::*;
1841 use crate::constants;
1842 use crate::endianity::LittleEndian;
1843 use crate::read::{EndianSlice, Error};
1844 use crate::test_util::GimliSectionMethods;
1845 use test_assembler::{Endian, Label, LabelMaker, Section};
1846
1847 #[test]
1848 fn test_parse_debug_line_32_ok() {
1849 #[rustfmt::skip]
1850 let buf = [
1851 0x3e, 0x00, 0x00, 0x00,
1853 0x04, 0x00,
1855 0x28, 0x00, 0x00, 0x00,
1857 0x01,
1859 0x01,
1861 0x01,
1863 0x00,
1865 0x01,
1867 0x03,
1869 0x01, 0x02,
1871 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
1873 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
1876 0x00,
1877 0x00,
1878 0x00,
1879 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
1881 0x01,
1882 0x00,
1883 0x00,
1884 0x00,
1886
1887 0x00, 0x00, 0x00, 0x00,
1889 0x00, 0x00, 0x00, 0x00,
1890 0x00, 0x00, 0x00, 0x00,
1891 0x00, 0x00, 0x00, 0x00,
1892
1893 0x00, 0x00, 0x00, 0x00,
1895 0x00, 0x00, 0x00, 0x00,
1896 0x00, 0x00, 0x00, 0x00,
1897 0x00, 0x00, 0x00, 0x00,
1898 ];
1899
1900 let rest = &mut EndianSlice::new(&buf, LittleEndian);
1901 let comp_dir = EndianSlice::new(b"/comp_dir", LittleEndian);
1902 let comp_name = EndianSlice::new(b"/comp_name", LittleEndian);
1903
1904 let header =
1905 LineProgramHeader::parse(rest, DebugLineOffset(0), 4, Some(comp_dir), Some(comp_name))
1906 .expect("should parse header ok");
1907
1908 assert_eq!(
1909 *rest,
1910 EndianSlice::new(&buf[buf.len() - 16..], LittleEndian)
1911 );
1912
1913 assert_eq!(header.offset, DebugLineOffset(0));
1914 assert_eq!(header.version(), 4);
1915 assert_eq!(header.minimum_instruction_length(), 1);
1916 assert_eq!(header.maximum_operations_per_instruction(), 1);
1917 assert!(header.default_is_stmt());
1918 assert_eq!(header.line_base(), 0);
1919 assert_eq!(header.line_range(), 1);
1920 assert_eq!(header.opcode_base(), 3);
1921 assert_eq!(header.directory(0), Some(AttributeValue::String(comp_dir)));
1922 assert_eq!(
1923 header.file(0).unwrap().path_name,
1924 AttributeValue::String(comp_name)
1925 );
1926
1927 let expected_lengths = [1, 2];
1928 assert_eq!(header.standard_opcode_lengths().slice(), &expected_lengths);
1929
1930 let expected_include_directories = [
1931 AttributeValue::String(EndianSlice::new(b"/inc", LittleEndian)),
1932 AttributeValue::String(EndianSlice::new(b"/inc2", LittleEndian)),
1933 ];
1934 assert_eq!(header.include_directories(), &expected_include_directories);
1935
1936 let expected_file_names = [
1937 FileEntry {
1938 path_name: AttributeValue::String(EndianSlice::new(b"foo.rs", LittleEndian)),
1939 directory_index: 0,
1940 timestamp: 0,
1941 size: 0,
1942 md5: [0; 16],
1943 source: None,
1944 },
1945 FileEntry {
1946 path_name: AttributeValue::String(EndianSlice::new(b"bar.h", LittleEndian)),
1947 directory_index: 1,
1948 timestamp: 0,
1949 size: 0,
1950 md5: [0; 16],
1951 source: None,
1952 },
1953 ];
1954 assert_eq!(header.file_names(), &expected_file_names);
1955 }
1956
1957 #[test]
1958 fn test_parse_debug_line_header_length_too_short() {
1959 #[rustfmt::skip]
1960 let buf = [
1961 0x3e, 0x00, 0x00, 0x00,
1963 0x04, 0x00,
1965 0x15, 0x00, 0x00, 0x00,
1967 0x01,
1969 0x01,
1971 0x01,
1973 0x00,
1975 0x01,
1977 0x03,
1979 0x01, 0x02,
1981 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
1983 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
1986 0x00,
1987 0x00,
1988 0x00,
1989 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
1991 0x01,
1992 0x00,
1993 0x00,
1994 0x00,
1996
1997 0x00, 0x00, 0x00, 0x00,
1999 0x00, 0x00, 0x00, 0x00,
2000 0x00, 0x00, 0x00, 0x00,
2001 0x00, 0x00, 0x00, 0x00,
2002
2003 0x00, 0x00, 0x00, 0x00,
2005 0x00, 0x00, 0x00, 0x00,
2006 0x00, 0x00, 0x00, 0x00,
2007 0x00, 0x00, 0x00, 0x00,
2008 ];
2009
2010 let input = &mut EndianSlice::new(&buf, LittleEndian);
2011
2012 match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) {
2013 Err(Error::UnexpectedEof(_)) => {}
2014 otherwise => panic!("Unexpected result: {:?}", otherwise),
2015 }
2016 }
2017
2018 #[test]
2019 fn test_parse_debug_line_unit_length_too_short() {
2020 #[rustfmt::skip]
2021 let buf = [
2022 0x28, 0x00, 0x00, 0x00,
2024 0x04, 0x00,
2026 0x28, 0x00, 0x00, 0x00,
2028 0x01,
2030 0x01,
2032 0x01,
2034 0x00,
2036 0x01,
2038 0x03,
2040 0x01, 0x02,
2042 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
2044 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
2047 0x00,
2048 0x00,
2049 0x00,
2050 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
2052 0x01,
2053 0x00,
2054 0x00,
2055 0x00,
2057
2058 0x00, 0x00, 0x00, 0x00,
2060 0x00, 0x00, 0x00, 0x00,
2061 0x00, 0x00, 0x00, 0x00,
2062 0x00, 0x00, 0x00, 0x00,
2063
2064 0x00, 0x00, 0x00, 0x00,
2066 0x00, 0x00, 0x00, 0x00,
2067 0x00, 0x00, 0x00, 0x00,
2068 0x00, 0x00, 0x00, 0x00,
2069 ];
2070
2071 let input = &mut EndianSlice::new(&buf, LittleEndian);
2072
2073 match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) {
2074 Err(Error::UnexpectedEof(_)) => {}
2075 otherwise => panic!("Unexpected result: {:?}", otherwise),
2076 }
2077 }
2078
2079 const OPCODE_BASE: u8 = 13;
2080 const STANDARD_OPCODE_LENGTHS: &[u8] = &[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1];
2081
2082 fn make_test_header(
2083 buf: EndianSlice<'_, LittleEndian>,
2084 ) -> LineProgramHeader<EndianSlice<'_, LittleEndian>> {
2085 let encoding = Encoding {
2086 format: Format::Dwarf32,
2087 version: 4,
2088 address_size: 8,
2089 };
2090 let line_encoding = LineEncoding {
2091 line_base: -3,
2092 line_range: 12,
2093 ..Default::default()
2094 };
2095 LineProgramHeader {
2096 encoding,
2097 offset: DebugLineOffset(0),
2098 unit_length: 1,
2099 header_length: 1,
2100 line_encoding,
2101 opcode_base: OPCODE_BASE,
2102 standard_opcode_lengths: EndianSlice::new(STANDARD_OPCODE_LENGTHS, LittleEndian),
2103 file_names: vec![
2104 FileEntry {
2105 path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)),
2106 directory_index: 0,
2107 timestamp: 0,
2108 size: 0,
2109 md5: [0; 16],
2110 source: None,
2111 },
2112 FileEntry {
2113 path_name: AttributeValue::String(EndianSlice::new(b"bar.rs", LittleEndian)),
2114 directory_index: 0,
2115 timestamp: 0,
2116 size: 0,
2117 md5: [0; 16],
2118 source: None,
2119 },
2120 ],
2121 include_directories: vec![],
2122 directory_entry_format: vec![],
2123 file_name_entry_format: vec![],
2124 program_buf: buf,
2125 comp_dir: None,
2126 comp_file: None,
2127 }
2128 }
2129
2130 fn make_test_program(
2131 buf: EndianSlice<'_, LittleEndian>,
2132 ) -> IncompleteLineProgram<EndianSlice<'_, LittleEndian>> {
2133 IncompleteLineProgram {
2134 header: make_test_header(buf),
2135 }
2136 }
2137
2138 #[test]
2139 fn test_parse_special_opcodes() {
2140 for i in OPCODE_BASE..u8::MAX {
2141 let input = [i, 0, 0, 0];
2142 let input = EndianSlice::new(&input, LittleEndian);
2143 let header = make_test_header(input);
2144
2145 let mut rest = input;
2146 let opcode =
2147 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2148
2149 assert_eq!(*rest, *input.range_from(1..));
2150 assert_eq!(opcode, LineInstruction::Special(i));
2151 }
2152 }
2153
2154 #[test]
2155 fn test_parse_standard_opcodes() {
2156 fn test<Operands>(
2157 raw: constants::DwLns,
2158 operands: Operands,
2159 expected: LineInstruction<EndianSlice<'_, LittleEndian>>,
2160 ) where
2161 Operands: AsRef<[u8]>,
2162 {
2163 let mut input = Vec::new();
2164 input.push(raw.0);
2165 input.extend_from_slice(operands.as_ref());
2166
2167 let expected_rest = [0, 1, 2, 3, 4];
2168 input.extend_from_slice(&expected_rest);
2169
2170 let input = EndianSlice::new(&input, LittleEndian);
2171 let header = make_test_header(input);
2172
2173 let mut rest = input;
2174 let opcode =
2175 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2176
2177 assert_eq!(opcode, expected);
2178 assert_eq!(*rest, expected_rest);
2179 }
2180
2181 test(constants::DW_LNS_copy, [], LineInstruction::Copy);
2182 test(
2183 constants::DW_LNS_advance_pc,
2184 [42],
2185 LineInstruction::AdvancePc(42),
2186 );
2187 test(
2188 constants::DW_LNS_advance_line,
2189 [9],
2190 LineInstruction::AdvanceLine(9),
2191 );
2192 test(constants::DW_LNS_set_file, [7], LineInstruction::SetFile(7));
2193 test(
2194 constants::DW_LNS_set_column,
2195 [1],
2196 LineInstruction::SetColumn(1),
2197 );
2198 test(
2199 constants::DW_LNS_negate_stmt,
2200 [],
2201 LineInstruction::NegateStatement,
2202 );
2203 test(
2204 constants::DW_LNS_set_basic_block,
2205 [],
2206 LineInstruction::SetBasicBlock,
2207 );
2208 test(
2209 constants::DW_LNS_const_add_pc,
2210 [],
2211 LineInstruction::ConstAddPc,
2212 );
2213 test(
2214 constants::DW_LNS_fixed_advance_pc,
2215 [42, 0],
2216 LineInstruction::FixedAddPc(42),
2217 );
2218 test(
2219 constants::DW_LNS_set_prologue_end,
2220 [],
2221 LineInstruction::SetPrologueEnd,
2222 );
2223 test(
2224 constants::DW_LNS_set_isa,
2225 [57 + 0x80, 100],
2226 LineInstruction::SetIsa(12857),
2227 );
2228 }
2229
2230 #[test]
2231 fn test_parse_unknown_standard_opcode_no_args() {
2232 let input = [OPCODE_BASE, 1, 2, 3];
2233 let input = EndianSlice::new(&input, LittleEndian);
2234 let mut standard_opcode_lengths = Vec::new();
2235 let mut header = make_test_header(input);
2236 standard_opcode_lengths.extend(header.standard_opcode_lengths.slice());
2237 standard_opcode_lengths.push(0);
2238 header.opcode_base += 1;
2239 header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian);
2240
2241 let mut rest = input;
2242 let opcode =
2243 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2244
2245 assert_eq!(
2246 opcode,
2247 LineInstruction::UnknownStandard0(constants::DwLns(OPCODE_BASE))
2248 );
2249 assert_eq!(*rest, *input.range_from(1..));
2250 }
2251
2252 #[test]
2253 fn test_parse_unknown_standard_opcode_one_arg() {
2254 let input = [OPCODE_BASE, 1, 2, 3];
2255 let input = EndianSlice::new(&input, LittleEndian);
2256 let mut standard_opcode_lengths = Vec::new();
2257 let mut header = make_test_header(input);
2258 standard_opcode_lengths.extend(header.standard_opcode_lengths.slice());
2259 standard_opcode_lengths.push(1);
2260 header.opcode_base += 1;
2261 header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian);
2262
2263 let mut rest = input;
2264 let opcode =
2265 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2266
2267 assert_eq!(
2268 opcode,
2269 LineInstruction::UnknownStandard1(constants::DwLns(OPCODE_BASE), 1)
2270 );
2271 assert_eq!(*rest, *input.range_from(2..));
2272 }
2273
2274 #[test]
2275 fn test_parse_unknown_standard_opcode_many_args() {
2276 let input = [OPCODE_BASE, 1, 2, 3];
2277 let input = EndianSlice::new(&input, LittleEndian);
2278 let args = input.range_from(1..);
2279 let mut standard_opcode_lengths = Vec::new();
2280 let mut header = make_test_header(input);
2281 standard_opcode_lengths.extend(header.standard_opcode_lengths.slice());
2282 standard_opcode_lengths.push(3);
2283 header.opcode_base += 1;
2284 header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian);
2285
2286 let mut rest = input;
2287 let opcode =
2288 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2289
2290 assert_eq!(
2291 opcode,
2292 LineInstruction::UnknownStandardN(constants::DwLns(OPCODE_BASE), args)
2293 );
2294 assert_eq!(*rest, []);
2295 }
2296
2297 #[test]
2298 fn test_parse_extended_opcodes() {
2299 fn test<Operands>(
2300 raw: constants::DwLne,
2301 operands: Operands,
2302 expected: LineInstruction<EndianSlice<'_, LittleEndian>>,
2303 ) where
2304 Operands: AsRef<[u8]>,
2305 {
2306 let mut input = Vec::new();
2307 input.push(0);
2308
2309 let operands = operands.as_ref();
2310 input.push(1 + operands.len() as u8);
2311
2312 input.push(raw.0);
2313 input.extend_from_slice(operands);
2314
2315 let expected_rest = [0, 1, 2, 3, 4];
2316 input.extend_from_slice(&expected_rest);
2317
2318 let input = EndianSlice::new(&input, LittleEndian);
2319 let header = make_test_header(input);
2320
2321 let mut rest = input;
2322 let opcode =
2323 LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK");
2324
2325 assert_eq!(opcode, expected);
2326 assert_eq!(*rest, expected_rest);
2327 }
2328
2329 test(
2330 constants::DW_LNE_end_sequence,
2331 [],
2332 LineInstruction::EndSequence,
2333 );
2334 test(
2335 constants::DW_LNE_set_address,
2336 [1, 2, 3, 4, 5, 6, 7, 8],
2337 LineInstruction::SetAddress(578_437_695_752_307_201),
2338 );
2339 test(
2340 constants::DW_LNE_set_discriminator,
2341 [42],
2342 LineInstruction::SetDiscriminator(42),
2343 );
2344
2345 let mut file = Vec::new();
2346 let path_name = [b'f', b'o', b'o', b'.', b'c', 0];
2348 file.extend_from_slice(&path_name);
2349 file.push(0);
2351 file.push(1);
2353 file.push(2);
2355
2356 test(
2357 constants::DW_LNE_define_file,
2358 file,
2359 LineInstruction::DefineFile(FileEntry {
2360 path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)),
2361 directory_index: 0,
2362 timestamp: 1,
2363 size: 2,
2364 md5: [0; 16],
2365 source: None,
2366 }),
2367 );
2368
2369 let operands = [1, 2, 3, 4, 5, 6];
2371 let opcode = constants::DwLne(99);
2372 test(
2373 opcode,
2374 operands,
2375 LineInstruction::UnknownExtended(opcode, EndianSlice::new(&operands, LittleEndian)),
2376 );
2377 }
2378
2379 #[test]
2380 fn test_file_entry_directory() {
2381 let path_name = [b'f', b'o', b'o', b'.', b'r', b's', 0];
2382
2383 let mut file = FileEntry {
2384 path_name: AttributeValue::String(EndianSlice::new(&path_name, LittleEndian)),
2385 directory_index: 1,
2386 timestamp: 0,
2387 size: 0,
2388 md5: [0; 16],
2389 source: None,
2390 };
2391
2392 let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
2393
2394 let dir = AttributeValue::String(EndianSlice::new(b"dir", LittleEndian));
2395 header.include_directories.push(dir);
2396
2397 assert_eq!(file.directory(&header), Some(dir));
2398
2399 file.directory_index = 0;
2401 assert_eq!(file.directory(&header), None);
2402 }
2403
2404 fn assert_exec_opcode<'input>(
2405 header: LineProgramHeader<EndianSlice<'input, LittleEndian>>,
2406 mut registers: LineRow,
2407 opcode: LineInstruction<EndianSlice<'input, LittleEndian>>,
2408 expected_registers: LineRow,
2409 expect_new_row: bool,
2410 ) {
2411 let mut program = IncompleteLineProgram { header };
2412 let is_new_row = registers.execute(opcode, &mut program);
2413
2414 assert_eq!(is_new_row, Ok(expect_new_row));
2415 assert_eq!(registers, expected_registers);
2416 }
2417
2418 #[test]
2419 fn test_exec_special_noop() {
2420 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2421
2422 let initial_registers = LineRow::new(&header);
2423 let opcode = LineInstruction::Special(16);
2424 let expected_registers = initial_registers;
2425
2426 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2427 }
2428
2429 #[test]
2430 fn test_exec_special_negative_line_advance() {
2431 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2432
2433 let mut initial_registers = LineRow::new(&header);
2434 initial_registers.line.0 = 10;
2435
2436 let opcode = LineInstruction::Special(13);
2437
2438 let mut expected_registers = initial_registers;
2439 expected_registers.line.0 -= 3;
2440
2441 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2442 }
2443
2444 #[test]
2445 fn test_exec_special_positive_line_advance() {
2446 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2447
2448 let initial_registers = LineRow::new(&header);
2449
2450 let opcode = LineInstruction::Special(19);
2451
2452 let mut expected_registers = initial_registers;
2453 expected_registers.line.0 += 3;
2454
2455 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2456 }
2457
2458 #[test]
2459 fn test_exec_special_positive_address_advance() {
2460 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2461
2462 let initial_registers = LineRow::new(&header);
2463
2464 let opcode = LineInstruction::Special(52);
2465
2466 let mut expected_registers = initial_registers;
2467 expected_registers.address += 3;
2468
2469 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2470 }
2471
2472 #[test]
2473 fn test_exec_special_positive_address_and_line_advance() {
2474 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2475
2476 let initial_registers = LineRow::new(&header);
2477
2478 let opcode = LineInstruction::Special(55);
2479
2480 let mut expected_registers = initial_registers;
2481 expected_registers.address += 3;
2482 expected_registers.line.0 += 3;
2483
2484 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2485 }
2486
2487 #[test]
2488 fn test_exec_special_positive_address_and_negative_line_advance() {
2489 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2490
2491 let mut initial_registers = LineRow::new(&header);
2492 initial_registers.line.0 = 10;
2493
2494 let opcode = LineInstruction::Special(49);
2495
2496 let mut expected_registers = initial_registers;
2497 expected_registers.address += 3;
2498 expected_registers.line.0 -= 3;
2499
2500 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2501 }
2502
2503 #[test]
2504 fn test_exec_special_line_underflow() {
2505 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2506
2507 let mut initial_registers = LineRow::new(&header);
2508 initial_registers.line.0 = 2;
2509
2510 let opcode = LineInstruction::Special(13);
2512
2513 let mut expected_registers = initial_registers;
2514 expected_registers.line.0 = 0;
2517
2518 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2519 }
2520
2521 #[test]
2522 fn test_exec_copy() {
2523 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2524
2525 let mut initial_registers = LineRow::new(&header);
2526 initial_registers.address = 1337;
2527 initial_registers.line.0 = 42;
2528
2529 let opcode = LineInstruction::Copy;
2530
2531 let expected_registers = initial_registers;
2532
2533 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2534 }
2535
2536 #[test]
2537 fn test_exec_advance_pc() {
2538 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2539 let initial_registers = LineRow::new(&header);
2540 let opcode = LineInstruction::AdvancePc(42);
2541
2542 let mut expected_registers = initial_registers;
2543 expected_registers.address += 42;
2544
2545 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2546 }
2547
2548 #[test]
2549 fn test_exec_advance_pc_overflow_32() {
2550 let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
2551 header.encoding.address_size = 4;
2552 let mut registers = LineRow::new(&header);
2553 registers.address = u32::MAX.into();
2554 let opcode = LineInstruction::AdvancePc(42);
2555 let mut program = IncompleteLineProgram { header };
2556 let result = registers.execute(opcode, &mut program);
2557 assert_eq!(result, Err(Error::AddressOverflow));
2558 }
2559
2560 #[test]
2561 fn test_exec_advance_pc_overflow_64() {
2562 let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
2563 header.encoding.address_size = 8;
2564 let mut registers = LineRow::new(&header);
2565 registers.address = u64::MAX;
2566 let opcode = LineInstruction::AdvancePc(42);
2567 let mut program = IncompleteLineProgram { header };
2568 let result = registers.execute(opcode, &mut program);
2569 assert_eq!(result, Err(Error::AddressOverflow));
2570 }
2571
2572 #[test]
2573 fn test_exec_advance_line() {
2574 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2575 let initial_registers = LineRow::new(&header);
2576 let opcode = LineInstruction::AdvanceLine(42);
2577
2578 let mut expected_registers = initial_registers;
2579 expected_registers.line.0 += 42;
2580
2581 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2582 }
2583
2584 #[test]
2585 fn test_exec_advance_line_overflow() {
2586 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2587 let opcode = LineInstruction::AdvanceLine(42);
2588
2589 let mut initial_registers = LineRow::new(&header);
2590 initial_registers.line.0 = u64::MAX;
2591
2592 let mut expected_registers = initial_registers;
2593 expected_registers.line.0 = 41;
2594
2595 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2596 }
2597
2598 #[test]
2599 fn test_exec_set_file_in_bounds() {
2600 for file_idx in 1..3 {
2601 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2602 let initial_registers = LineRow::new(&header);
2603 let opcode = LineInstruction::SetFile(file_idx);
2604
2605 let mut expected_registers = initial_registers;
2606 expected_registers.file = file_idx;
2607
2608 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2609 }
2610 }
2611
2612 #[test]
2613 fn test_exec_set_file_out_of_bounds() {
2614 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2615 let initial_registers = LineRow::new(&header);
2616 let opcode = LineInstruction::SetFile(100);
2617
2618 let mut expected_registers = initial_registers;
2625 expected_registers.file = 100;
2626
2627 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2628 }
2629
2630 #[test]
2631 fn test_file_entry_file_index_out_of_bounds() {
2632 let out_of_bounds_indices = [0, 100];
2635
2636 for file_idx in &out_of_bounds_indices[..] {
2637 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2638 let mut row = LineRow::new(&header);
2639
2640 row.file = *file_idx;
2641
2642 assert_eq!(row.file(&header), None);
2643 }
2644 }
2645
2646 #[test]
2647 fn test_file_entry_file_index_in_bounds() {
2648 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2649 let mut row = LineRow::new(&header);
2650
2651 row.file = 2;
2652
2653 assert_eq!(row.file(&header), Some(&header.file_names()[1]));
2654 }
2655
2656 #[test]
2657 fn test_exec_set_column() {
2658 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2659 let initial_registers = LineRow::new(&header);
2660 let opcode = LineInstruction::SetColumn(42);
2661
2662 let mut expected_registers = initial_registers;
2663 expected_registers.column = 42;
2664
2665 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2666 }
2667
2668 #[test]
2669 fn test_exec_negate_statement() {
2670 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2671 let initial_registers = LineRow::new(&header);
2672 let opcode = LineInstruction::NegateStatement;
2673
2674 let mut expected_registers = initial_registers;
2675 expected_registers.is_stmt = !initial_registers.is_stmt;
2676
2677 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2678 }
2679
2680 #[test]
2681 fn test_exec_set_basic_block() {
2682 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2683
2684 let mut initial_registers = LineRow::new(&header);
2685 initial_registers.basic_block = false;
2686
2687 let opcode = LineInstruction::SetBasicBlock;
2688
2689 let mut expected_registers = initial_registers;
2690 expected_registers.basic_block = true;
2691
2692 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2693 }
2694
2695 #[test]
2696 fn test_exec_const_add_pc() {
2697 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2698 let initial_registers = LineRow::new(&header);
2699 let opcode = LineInstruction::ConstAddPc;
2700
2701 let mut expected_registers = initial_registers;
2702 expected_registers.address += 20;
2703
2704 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2705 }
2706
2707 #[test]
2708 fn test_exec_const_add_pc_overflow() {
2709 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2710 let mut registers = LineRow::new(&header);
2711 registers.address = u64::MAX;
2712 let opcode = LineInstruction::ConstAddPc;
2713 let mut program = IncompleteLineProgram { header };
2714 let result = registers.execute(opcode, &mut program);
2715 assert_eq!(result, Err(Error::AddressOverflow));
2716 }
2717
2718 #[test]
2719 fn test_exec_fixed_add_pc() {
2720 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2721
2722 let mut initial_registers = LineRow::new(&header);
2723 initial_registers.op_index.0 = 1;
2724
2725 let opcode = LineInstruction::FixedAddPc(10);
2726
2727 let mut expected_registers = initial_registers;
2728 expected_registers.address += 10;
2729 expected_registers.op_index.0 = 0;
2730
2731 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2732 }
2733
2734 #[test]
2735 fn test_exec_fixed_add_pc_overflow() {
2736 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2737 let mut registers = LineRow::new(&header);
2738 registers.address = u64::MAX;
2739 registers.op_index.0 = 1;
2740 let opcode = LineInstruction::FixedAddPc(10);
2741 let mut program = IncompleteLineProgram { header };
2742 let result = registers.execute(opcode, &mut program);
2743 assert_eq!(result, Err(Error::AddressOverflow));
2744 }
2745
2746 #[test]
2747 fn test_exec_set_prologue_end() {
2748 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2749
2750 let mut initial_registers = LineRow::new(&header);
2751 initial_registers.prologue_end = false;
2752
2753 let opcode = LineInstruction::SetPrologueEnd;
2754
2755 let mut expected_registers = initial_registers;
2756 expected_registers.prologue_end = true;
2757
2758 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2759 }
2760
2761 #[test]
2762 fn test_exec_set_isa() {
2763 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2764 let initial_registers = LineRow::new(&header);
2765 let opcode = LineInstruction::SetIsa(1993);
2766
2767 let mut expected_registers = initial_registers;
2768 expected_registers.isa = 1993;
2769
2770 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2771 }
2772
2773 #[test]
2774 fn test_exec_unknown_standard_0() {
2775 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2776 let initial_registers = LineRow::new(&header);
2777 let opcode = LineInstruction::UnknownStandard0(constants::DwLns(111));
2778 let expected_registers = initial_registers;
2779 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2780 }
2781
2782 #[test]
2783 fn test_exec_unknown_standard_1() {
2784 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2785 let initial_registers = LineRow::new(&header);
2786 let opcode = LineInstruction::UnknownStandard1(constants::DwLns(111), 2);
2787 let expected_registers = initial_registers;
2788 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2789 }
2790
2791 #[test]
2792 fn test_exec_unknown_standard_n() {
2793 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2794 let initial_registers = LineRow::new(&header);
2795 let opcode = LineInstruction::UnknownStandardN(
2796 constants::DwLns(111),
2797 EndianSlice::new(&[2, 2, 2], LittleEndian),
2798 );
2799 let expected_registers = initial_registers;
2800 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2801 }
2802
2803 #[test]
2804 fn test_exec_end_sequence() {
2805 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2806 let initial_registers = LineRow::new(&header);
2807 let opcode = LineInstruction::EndSequence;
2808
2809 let mut expected_registers = initial_registers;
2810 expected_registers.end_sequence = true;
2811
2812 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2813 }
2814
2815 #[test]
2816 fn test_exec_set_address() {
2817 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2818 let initial_registers = LineRow::new(&header);
2819 let opcode = LineInstruction::SetAddress(3030);
2820
2821 let mut expected_registers = initial_registers;
2822 expected_registers.address = 3030;
2823
2824 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2825 }
2826
2827 #[test]
2828 fn test_exec_set_address_tombstone() {
2829 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2830 let initial_registers = LineRow::new(&header);
2831 let opcode = LineInstruction::SetAddress(!0);
2832
2833 let mut expected_registers = initial_registers;
2834 expected_registers.tombstone = true;
2835
2836 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2837 }
2838
2839 #[test]
2840 fn test_exec_set_address_backwards() {
2841 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2842 let mut initial_registers = LineRow::new(&header);
2843 initial_registers.address = 1;
2844 let opcode = LineInstruction::SetAddress(0);
2845
2846 let mut expected_registers = initial_registers;
2847 expected_registers.tombstone = true;
2848
2849 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2850 }
2851
2852 #[test]
2853 fn test_exec_define_file() {
2854 let mut program = make_test_program(EndianSlice::new(&[], LittleEndian));
2855 let mut row = LineRow::new(program.header());
2856
2857 let file = FileEntry {
2858 path_name: AttributeValue::String(EndianSlice::new(b"test.cpp", LittleEndian)),
2859 directory_index: 0,
2860 timestamp: 0,
2861 size: 0,
2862 md5: [0; 16],
2863 source: None,
2864 };
2865
2866 let opcode = LineInstruction::DefineFile(file);
2867 let is_new_row = row.execute(opcode, &mut program).unwrap();
2868
2869 assert!(!is_new_row);
2870 assert_eq!(Some(&file), program.header().file_names.last());
2871 }
2872
2873 #[test]
2874 fn test_exec_set_discriminator() {
2875 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2876 let initial_registers = LineRow::new(&header);
2877 let opcode = LineInstruction::SetDiscriminator(9);
2878
2879 let mut expected_registers = initial_registers;
2880 expected_registers.discriminator = 9;
2881
2882 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2883 }
2884
2885 #[test]
2886 fn test_exec_unknown_extended() {
2887 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2888 let initial_registers = LineRow::new(&header);
2889 let opcode = LineInstruction::UnknownExtended(
2890 constants::DwLne(74),
2891 EndianSlice::new(&[], LittleEndian),
2892 );
2893 let expected_registers = initial_registers;
2894 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2895 }
2896
2897 #[allow(dead_code, unreachable_code, unused_variables)]
2900 #[allow(clippy::diverging_sub_expression)]
2901 fn test_line_rows_variance<'a, 'b>(_: &'a [u8], _: &'b [u8])
2902 where
2903 'a: 'b,
2904 {
2905 let a: &OneShotLineRows<EndianSlice<'a, LittleEndian>> = unimplemented!();
2906 let _: &OneShotLineRows<EndianSlice<'b, LittleEndian>> = a;
2907 }
2908
2909 #[test]
2910 fn test_parse_debug_line_v5_ok() {
2911 let expected_lengths = &[1, 2];
2912 let expected_program = &[0, 1, 2, 3, 4];
2913 let expected_rest = &[5, 6, 7, 8, 9];
2914 let expected_include_directories = [
2915 AttributeValue::String(EndianSlice::new(b"dir1", LittleEndian)),
2916 AttributeValue::String(EndianSlice::new(b"dir2", LittleEndian)),
2917 ];
2918 let expected_file_names = [
2919 FileEntry {
2920 path_name: AttributeValue::String(EndianSlice::new(b"file1", LittleEndian)),
2921 directory_index: 0,
2922 timestamp: 0,
2923 size: 0,
2924 md5: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
2925 source: Some(AttributeValue::String(EndianSlice::new(
2926 b"foobar",
2927 LittleEndian,
2928 ))),
2929 },
2930 FileEntry {
2931 path_name: AttributeValue::String(EndianSlice::new(b"file2", LittleEndian)),
2932 directory_index: 1,
2933 timestamp: 0,
2934 size: 0,
2935 md5: [
2936 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
2937 ],
2938 source: Some(AttributeValue::String(EndianSlice::new(
2939 b"quux",
2940 LittleEndian,
2941 ))),
2942 },
2943 ];
2944
2945 for format in [Format::Dwarf32, Format::Dwarf64] {
2946 let length = Label::new();
2947 let header_length = Label::new();
2948 let start = Label::new();
2949 let header_start = Label::new();
2950 let end = Label::new();
2951 let header_end = Label::new();
2952 let section = Section::with_endian(Endian::Little)
2953 .initial_length(format, &length, &start)
2954 .D16(5)
2955 .D8(4)
2957 .D8(0)
2959 .word_label(format.word_size(), &header_length)
2960 .mark(&header_start)
2961 .D8(1)
2963 .D8(1)
2965 .D8(1)
2967 .D8(0)
2969 .D8(1)
2971 .D8(expected_lengths.len() as u8 + 1)
2973 .append_bytes(expected_lengths)
2975 .D8(1)
2977 .uleb(constants::DW_LNCT_path.0 as u64)
2978 .uleb(constants::DW_FORM_string.0 as u64)
2979 .D8(2)
2981 .append_bytes(b"dir1\0")
2982 .append_bytes(b"dir2\0")
2983 .D8(4)
2985 .uleb(constants::DW_LNCT_path.0 as u64)
2986 .uleb(constants::DW_FORM_string.0 as u64)
2987 .uleb(constants::DW_LNCT_directory_index.0 as u64)
2988 .uleb(constants::DW_FORM_data1.0 as u64)
2989 .uleb(constants::DW_LNCT_MD5.0 as u64)
2990 .uleb(constants::DW_FORM_data16.0 as u64)
2991 .uleb(constants::DW_LNCT_LLVM_source.0 as u64)
2992 .uleb(constants::DW_FORM_string.0 as u64)
2993 .D8(2)
2995 .append_bytes(b"file1\0")
2996 .D8(0)
2997 .append_bytes(&expected_file_names[0].md5)
2998 .append_bytes(b"foobar\0")
2999 .append_bytes(b"file2\0")
3000 .D8(1)
3001 .append_bytes(&expected_file_names[1].md5)
3002 .append_bytes(b"quux\0")
3003 .mark(&header_end)
3004 .append_bytes(expected_program)
3006 .mark(&end)
3007 .append_bytes(expected_rest);
3009 length.set_const((&end - &start) as u64);
3010 header_length.set_const((&header_end - &header_start) as u64);
3011 let section = section.get_contents().unwrap();
3012
3013 let input = &mut EndianSlice::new(§ion, LittleEndian);
3014
3015 let header = LineProgramHeader::parse(input, DebugLineOffset(0), 0, None, None)
3016 .expect("should parse header ok");
3017
3018 assert_eq!(header.raw_program_buf().slice(), expected_program);
3019 assert_eq!(input.slice(), expected_rest);
3020
3021 assert_eq!(header.offset, DebugLineOffset(0));
3022 assert_eq!(header.version(), 5);
3023 assert_eq!(header.address_size(), 4);
3024 assert_eq!(header.minimum_instruction_length(), 1);
3025 assert_eq!(header.maximum_operations_per_instruction(), 1);
3026 assert!(header.default_is_stmt());
3027 assert_eq!(header.line_base(), 0);
3028 assert_eq!(header.line_range(), 1);
3029 assert_eq!(header.opcode_base(), expected_lengths.len() as u8 + 1);
3030 assert_eq!(header.standard_opcode_lengths().slice(), expected_lengths);
3031 assert_eq!(
3032 header.directory_entry_format(),
3033 &[FileEntryFormat {
3034 content_type: constants::DW_LNCT_path,
3035 form: constants::DW_FORM_string,
3036 }]
3037 );
3038 assert_eq!(header.include_directories(), expected_include_directories);
3039 assert_eq!(header.directory(0), Some(expected_include_directories[0]));
3040 assert_eq!(
3041 header.file_name_entry_format(),
3042 &[
3043 FileEntryFormat {
3044 content_type: constants::DW_LNCT_path,
3045 form: constants::DW_FORM_string,
3046 },
3047 FileEntryFormat {
3048 content_type: constants::DW_LNCT_directory_index,
3049 form: constants::DW_FORM_data1,
3050 },
3051 FileEntryFormat {
3052 content_type: constants::DW_LNCT_MD5,
3053 form: constants::DW_FORM_data16,
3054 },
3055 FileEntryFormat {
3056 content_type: constants::DW_LNCT_LLVM_source,
3057 form: constants::DW_FORM_string,
3058 }
3059 ]
3060 );
3061 assert_eq!(header.file_names(), expected_file_names);
3062 assert_eq!(header.file(0), Some(&expected_file_names[0]));
3063 }
3064 }
3065
3066 #[test]
3067 fn test_sequences() {
3068 #[rustfmt::skip]
3069 let buf = [
3070 94, 0x00, 0x00, 0x00,
3072 0x04, 0x00,
3074 0x28, 0x00, 0x00, 0x00,
3076 0x01,
3078 0x01,
3080 0x01,
3082 0x00,
3084 0x01,
3086 0x03,
3088 0x01, 0x02,
3090 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
3092 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
3095 0x00,
3096 0x00,
3097 0x00,
3098 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
3100 0x01,
3101 0x00,
3102 0x00,
3103 0x00,
3105
3106 0, 5, constants::DW_LNE_set_address.0, 1, 0, 0, 0,
3107 constants::DW_LNS_copy.0,
3108 constants::DW_LNS_advance_pc.0, 1,
3109 constants::DW_LNS_copy.0,
3110 constants::DW_LNS_advance_pc.0, 2,
3111 0, 1, constants::DW_LNE_end_sequence.0,
3112
3113 0, 5, constants::DW_LNE_set_address.0, 0xff, 0xff, 0xff, 0xff,
3115 constants::DW_LNS_copy.0,
3116 constants::DW_LNS_advance_pc.0, 1,
3117 constants::DW_LNS_copy.0,
3118 constants::DW_LNS_advance_pc.0, 2,
3119 0, 1, constants::DW_LNE_end_sequence.0,
3120
3121 0, 5, constants::DW_LNE_set_address.0, 11, 0, 0, 0,
3122 constants::DW_LNS_copy.0,
3123 constants::DW_LNS_advance_pc.0, 1,
3124 constants::DW_LNS_copy.0,
3125 constants::DW_LNS_advance_pc.0, 2,
3126 0, 1, constants::DW_LNE_end_sequence.0,
3127 ];
3128 assert_eq!(buf[0] as usize, buf.len() - 4);
3129
3130 let rest = &mut EndianSlice::new(&buf, LittleEndian);
3131
3132 let header = LineProgramHeader::parse(rest, DebugLineOffset(0), 4, None, None)
3133 .expect("should parse header ok");
3134 let program = IncompleteLineProgram { header };
3135
3136 let sequences = program.sequences().unwrap().1;
3137 assert_eq!(sequences.len(), 2);
3138 assert_eq!(sequences[0].start, 1);
3139 assert_eq!(sequences[0].end, 4);
3140 assert_eq!(sequences[1].start, 11);
3141 assert_eq!(sequences[1].end, 14);
3142 }
3143}