1use alloc::vec::Vec;
2use core::fmt;
3use core::num::{NonZeroU64, Wrapping};
4use core::result;
5
6use crate::common::{
7 DebugLineOffset, DebugLineStrOffset, DebugStrOffset, DebugStrOffsetsIndex, Encoding, Format,
8 LineEncoding, SectionId,
9};
10use crate::constants;
11use crate::endianity::Endianity;
12use crate::read::{AttributeValue, EndianSlice, Error, Reader, ReaderOffset, Result, Section};
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
519impl<R, Offset> fmt::Display for LineInstruction<R, Offset>
520where
521 R: Reader<Offset = Offset>,
522 Offset: ReaderOffset,
523{
524 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
525 match *self {
526 LineInstruction::Special(opcode) => write!(f, "Special opcode {}", opcode),
527 LineInstruction::Copy => write!(f, "{}", constants::DW_LNS_copy),
528 LineInstruction::AdvancePc(advance) => {
529 write!(f, "{} by {}", constants::DW_LNS_advance_pc, advance)
530 }
531 LineInstruction::AdvanceLine(increment) => {
532 write!(f, "{} by {}", constants::DW_LNS_advance_line, increment)
533 }
534 LineInstruction::SetFile(file) => {
535 write!(f, "{} to {}", constants::DW_LNS_set_file, file)
536 }
537 LineInstruction::SetColumn(column) => {
538 write!(f, "{} to {}", constants::DW_LNS_set_column, column)
539 }
540 LineInstruction::NegateStatement => write!(f, "{}", constants::DW_LNS_negate_stmt),
541 LineInstruction::SetBasicBlock => write!(f, "{}", constants::DW_LNS_set_basic_block),
542 LineInstruction::ConstAddPc => write!(f, "{}", constants::DW_LNS_const_add_pc),
543 LineInstruction::FixedAddPc(advance) => {
544 write!(f, "{} by {}", constants::DW_LNS_fixed_advance_pc, advance)
545 }
546 LineInstruction::SetPrologueEnd => write!(f, "{}", constants::DW_LNS_set_prologue_end),
547 LineInstruction::SetEpilogueBegin => {
548 write!(f, "{}", constants::DW_LNS_set_epilogue_begin)
549 }
550 LineInstruction::SetIsa(isa) => write!(f, "{} to {}", constants::DW_LNS_set_isa, isa),
551 LineInstruction::UnknownStandard0(opcode) => write!(f, "Unknown {}", opcode),
552 LineInstruction::UnknownStandard1(opcode, arg) => {
553 write!(f, "Unknown {} with operand {}", opcode, arg)
554 }
555 LineInstruction::UnknownStandardN(opcode, ref args) => {
556 write!(f, "Unknown {} with operands {:?}", opcode, args)
557 }
558 LineInstruction::EndSequence => write!(f, "{}", constants::DW_LNE_end_sequence),
559 LineInstruction::SetAddress(address) => {
560 write!(f, "{} to {}", constants::DW_LNE_set_address, address)
561 }
562 LineInstruction::DefineFile(_) => write!(f, "{}", constants::DW_LNE_define_file),
563 LineInstruction::SetDiscriminator(discr) => {
564 write!(f, "{} to {}", constants::DW_LNE_set_discriminator, discr)
565 }
566 LineInstruction::UnknownExtended(opcode, _) => write!(f, "Unknown {}", opcode),
567 }
568 }
569}
570
571#[deprecated(note = "OpcodesIter has been renamed to LineInstructions, use that instead.")]
573pub type OpcodesIter<R> = LineInstructions<R>;
574
575#[derive(Clone, Debug)]
581pub struct LineInstructions<R: Reader> {
582 input: R,
583}
584
585impl<R: Reader> LineInstructions<R> {
586 fn remove_trailing(&self, other: &LineInstructions<R>) -> Result<LineInstructions<R>> {
587 let offset = other.input.offset_from(&self.input);
588 let mut input = self.input.clone();
589 input.truncate(offset)?;
590 Ok(LineInstructions { input })
591 }
592}
593
594impl<R: Reader> LineInstructions<R> {
595 #[inline(always)]
606 pub fn next_instruction(
607 &mut self,
608 header: &LineProgramHeader<R>,
609 ) -> Result<Option<LineInstruction<R>>> {
610 if self.input.is_empty() {
611 return Ok(None);
612 }
613
614 match LineInstruction::parse(header, &mut self.input) {
615 Ok(instruction) => Ok(Some(instruction)),
616 Err(e) => {
617 self.input.empty();
618 Err(e)
619 }
620 }
621 }
622}
623
624#[deprecated(note = "LineNumberRow has been renamed to LineRow, use that instead.")]
626pub type LineNumberRow = LineRow;
627
628#[derive(Clone, Copy, Debug, PartialEq, Eq)]
632pub struct LineRow {
633 tombstone: bool,
634 address: Wrapping<u64>,
635 op_index: Wrapping<u64>,
636 file: u64,
637 line: Wrapping<u64>,
638 column: u64,
639 is_stmt: bool,
640 basic_block: bool,
641 end_sequence: bool,
642 prologue_end: bool,
643 epilogue_begin: bool,
644 isa: u64,
645 discriminator: u64,
646}
647
648impl LineRow {
649 pub fn new<R: Reader>(header: &LineProgramHeader<R>) -> Self {
651 LineRow {
652 tombstone: false,
655 address: Wrapping(0),
656 op_index: Wrapping(0),
657 file: 1,
658 line: Wrapping(1),
659 column: 0,
660 is_stmt: header.line_encoding.default_is_stmt,
662 basic_block: false,
663 end_sequence: false,
664 prologue_end: false,
665 epilogue_begin: false,
666 isa: 0,
671 discriminator: 0,
672 }
673 }
674
675 #[inline]
678 pub fn address(&self) -> u64 {
679 self.address.0
680 }
681
682 #[inline]
690 pub fn op_index(&self) -> u64 {
691 self.op_index.0
692 }
693
694 #[inline]
697 pub fn file_index(&self) -> u64 {
698 self.file
699 }
700
701 #[inline]
703 pub fn file<'header, R: Reader>(
704 &self,
705 header: &'header LineProgramHeader<R>,
706 ) -> Option<&'header FileEntry<R>> {
707 header.file(self.file)
708 }
709
710 #[inline]
715 pub fn line(&self) -> Option<NonZeroU64> {
716 NonZeroU64::new(self.line.0)
717 }
718
719 #[inline]
723 pub fn column(&self) -> ColumnType {
724 NonZeroU64::new(self.column)
725 .map(ColumnType::Column)
726 .unwrap_or(ColumnType::LeftEdge)
727 }
728
729 #[inline]
734 pub fn is_stmt(&self) -> bool {
735 self.is_stmt
736 }
737
738 #[inline]
741 pub fn basic_block(&self) -> bool {
742 self.basic_block
743 }
744
745 #[inline]
750 pub fn end_sequence(&self) -> bool {
751 self.end_sequence
752 }
753
754 #[inline]
758 pub fn prologue_end(&self) -> bool {
759 self.prologue_end
760 }
761
762 #[inline]
766 pub fn epilogue_begin(&self) -> bool {
767 self.epilogue_begin
768 }
769
770 #[inline]
779 pub fn isa(&self) -> u64 {
780 self.isa
781 }
782
783 #[inline]
790 pub fn discriminator(&self) -> u64 {
791 self.discriminator
792 }
793
794 #[inline]
799 pub fn execute<R, Program>(
800 &mut self,
801 instruction: LineInstruction<R>,
802 program: &mut Program,
803 ) -> bool
804 where
805 Program: LineProgram<R>,
806 R: Reader,
807 {
808 match instruction {
809 LineInstruction::Special(opcode) => {
810 self.exec_special_opcode(opcode, program.header());
811 true
812 }
813
814 LineInstruction::Copy => true,
815
816 LineInstruction::AdvancePc(operation_advance) => {
817 self.apply_operation_advance(operation_advance, program.header());
818 false
819 }
820
821 LineInstruction::AdvanceLine(line_increment) => {
822 self.apply_line_advance(line_increment);
823 false
824 }
825
826 LineInstruction::SetFile(file) => {
827 self.file = file;
828 false
829 }
830
831 LineInstruction::SetColumn(column) => {
832 self.column = column;
833 false
834 }
835
836 LineInstruction::NegateStatement => {
837 self.is_stmt = !self.is_stmt;
838 false
839 }
840
841 LineInstruction::SetBasicBlock => {
842 self.basic_block = true;
843 false
844 }
845
846 LineInstruction::ConstAddPc => {
847 let adjusted = self.adjust_opcode(255, program.header());
848 let operation_advance = adjusted / program.header().line_encoding.line_range;
849 self.apply_operation_advance(u64::from(operation_advance), program.header());
850 false
851 }
852
853 LineInstruction::FixedAddPc(operand) => {
854 self.address += Wrapping(u64::from(operand));
855 self.op_index.0 = 0;
856 false
857 }
858
859 LineInstruction::SetPrologueEnd => {
860 self.prologue_end = true;
861 false
862 }
863
864 LineInstruction::SetEpilogueBegin => {
865 self.epilogue_begin = true;
866 false
867 }
868
869 LineInstruction::SetIsa(isa) => {
870 self.isa = isa;
871 false
872 }
873
874 LineInstruction::EndSequence => {
875 self.end_sequence = true;
876 true
877 }
878
879 LineInstruction::SetAddress(address) => {
880 let tombstone_address = !0 >> (64 - program.header().encoding.address_size * 8);
881 self.tombstone = address == tombstone_address;
882 self.address.0 = address;
883 self.op_index.0 = 0;
884 false
885 }
886
887 LineInstruction::DefineFile(entry) => {
888 program.add_file(entry);
889 false
890 }
891
892 LineInstruction::SetDiscriminator(discriminator) => {
893 self.discriminator = discriminator;
894 false
895 }
896
897 LineInstruction::UnknownStandard0(_)
899 | LineInstruction::UnknownStandard1(_, _)
900 | LineInstruction::UnknownStandardN(_, _)
901 | LineInstruction::UnknownExtended(_, _) => false,
902 }
903 }
904
905 #[inline]
907 pub fn reset<R: Reader>(&mut self, header: &LineProgramHeader<R>) {
908 if self.end_sequence {
909 *self = Self::new(header);
912 } else {
913 self.discriminator = 0;
918 self.basic_block = false;
919 self.prologue_end = false;
920 self.epilogue_begin = false;
921 }
922 }
923
924 fn apply_line_advance(&mut self, line_increment: i64) {
926 if line_increment < 0 {
927 let decrement = -line_increment as u64;
928 if decrement <= self.line.0 {
929 self.line.0 -= decrement;
930 } else {
931 self.line.0 = 0;
932 }
933 } else {
934 self.line += Wrapping(line_increment as u64);
935 }
936 }
937
938 fn apply_operation_advance<R: Reader>(
940 &mut self,
941 operation_advance: u64,
942 header: &LineProgramHeader<R>,
943 ) {
944 let operation_advance = Wrapping(operation_advance);
945
946 let minimum_instruction_length = u64::from(header.line_encoding.minimum_instruction_length);
947 let minimum_instruction_length = Wrapping(minimum_instruction_length);
948
949 let maximum_operations_per_instruction =
950 u64::from(header.line_encoding.maximum_operations_per_instruction);
951 let maximum_operations_per_instruction = Wrapping(maximum_operations_per_instruction);
952
953 if maximum_operations_per_instruction.0 == 1 {
954 self.address += minimum_instruction_length * operation_advance;
955 self.op_index.0 = 0;
956 } else {
957 let op_index_with_advance = self.op_index + operation_advance;
958 self.address += minimum_instruction_length
959 * (op_index_with_advance / maximum_operations_per_instruction);
960 self.op_index = op_index_with_advance % maximum_operations_per_instruction;
961 }
962 }
963
964 #[inline]
965 fn adjust_opcode<R: Reader>(&self, opcode: u8, header: &LineProgramHeader<R>) -> u8 {
966 opcode - header.opcode_base
967 }
968
969 fn exec_special_opcode<R: Reader>(&mut self, opcode: u8, header: &LineProgramHeader<R>) {
971 let adjusted_opcode = self.adjust_opcode(opcode, header);
972
973 let line_range = header.line_encoding.line_range;
974 let line_advance = adjusted_opcode % line_range;
975 let operation_advance = adjusted_opcode / line_range;
976
977 let line_base = i64::from(header.line_encoding.line_base);
979 self.apply_line_advance(line_base + i64::from(line_advance));
980
981 self.apply_operation_advance(u64::from(operation_advance), header);
983 }
984}
985
986#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
988pub enum ColumnType {
989 LeftEdge,
992 Column(NonZeroU64),
994}
995
996#[deprecated(note = "LineNumberSequence has been renamed to LineSequence, use that instead.")]
998pub type LineNumberSequence<R> = LineSequence<R>;
999
1000#[derive(Clone, Debug)]
1004pub struct LineSequence<R: Reader> {
1005 pub start: u64,
1008 pub end: u64,
1011 instructions: LineInstructions<R>,
1012}
1013
1014#[deprecated(
1016 note = "LineNumberProgramHeader has been renamed to LineProgramHeader, use that instead."
1017)]
1018pub type LineNumberProgramHeader<R, Offset> = LineProgramHeader<R, Offset>;
1019
1020#[derive(Clone, Debug, Eq, PartialEq)]
1023pub struct LineProgramHeader<R, Offset = <R as Reader>::Offset>
1024where
1025 R: Reader<Offset = Offset>,
1026 Offset: ReaderOffset,
1027{
1028 encoding: Encoding,
1029 offset: DebugLineOffset<Offset>,
1030 unit_length: Offset,
1031
1032 header_length: Offset,
1033
1034 line_encoding: LineEncoding,
1035
1036 opcode_base: u8,
1038
1039 standard_opcode_lengths: R,
1044
1045 directory_entry_format: Vec<FileEntryFormat>,
1047
1048 include_directories: Vec<AttributeValue<R, Offset>>,
1057
1058 file_name_entry_format: Vec<FileEntryFormat>,
1060
1061 file_names: Vec<FileEntry<R, Offset>>,
1065
1066 program_buf: R,
1068
1069 comp_dir: Option<R>,
1071
1072 comp_file: Option<FileEntry<R, Offset>>,
1074}
1075
1076impl<R, Offset> LineProgramHeader<R, Offset>
1077where
1078 R: Reader<Offset = Offset>,
1079 Offset: ReaderOffset,
1080{
1081 pub fn offset(&self) -> DebugLineOffset<R::Offset> {
1083 self.offset
1084 }
1085
1086 pub fn unit_length(&self) -> R::Offset {
1089 self.unit_length
1090 }
1091
1092 pub fn encoding(&self) -> Encoding {
1094 self.encoding
1095 }
1096
1097 pub fn version(&self) -> u16 {
1099 self.encoding.version
1100 }
1101
1102 pub fn header_length(&self) -> R::Offset {
1105 self.header_length
1106 }
1107
1108 pub fn address_size(&self) -> u8 {
1110 self.encoding.address_size
1111 }
1112
1113 pub fn format(&self) -> Format {
1115 self.encoding.format
1116 }
1117
1118 pub fn line_encoding(&self) -> LineEncoding {
1120 self.line_encoding
1121 }
1122
1123 pub fn minimum_instruction_length(&self) -> u8 {
1126 self.line_encoding.minimum_instruction_length
1127 }
1128
1129 pub fn maximum_operations_per_instruction(&self) -> u8 {
1132 self.line_encoding.maximum_operations_per_instruction
1133 }
1134
1135 pub fn default_is_stmt(&self) -> bool {
1138 self.line_encoding.default_is_stmt
1139 }
1140
1141 pub fn line_base(&self) -> i8 {
1143 self.line_encoding.line_base
1144 }
1145
1146 pub fn line_range(&self) -> u8 {
1148 self.line_encoding.line_range
1149 }
1150
1151 pub fn opcode_base(&self) -> u8 {
1153 self.opcode_base
1154 }
1155
1156 pub fn standard_opcode_lengths(&self) -> &R {
1159 &self.standard_opcode_lengths
1160 }
1161
1162 pub fn directory_entry_format(&self) -> &[FileEntryFormat] {
1164 &self.directory_entry_format[..]
1165 }
1166
1167 pub fn include_directories(&self) -> &[AttributeValue<R, Offset>] {
1172 &self.include_directories[..]
1173 }
1174
1175 pub fn directory(&self, directory: u64) -> Option<AttributeValue<R, Offset>> {
1179 if self.encoding.version <= 4 {
1180 if directory == 0 {
1181 self.comp_dir.clone().map(AttributeValue::String)
1182 } else {
1183 let directory = directory as usize - 1;
1184 self.include_directories.get(directory).cloned()
1185 }
1186 } else {
1187 self.include_directories.get(directory as usize).cloned()
1188 }
1189 }
1190
1191 pub fn file_name_entry_format(&self) -> &[FileEntryFormat] {
1193 &self.file_name_entry_format[..]
1194 }
1195
1196 pub fn file_has_timestamp(&self) -> bool {
1201 self.encoding.version <= 4
1202 || self
1203 .file_name_entry_format
1204 .iter()
1205 .any(|x| x.content_type == constants::DW_LNCT_timestamp)
1206 }
1207
1208 pub fn file_has_size(&self) -> bool {
1213 self.encoding.version <= 4
1214 || self
1215 .file_name_entry_format
1216 .iter()
1217 .any(|x| x.content_type == constants::DW_LNCT_size)
1218 }
1219
1220 pub fn file_has_md5(&self) -> bool {
1222 self.file_name_entry_format
1223 .iter()
1224 .any(|x| x.content_type == constants::DW_LNCT_MD5)
1225 }
1226
1227 pub fn file_names(&self) -> &[FileEntry<R, Offset>] {
1229 &self.file_names[..]
1230 }
1231
1232 pub fn file(&self, file: u64) -> Option<&FileEntry<R, Offset>> {
1238 if self.encoding.version <= 4 {
1239 if file == 0 {
1240 self.comp_file.as_ref()
1241 } else {
1242 let file = file as usize - 1;
1243 self.file_names.get(file)
1244 }
1245 } else {
1246 self.file_names.get(file as usize)
1247 }
1248 }
1249
1250 pub fn raw_program_buf(&self) -> R {
1269 self.program_buf.clone()
1270 }
1271
1272 pub fn instructions(&self) -> LineInstructions<R> {
1275 LineInstructions {
1276 input: self.program_buf.clone(),
1277 }
1278 }
1279
1280 fn parse(
1281 input: &mut R,
1282 offset: DebugLineOffset<Offset>,
1283 mut address_size: u8,
1284 mut comp_dir: Option<R>,
1285 comp_name: Option<R>,
1286 ) -> Result<LineProgramHeader<R, Offset>> {
1287 let (unit_length, format) = input.read_initial_length()?;
1288 let rest = &mut input.split(unit_length)?;
1289
1290 let version = rest.read_u16()?;
1291 if version < 2 || version > 5 {
1292 return Err(Error::UnknownVersion(u64::from(version)));
1293 }
1294
1295 if version >= 5 {
1296 address_size = rest.read_u8()?;
1297 let segment_selector_size = rest.read_u8()?;
1298 if segment_selector_size != 0 {
1299 return Err(Error::UnsupportedSegmentSize);
1300 }
1301 }
1302
1303 let encoding = Encoding {
1304 format,
1305 version,
1306 address_size,
1307 };
1308
1309 let header_length = rest.read_length(format)?;
1310
1311 let mut program_buf = rest.clone();
1312 program_buf.skip(header_length)?;
1313 rest.truncate(header_length)?;
1314
1315 let minimum_instruction_length = rest.read_u8()?;
1316 if minimum_instruction_length == 0 {
1317 return Err(Error::MinimumInstructionLengthZero);
1318 }
1319
1320 let maximum_operations_per_instruction = if version >= 4 { rest.read_u8()? } else { 1 };
1323 if maximum_operations_per_instruction == 0 {
1324 return Err(Error::MaximumOperationsPerInstructionZero);
1325 }
1326
1327 let default_is_stmt = rest.read_u8()? != 0;
1328 let line_base = rest.read_i8()?;
1329 let line_range = rest.read_u8()?;
1330 if line_range == 0 {
1331 return Err(Error::LineRangeZero);
1332 }
1333 let line_encoding = LineEncoding {
1334 minimum_instruction_length,
1335 maximum_operations_per_instruction,
1336 default_is_stmt,
1337 line_base,
1338 line_range,
1339 };
1340
1341 let opcode_base = rest.read_u8()?;
1342 if opcode_base == 0 {
1343 return Err(Error::OpcodeBaseZero);
1344 }
1345
1346 let standard_opcode_count = R::Offset::from_u8(opcode_base - 1);
1347 let standard_opcode_lengths = rest.split(standard_opcode_count)?;
1348
1349 let directory_entry_format;
1350 let mut include_directories = Vec::new();
1351 if version <= 4 {
1352 directory_entry_format = Vec::new();
1353 loop {
1354 let directory = rest.read_null_terminated_slice()?;
1355 if directory.is_empty() {
1356 break;
1357 }
1358 include_directories.push(AttributeValue::String(directory));
1359 }
1360 } else {
1361 comp_dir = None;
1362 directory_entry_format = FileEntryFormat::parse(rest)?;
1363 let count = rest.read_uleb128()?;
1364 for _ in 0..count {
1365 include_directories.push(parse_directory_v5(
1366 rest,
1367 encoding,
1368 &directory_entry_format,
1369 )?);
1370 }
1371 }
1372
1373 let comp_file;
1374 let file_name_entry_format;
1375 let mut file_names = Vec::new();
1376 if version <= 4 {
1377 comp_file = comp_name.map(|name| FileEntry {
1378 path_name: AttributeValue::String(name),
1379 directory_index: 0,
1380 timestamp: 0,
1381 size: 0,
1382 md5: [0; 16],
1383 });
1384
1385 file_name_entry_format = Vec::new();
1386 loop {
1387 let path_name = rest.read_null_terminated_slice()?;
1388 if path_name.is_empty() {
1389 break;
1390 }
1391 file_names.push(FileEntry::parse(rest, path_name)?);
1392 }
1393 } else {
1394 comp_file = None;
1395 file_name_entry_format = FileEntryFormat::parse(rest)?;
1396 let count = rest.read_uleb128()?;
1397 for _ in 0..count {
1398 file_names.push(parse_file_v5(rest, encoding, &file_name_entry_format)?);
1399 }
1400 }
1401
1402 let header = LineProgramHeader {
1403 encoding,
1404 offset,
1405 unit_length,
1406 header_length,
1407 line_encoding,
1408 opcode_base,
1409 standard_opcode_lengths,
1410 directory_entry_format,
1411 include_directories,
1412 file_name_entry_format,
1413 file_names,
1414 program_buf,
1415 comp_dir,
1416 comp_file,
1417 };
1418 Ok(header)
1419 }
1420}
1421
1422#[deprecated(
1424 note = "IncompleteLineNumberProgram has been renamed to IncompleteLineProgram, use that instead."
1425)]
1426pub type IncompleteLineNumberProgram<R, Offset> = IncompleteLineProgram<R, Offset>;
1427
1428#[derive(Clone, Debug, Eq, PartialEq)]
1430pub struct IncompleteLineProgram<R, Offset = <R as Reader>::Offset>
1431where
1432 R: Reader<Offset = Offset>,
1433 Offset: ReaderOffset,
1434{
1435 header: LineProgramHeader<R, Offset>,
1436}
1437
1438impl<R, Offset> IncompleteLineProgram<R, Offset>
1439where
1440 R: Reader<Offset = Offset>,
1441 Offset: ReaderOffset,
1442{
1443 pub fn header(&self) -> &LineProgramHeader<R, Offset> {
1445 &self.header
1446 }
1447
1448 pub fn rows(self) -> OneShotLineRows<R, Offset> {
1451 OneShotLineRows::new(self)
1452 }
1453
1454 #[allow(clippy::type_complexity)]
1475 pub fn sequences(self) -> Result<(CompleteLineProgram<R, Offset>, Vec<LineSequence<R>>)> {
1476 let mut sequences = Vec::new();
1477 let mut rows = self.rows();
1478 let mut instructions = rows.instructions.clone();
1479 let mut sequence_start_addr = None;
1480 loop {
1481 let sequence_end_addr;
1482 if rows.next_row()?.is_none() {
1483 break;
1484 }
1485
1486 let row = &rows.row;
1487 if row.end_sequence() {
1488 sequence_end_addr = row.address();
1489 } else if sequence_start_addr.is_none() {
1490 sequence_start_addr = Some(row.address());
1491 continue;
1492 } else {
1493 continue;
1494 }
1495
1496 sequences.push(LineSequence {
1498 start: sequence_start_addr.unwrap_or(0),
1501 end: sequence_end_addr,
1502 instructions: instructions.remove_trailing(&rows.instructions)?,
1503 });
1504 sequence_start_addr = None;
1505 instructions = rows.instructions.clone();
1506 }
1507
1508 let program = CompleteLineProgram {
1509 header: rows.program.header,
1510 };
1511 Ok((program, sequences))
1512 }
1513}
1514
1515#[deprecated(
1517 note = "CompleteLineNumberProgram has been renamed to CompleteLineProgram, use that instead."
1518)]
1519pub type CompleteLineNumberProgram<R, Offset> = CompleteLineProgram<R, Offset>;
1520
1521#[derive(Clone, Debug, Eq, PartialEq)]
1523pub struct CompleteLineProgram<R, Offset = <R as Reader>::Offset>
1524where
1525 R: Reader<Offset = Offset>,
1526 Offset: ReaderOffset,
1527{
1528 header: LineProgramHeader<R, Offset>,
1529}
1530
1531impl<R, Offset> CompleteLineProgram<R, Offset>
1532where
1533 R: Reader<Offset = Offset>,
1534 Offset: ReaderOffset,
1535{
1536 pub fn header(&self) -> &LineProgramHeader<R, Offset> {
1538 &self.header
1539 }
1540
1541 pub fn resume_from<'program>(
1563 &'program self,
1564 sequence: &LineSequence<R>,
1565 ) -> ResumedLineRows<'program, R, Offset> {
1566 ResumedLineRows::resume(self, sequence)
1567 }
1568}
1569
1570#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1572pub struct FileEntry<R, Offset = <R as Reader>::Offset>
1573where
1574 R: Reader<Offset = Offset>,
1575 Offset: ReaderOffset,
1576{
1577 path_name: AttributeValue<R, Offset>,
1578 directory_index: u64,
1579 timestamp: u64,
1580 size: u64,
1581 md5: [u8; 16],
1582}
1583
1584impl<R, Offset> FileEntry<R, Offset>
1585where
1586 R: Reader<Offset = Offset>,
1587 Offset: ReaderOffset,
1588{
1589 fn parse(input: &mut R, path_name: R) -> Result<FileEntry<R, Offset>> {
1591 let directory_index = input.read_uleb128()?;
1592 let timestamp = input.read_uleb128()?;
1593 let size = input.read_uleb128()?;
1594
1595 let entry = FileEntry {
1596 path_name: AttributeValue::String(path_name),
1597 directory_index,
1598 timestamp,
1599 size,
1600 md5: [0; 16],
1601 };
1602
1603 Ok(entry)
1604 }
1605
1606 pub fn path_name(&self) -> AttributeValue<R, Offset> {
1612 self.path_name.clone()
1613 }
1614
1615 pub fn directory_index(&self) -> u64 {
1627 self.directory_index
1628 }
1629
1630 pub fn directory(&self, header: &LineProgramHeader<R>) -> Option<AttributeValue<R, Offset>> {
1634 header.directory(self.directory_index)
1635 }
1636
1637 pub fn timestamp(&self) -> u64 {
1640 self.timestamp
1641 }
1642
1643 #[doc(hidden)]
1647 pub fn last_modification(&self) -> u64 {
1648 self.timestamp
1649 }
1650
1651 pub fn size(&self) -> u64 {
1653 self.size
1654 }
1655
1656 #[doc(hidden)]
1660 pub fn length(&self) -> u64 {
1661 self.size
1662 }
1663
1664 pub fn md5(&self) -> &[u8; 16] {
1668 &self.md5
1669 }
1670}
1671
1672#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1674pub struct FileEntryFormat {
1675 pub content_type: constants::DwLnct,
1677
1678 pub form: constants::DwForm,
1680}
1681
1682impl FileEntryFormat {
1683 fn parse<R: Reader>(input: &mut R) -> Result<Vec<FileEntryFormat>> {
1684 let format_count = input.read_u8()? as usize;
1685 let mut format = Vec::with_capacity(format_count);
1686 let mut path_count = 0;
1687 for _ in 0..format_count {
1688 let content_type = input.read_uleb128()?;
1689 let content_type = if content_type > u64::from(u16::max_value()) {
1690 constants::DwLnct(u16::max_value())
1691 } else {
1692 constants::DwLnct(content_type as u16)
1693 };
1694 if content_type == constants::DW_LNCT_path {
1695 path_count += 1;
1696 }
1697
1698 let form = constants::DwForm(input.read_uleb128_u16()?);
1699
1700 format.push(FileEntryFormat { content_type, form });
1701 }
1702 if path_count != 1 {
1703 return Err(Error::MissingFileEntryFormatPath);
1704 }
1705 Ok(format)
1706 }
1707}
1708
1709fn parse_directory_v5<R: Reader>(
1710 input: &mut R,
1711 encoding: Encoding,
1712 formats: &[FileEntryFormat],
1713) -> Result<AttributeValue<R>> {
1714 let mut path_name = None;
1715
1716 for format in formats {
1717 let value = parse_attribute(input, encoding, format.form)?;
1718 if format.content_type == constants::DW_LNCT_path {
1719 path_name = Some(value);
1720 }
1721 }
1722
1723 Ok(path_name.unwrap())
1724}
1725
1726fn parse_file_v5<R: Reader>(
1727 input: &mut R,
1728 encoding: Encoding,
1729 formats: &[FileEntryFormat],
1730) -> Result<FileEntry<R>> {
1731 let mut path_name = None;
1732 let mut directory_index = 0;
1733 let mut timestamp = 0;
1734 let mut size = 0;
1735 let mut md5 = [0; 16];
1736
1737 for format in formats {
1738 let value = parse_attribute(input, encoding, format.form)?;
1739 match format.content_type {
1740 constants::DW_LNCT_path => path_name = Some(value),
1741 constants::DW_LNCT_directory_index => {
1742 if let Some(value) = value.udata_value() {
1743 directory_index = value;
1744 }
1745 }
1746 constants::DW_LNCT_timestamp => {
1747 if let Some(value) = value.udata_value() {
1748 timestamp = value;
1749 }
1750 }
1751 constants::DW_LNCT_size => {
1752 if let Some(value) = value.udata_value() {
1753 size = value;
1754 }
1755 }
1756 constants::DW_LNCT_MD5 => {
1757 if let AttributeValue::Block(mut value) = value {
1758 if value.len().into_u64() == 16 {
1759 md5 = value.read_u8_array()?;
1760 }
1761 }
1762 }
1763 _ => {}
1765 }
1766 }
1767
1768 Ok(FileEntry {
1769 path_name: path_name.unwrap(),
1770 directory_index,
1771 timestamp,
1772 size,
1773 md5,
1774 })
1775}
1776
1777fn parse_attribute<R: Reader>(
1779 input: &mut R,
1780 encoding: Encoding,
1781 form: constants::DwForm,
1782) -> Result<AttributeValue<R>> {
1783 Ok(match form {
1784 constants::DW_FORM_block1 => {
1785 let len = input.read_u8().map(R::Offset::from_u8)?;
1786 let block = input.split(len)?;
1787 AttributeValue::Block(block)
1788 }
1789 constants::DW_FORM_block2 => {
1790 let len = input.read_u16().map(R::Offset::from_u16)?;
1791 let block = input.split(len)?;
1792 AttributeValue::Block(block)
1793 }
1794 constants::DW_FORM_block4 => {
1795 let len = input.read_u32().map(R::Offset::from_u32)?;
1796 let block = input.split(len)?;
1797 AttributeValue::Block(block)
1798 }
1799 constants::DW_FORM_block => {
1800 let len = input.read_uleb128().and_then(R::Offset::from_u64)?;
1801 let block = input.split(len)?;
1802 AttributeValue::Block(block)
1803 }
1804 constants::DW_FORM_data1 => {
1805 let data = input.read_u8()?;
1806 AttributeValue::Data1(data)
1807 }
1808 constants::DW_FORM_data2 => {
1809 let data = input.read_u16()?;
1810 AttributeValue::Data2(data)
1811 }
1812 constants::DW_FORM_data4 => {
1813 let data = input.read_u32()?;
1814 AttributeValue::Data4(data)
1815 }
1816 constants::DW_FORM_data8 => {
1817 let data = input.read_u64()?;
1818 AttributeValue::Data8(data)
1819 }
1820 constants::DW_FORM_data16 => {
1821 let block = input.split(R::Offset::from_u8(16))?;
1822 AttributeValue::Block(block)
1823 }
1824 constants::DW_FORM_udata => {
1825 let data = input.read_uleb128()?;
1826 AttributeValue::Udata(data)
1827 }
1828 constants::DW_FORM_sdata => {
1829 let data = input.read_sleb128()?;
1830 AttributeValue::Sdata(data)
1831 }
1832 constants::DW_FORM_flag => {
1833 let present = input.read_u8()?;
1834 AttributeValue::Flag(present != 0)
1835 }
1836 constants::DW_FORM_sec_offset => {
1837 let offset = input.read_offset(encoding.format)?;
1838 AttributeValue::SecOffset(offset)
1839 }
1840 constants::DW_FORM_string => {
1841 let string = input.read_null_terminated_slice()?;
1842 AttributeValue::String(string)
1843 }
1844 constants::DW_FORM_strp => {
1845 let offset = input.read_offset(encoding.format)?;
1846 AttributeValue::DebugStrRef(DebugStrOffset(offset))
1847 }
1848 constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt => {
1849 let offset = input.read_offset(encoding.format)?;
1850 AttributeValue::DebugStrRefSup(DebugStrOffset(offset))
1851 }
1852 constants::DW_FORM_line_strp => {
1853 let offset = input.read_offset(encoding.format)?;
1854 AttributeValue::DebugLineStrRef(DebugLineStrOffset(offset))
1855 }
1856 constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index => {
1857 let index = input.read_uleb128().and_then(R::Offset::from_u64)?;
1858 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1859 }
1860 constants::DW_FORM_strx1 => {
1861 let index = input.read_u8().map(R::Offset::from_u8)?;
1862 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1863 }
1864 constants::DW_FORM_strx2 => {
1865 let index = input.read_u16().map(R::Offset::from_u16)?;
1866 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1867 }
1868 constants::DW_FORM_strx3 => {
1869 let index = input.read_uint(3).and_then(R::Offset::from_u64)?;
1870 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1871 }
1872 constants::DW_FORM_strx4 => {
1873 let index = input.read_u32().map(R::Offset::from_u32)?;
1874 AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index))
1875 }
1876 _ => {
1877 return Err(Error::UnknownForm(form));
1878 }
1879 })
1880}
1881
1882#[cfg(test)]
1883mod tests {
1884 use super::*;
1885 use crate::constants;
1886 use crate::endianity::LittleEndian;
1887 use crate::read::{EndianSlice, Error};
1888 use crate::test_util::GimliSectionMethods;
1889 use core::u64;
1890 use core::u8;
1891 use test_assembler::{Endian, Label, LabelMaker, Section};
1892
1893 #[test]
1894 fn test_parse_debug_line_32_ok() {
1895 #[rustfmt::skip]
1896 let buf = [
1897 0x3e, 0x00, 0x00, 0x00,
1899 0x04, 0x00,
1901 0x28, 0x00, 0x00, 0x00,
1903 0x01,
1905 0x01,
1907 0x01,
1909 0x00,
1911 0x01,
1913 0x03,
1915 0x01, 0x02,
1917 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
1919 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
1922 0x00,
1923 0x00,
1924 0x00,
1925 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
1927 0x01,
1928 0x00,
1929 0x00,
1930 0x00,
1932
1933 0x00, 0x00, 0x00, 0x00,
1935 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00,
1938
1939 0x00, 0x00, 0x00, 0x00,
1941 0x00, 0x00, 0x00, 0x00,
1942 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x00, 0x00, 0x00,
1944 ];
1945
1946 let rest = &mut EndianSlice::new(&buf, LittleEndian);
1947 let comp_dir = EndianSlice::new(b"/comp_dir", LittleEndian);
1948 let comp_name = EndianSlice::new(b"/comp_name", LittleEndian);
1949
1950 let header =
1951 LineProgramHeader::parse(rest, DebugLineOffset(0), 4, Some(comp_dir), Some(comp_name))
1952 .expect("should parse header ok");
1953
1954 assert_eq!(
1955 *rest,
1956 EndianSlice::new(&buf[buf.len() - 16..], LittleEndian)
1957 );
1958
1959 assert_eq!(header.offset, DebugLineOffset(0));
1960 assert_eq!(header.version(), 4);
1961 assert_eq!(header.minimum_instruction_length(), 1);
1962 assert_eq!(header.maximum_operations_per_instruction(), 1);
1963 assert!(header.default_is_stmt());
1964 assert_eq!(header.line_base(), 0);
1965 assert_eq!(header.line_range(), 1);
1966 assert_eq!(header.opcode_base(), 3);
1967 assert_eq!(header.directory(0), Some(AttributeValue::String(comp_dir)));
1968 assert_eq!(
1969 header.file(0).unwrap().path_name,
1970 AttributeValue::String(comp_name)
1971 );
1972
1973 let expected_lengths = [1, 2];
1974 assert_eq!(header.standard_opcode_lengths().slice(), &expected_lengths);
1975
1976 let expected_include_directories = [
1977 AttributeValue::String(EndianSlice::new(b"/inc", LittleEndian)),
1978 AttributeValue::String(EndianSlice::new(b"/inc2", LittleEndian)),
1979 ];
1980 assert_eq!(header.include_directories(), &expected_include_directories);
1981
1982 let expected_file_names = [
1983 FileEntry {
1984 path_name: AttributeValue::String(EndianSlice::new(b"foo.rs", LittleEndian)),
1985 directory_index: 0,
1986 timestamp: 0,
1987 size: 0,
1988 md5: [0; 16],
1989 },
1990 FileEntry {
1991 path_name: AttributeValue::String(EndianSlice::new(b"bar.h", LittleEndian)),
1992 directory_index: 1,
1993 timestamp: 0,
1994 size: 0,
1995 md5: [0; 16],
1996 },
1997 ];
1998 assert_eq!(header.file_names(), &expected_file_names);
1999 }
2000
2001 #[test]
2002 fn test_parse_debug_line_header_length_too_short() {
2003 #[rustfmt::skip]
2004 let buf = [
2005 0x3e, 0x00, 0x00, 0x00,
2007 0x04, 0x00,
2009 0x15, 0x00, 0x00, 0x00,
2011 0x01,
2013 0x01,
2015 0x01,
2017 0x00,
2019 0x01,
2021 0x03,
2023 0x01, 0x02,
2025 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
2027 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
2030 0x00,
2031 0x00,
2032 0x00,
2033 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
2035 0x01,
2036 0x00,
2037 0x00,
2038 0x00,
2040
2041 0x00, 0x00, 0x00, 0x00,
2043 0x00, 0x00, 0x00, 0x00,
2044 0x00, 0x00, 0x00, 0x00,
2045 0x00, 0x00, 0x00, 0x00,
2046
2047 0x00, 0x00, 0x00, 0x00,
2049 0x00, 0x00, 0x00, 0x00,
2050 0x00, 0x00, 0x00, 0x00,
2051 0x00, 0x00, 0x00, 0x00,
2052 ];
2053
2054 let input = &mut EndianSlice::new(&buf, LittleEndian);
2055
2056 match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) {
2057 Err(Error::UnexpectedEof(_)) => {}
2058 otherwise => panic!("Unexpected result: {:?}", otherwise),
2059 }
2060 }
2061
2062 #[test]
2063 fn test_parse_debug_line_unit_length_too_short() {
2064 #[rustfmt::skip]
2065 let buf = [
2066 0x28, 0x00, 0x00, 0x00,
2068 0x04, 0x00,
2070 0x28, 0x00, 0x00, 0x00,
2072 0x01,
2074 0x01,
2076 0x01,
2078 0x00,
2080 0x01,
2082 0x03,
2084 0x01, 0x02,
2086 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
2088 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
2091 0x00,
2092 0x00,
2093 0x00,
2094 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
2096 0x01,
2097 0x00,
2098 0x00,
2099 0x00,
2101
2102 0x00, 0x00, 0x00, 0x00,
2104 0x00, 0x00, 0x00, 0x00,
2105 0x00, 0x00, 0x00, 0x00,
2106 0x00, 0x00, 0x00, 0x00,
2107
2108 0x00, 0x00, 0x00, 0x00,
2110 0x00, 0x00, 0x00, 0x00,
2111 0x00, 0x00, 0x00, 0x00,
2112 0x00, 0x00, 0x00, 0x00,
2113 ];
2114
2115 let input = &mut EndianSlice::new(&buf, LittleEndian);
2116
2117 match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) {
2118 Err(Error::UnexpectedEof(_)) => {}
2119 otherwise => panic!("Unexpected result: {:?}", otherwise),
2120 }
2121 }
2122
2123 const OPCODE_BASE: u8 = 13;
2124 const STANDARD_OPCODE_LENGTHS: &[u8] = &[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1];
2125
2126 fn make_test_header(
2127 buf: EndianSlice<'_, LittleEndian>,
2128 ) -> LineProgramHeader<EndianSlice<'_, LittleEndian>> {
2129 let encoding = Encoding {
2130 format: Format::Dwarf32,
2131 version: 4,
2132 address_size: 8,
2133 };
2134 let line_encoding = LineEncoding {
2135 line_base: -3,
2136 line_range: 12,
2137 ..Default::default()
2138 };
2139 LineProgramHeader {
2140 encoding,
2141 offset: DebugLineOffset(0),
2142 unit_length: 1,
2143 header_length: 1,
2144 line_encoding,
2145 opcode_base: OPCODE_BASE,
2146 standard_opcode_lengths: EndianSlice::new(STANDARD_OPCODE_LENGTHS, LittleEndian),
2147 file_names: vec![
2148 FileEntry {
2149 path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)),
2150 directory_index: 0,
2151 timestamp: 0,
2152 size: 0,
2153 md5: [0; 16],
2154 },
2155 FileEntry {
2156 path_name: AttributeValue::String(EndianSlice::new(b"bar.rs", LittleEndian)),
2157 directory_index: 0,
2158 timestamp: 0,
2159 size: 0,
2160 md5: [0; 16],
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 }),
2408 );
2409
2410 let operands = [1, 2, 3, 4, 5, 6];
2412 let opcode = constants::DwLne(99);
2413 test(
2414 opcode,
2415 operands,
2416 LineInstruction::UnknownExtended(opcode, EndianSlice::new(&operands, LittleEndian)),
2417 );
2418 }
2419
2420 #[test]
2421 fn test_file_entry_directory() {
2422 let path_name = [b'f', b'o', b'o', b'.', b'r', b's', 0];
2423
2424 let mut file = FileEntry {
2425 path_name: AttributeValue::String(EndianSlice::new(&path_name, LittleEndian)),
2426 directory_index: 1,
2427 timestamp: 0,
2428 size: 0,
2429 md5: [0; 16],
2430 };
2431
2432 let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
2433
2434 let dir = AttributeValue::String(EndianSlice::new(b"dir", LittleEndian));
2435 header.include_directories.push(dir);
2436
2437 assert_eq!(file.directory(&header), Some(dir));
2438
2439 file.directory_index = 0;
2441 assert_eq!(file.directory(&header), None);
2442 }
2443
2444 fn assert_exec_opcode<'input>(
2445 header: LineProgramHeader<EndianSlice<'input, LittleEndian>>,
2446 mut registers: LineRow,
2447 opcode: LineInstruction<EndianSlice<'input, LittleEndian>>,
2448 expected_registers: LineRow,
2449 expect_new_row: bool,
2450 ) {
2451 let mut program = IncompleteLineProgram { header };
2452 let is_new_row = registers.execute(opcode, &mut program);
2453
2454 assert_eq!(is_new_row, expect_new_row);
2455 assert_eq!(registers, expected_registers);
2456 }
2457
2458 #[test]
2459 fn test_exec_special_noop() {
2460 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2461
2462 let initial_registers = LineRow::new(&header);
2463 let opcode = LineInstruction::Special(16);
2464 let expected_registers = initial_registers;
2465
2466 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2467 }
2468
2469 #[test]
2470 fn test_exec_special_negative_line_advance() {
2471 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2472
2473 let mut initial_registers = LineRow::new(&header);
2474 initial_registers.line.0 = 10;
2475
2476 let opcode = LineInstruction::Special(13);
2477
2478 let mut expected_registers = initial_registers;
2479 expected_registers.line.0 -= 3;
2480
2481 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2482 }
2483
2484 #[test]
2485 fn test_exec_special_positive_line_advance() {
2486 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2487
2488 let initial_registers = LineRow::new(&header);
2489
2490 let opcode = LineInstruction::Special(19);
2491
2492 let mut expected_registers = initial_registers;
2493 expected_registers.line.0 += 3;
2494
2495 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2496 }
2497
2498 #[test]
2499 fn test_exec_special_positive_address_advance() {
2500 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2501
2502 let initial_registers = LineRow::new(&header);
2503
2504 let opcode = LineInstruction::Special(52);
2505
2506 let mut expected_registers = initial_registers;
2507 expected_registers.address.0 += 3;
2508
2509 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2510 }
2511
2512 #[test]
2513 fn test_exec_special_positive_address_and_line_advance() {
2514 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2515
2516 let initial_registers = LineRow::new(&header);
2517
2518 let opcode = LineInstruction::Special(55);
2519
2520 let mut expected_registers = initial_registers;
2521 expected_registers.address.0 += 3;
2522 expected_registers.line.0 += 3;
2523
2524 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2525 }
2526
2527 #[test]
2528 fn test_exec_special_positive_address_and_negative_line_advance() {
2529 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2530
2531 let mut initial_registers = LineRow::new(&header);
2532 initial_registers.line.0 = 10;
2533
2534 let opcode = LineInstruction::Special(49);
2535
2536 let mut expected_registers = initial_registers;
2537 expected_registers.address.0 += 3;
2538 expected_registers.line.0 -= 3;
2539
2540 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2541 }
2542
2543 #[test]
2544 fn test_exec_special_line_underflow() {
2545 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2546
2547 let mut initial_registers = LineRow::new(&header);
2548 initial_registers.line.0 = 2;
2549
2550 let opcode = LineInstruction::Special(13);
2552
2553 let mut expected_registers = initial_registers;
2554 expected_registers.line.0 = 0;
2557
2558 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2559 }
2560
2561 #[test]
2562 fn test_exec_copy() {
2563 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2564
2565 let mut initial_registers = LineRow::new(&header);
2566 initial_registers.address.0 = 1337;
2567 initial_registers.line.0 = 42;
2568
2569 let opcode = LineInstruction::Copy;
2570
2571 let expected_registers = initial_registers;
2572
2573 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2574 }
2575
2576 #[test]
2577 fn test_exec_advance_pc() {
2578 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2579 let initial_registers = LineRow::new(&header);
2580 let opcode = LineInstruction::AdvancePc(42);
2581
2582 let mut expected_registers = initial_registers;
2583 expected_registers.address.0 += 42;
2584
2585 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2586 }
2587
2588 #[test]
2589 fn test_exec_advance_pc_overflow() {
2590 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2591 let opcode = LineInstruction::AdvancePc(42);
2592
2593 let mut initial_registers = LineRow::new(&header);
2594 initial_registers.address.0 = u64::MAX;
2595
2596 let mut expected_registers = initial_registers;
2597 expected_registers.address.0 = 41;
2598
2599 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2600 }
2601
2602 #[test]
2603 fn test_exec_advance_line() {
2604 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2605 let initial_registers = LineRow::new(&header);
2606 let opcode = LineInstruction::AdvanceLine(42);
2607
2608 let mut expected_registers = initial_registers;
2609 expected_registers.line.0 += 42;
2610
2611 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2612 }
2613
2614 #[test]
2615 fn test_exec_advance_line_overflow() {
2616 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2617 let opcode = LineInstruction::AdvanceLine(42);
2618
2619 let mut initial_registers = LineRow::new(&header);
2620 initial_registers.line.0 = u64::MAX;
2621
2622 let mut expected_registers = initial_registers;
2623 expected_registers.line.0 = 41;
2624
2625 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2626 }
2627
2628 #[test]
2629 fn test_exec_set_file_in_bounds() {
2630 for file_idx in 1..3 {
2631 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2632 let initial_registers = LineRow::new(&header);
2633 let opcode = LineInstruction::SetFile(file_idx);
2634
2635 let mut expected_registers = initial_registers;
2636 expected_registers.file = file_idx;
2637
2638 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2639 }
2640 }
2641
2642 #[test]
2643 fn test_exec_set_file_out_of_bounds() {
2644 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2645 let initial_registers = LineRow::new(&header);
2646 let opcode = LineInstruction::SetFile(100);
2647
2648 let mut expected_registers = initial_registers;
2655 expected_registers.file = 100;
2656
2657 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2658 }
2659
2660 #[test]
2661 fn test_file_entry_file_index_out_of_bounds() {
2662 let out_of_bounds_indices = [0, 100];
2665
2666 for file_idx in &out_of_bounds_indices[..] {
2667 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2668 let mut row = LineRow::new(&header);
2669
2670 row.file = *file_idx;
2671
2672 assert_eq!(row.file(&header), None);
2673 }
2674 }
2675
2676 #[test]
2677 fn test_file_entry_file_index_in_bounds() {
2678 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2679 let mut row = LineRow::new(&header);
2680
2681 row.file = 2;
2682
2683 assert_eq!(row.file(&header), Some(&header.file_names()[1]));
2684 }
2685
2686 #[test]
2687 fn test_exec_set_column() {
2688 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2689 let initial_registers = LineRow::new(&header);
2690 let opcode = LineInstruction::SetColumn(42);
2691
2692 let mut expected_registers = initial_registers;
2693 expected_registers.column = 42;
2694
2695 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2696 }
2697
2698 #[test]
2699 fn test_exec_negate_statement() {
2700 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2701 let initial_registers = LineRow::new(&header);
2702 let opcode = LineInstruction::NegateStatement;
2703
2704 let mut expected_registers = initial_registers;
2705 expected_registers.is_stmt = !initial_registers.is_stmt;
2706
2707 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2708 }
2709
2710 #[test]
2711 fn test_exec_set_basic_block() {
2712 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2713
2714 let mut initial_registers = LineRow::new(&header);
2715 initial_registers.basic_block = false;
2716
2717 let opcode = LineInstruction::SetBasicBlock;
2718
2719 let mut expected_registers = initial_registers;
2720 expected_registers.basic_block = true;
2721
2722 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2723 }
2724
2725 #[test]
2726 fn test_exec_const_add_pc() {
2727 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2728 let initial_registers = LineRow::new(&header);
2729 let opcode = LineInstruction::ConstAddPc;
2730
2731 let mut expected_registers = initial_registers;
2732 expected_registers.address.0 += 20;
2733
2734 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2735 }
2736
2737 #[test]
2738 fn test_exec_fixed_add_pc() {
2739 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2740
2741 let mut initial_registers = LineRow::new(&header);
2742 initial_registers.op_index.0 = 1;
2743
2744 let opcode = LineInstruction::FixedAddPc(10);
2745
2746 let mut expected_registers = initial_registers;
2747 expected_registers.address.0 += 10;
2748 expected_registers.op_index.0 = 0;
2749
2750 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2751 }
2752
2753 #[test]
2754 fn test_exec_set_prologue_end() {
2755 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2756
2757 let mut initial_registers = LineRow::new(&header);
2758 initial_registers.prologue_end = false;
2759
2760 let opcode = LineInstruction::SetPrologueEnd;
2761
2762 let mut expected_registers = initial_registers;
2763 expected_registers.prologue_end = true;
2764
2765 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2766 }
2767
2768 #[test]
2769 fn test_exec_set_isa() {
2770 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2771 let initial_registers = LineRow::new(&header);
2772 let opcode = LineInstruction::SetIsa(1993);
2773
2774 let mut expected_registers = initial_registers;
2775 expected_registers.isa = 1993;
2776
2777 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2778 }
2779
2780 #[test]
2781 fn test_exec_unknown_standard_0() {
2782 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2783 let initial_registers = LineRow::new(&header);
2784 let opcode = LineInstruction::UnknownStandard0(constants::DwLns(111));
2785 let expected_registers = initial_registers;
2786 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2787 }
2788
2789 #[test]
2790 fn test_exec_unknown_standard_1() {
2791 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2792 let initial_registers = LineRow::new(&header);
2793 let opcode = LineInstruction::UnknownStandard1(constants::DwLns(111), 2);
2794 let expected_registers = initial_registers;
2795 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2796 }
2797
2798 #[test]
2799 fn test_exec_unknown_standard_n() {
2800 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2801 let initial_registers = LineRow::new(&header);
2802 let opcode = LineInstruction::UnknownStandardN(
2803 constants::DwLns(111),
2804 EndianSlice::new(&[2, 2, 2], LittleEndian),
2805 );
2806 let expected_registers = initial_registers;
2807 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2808 }
2809
2810 #[test]
2811 fn test_exec_end_sequence() {
2812 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2813 let initial_registers = LineRow::new(&header);
2814 let opcode = LineInstruction::EndSequence;
2815
2816 let mut expected_registers = initial_registers;
2817 expected_registers.end_sequence = true;
2818
2819 assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
2820 }
2821
2822 #[test]
2823 fn test_exec_set_address() {
2824 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2825 let initial_registers = LineRow::new(&header);
2826 let opcode = LineInstruction::SetAddress(3030);
2827
2828 let mut expected_registers = initial_registers;
2829 expected_registers.address.0 = 3030;
2830
2831 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2832 }
2833
2834 #[test]
2835 fn test_exec_set_address_tombstone() {
2836 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2837 let initial_registers = LineRow::new(&header);
2838 let opcode = LineInstruction::SetAddress(!0);
2839
2840 let mut expected_registers = initial_registers;
2841 expected_registers.tombstone = true;
2842 expected_registers.address.0 = !0;
2843
2844 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2845 }
2846
2847 #[test]
2848 fn test_exec_define_file() {
2849 let mut program = make_test_program(EndianSlice::new(&[], LittleEndian));
2850 let mut row = LineRow::new(program.header());
2851
2852 let file = FileEntry {
2853 path_name: AttributeValue::String(EndianSlice::new(b"test.cpp", LittleEndian)),
2854 directory_index: 0,
2855 timestamp: 0,
2856 size: 0,
2857 md5: [0; 16],
2858 };
2859
2860 let opcode = LineInstruction::DefineFile(file);
2861 let is_new_row = row.execute(opcode, &mut program);
2862
2863 assert!(!is_new_row);
2864 assert_eq!(Some(&file), program.header().file_names.last());
2865 }
2866
2867 #[test]
2868 fn test_exec_set_discriminator() {
2869 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2870 let initial_registers = LineRow::new(&header);
2871 let opcode = LineInstruction::SetDiscriminator(9);
2872
2873 let mut expected_registers = initial_registers;
2874 expected_registers.discriminator = 9;
2875
2876 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2877 }
2878
2879 #[test]
2880 fn test_exec_unknown_extended() {
2881 let header = make_test_header(EndianSlice::new(&[], LittleEndian));
2882 let initial_registers = LineRow::new(&header);
2883 let opcode = LineInstruction::UnknownExtended(
2884 constants::DwLne(74),
2885 EndianSlice::new(&[], LittleEndian),
2886 );
2887 let expected_registers = initial_registers;
2888 assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
2889 }
2890
2891 #[allow(dead_code, unreachable_code, unused_variables)]
2894 #[allow(clippy::diverging_sub_expression)]
2895 fn test_line_rows_variance<'a, 'b>(_: &'a [u8], _: &'b [u8])
2896 where
2897 'a: 'b,
2898 {
2899 let a: &OneShotLineRows<EndianSlice<'a, LittleEndian>> = unimplemented!();
2900 let _: &OneShotLineRows<EndianSlice<'b, LittleEndian>> = a;
2901 }
2902
2903 #[test]
2904 fn test_parse_debug_line_v5_ok() {
2905 let expected_lengths = &[1, 2];
2906 let expected_program = &[0, 1, 2, 3, 4];
2907 let expected_rest = &[5, 6, 7, 8, 9];
2908 let expected_include_directories = [
2909 AttributeValue::String(EndianSlice::new(b"dir1", LittleEndian)),
2910 AttributeValue::String(EndianSlice::new(b"dir2", LittleEndian)),
2911 ];
2912 let expected_file_names = [
2913 FileEntry {
2914 path_name: AttributeValue::String(EndianSlice::new(b"file1", LittleEndian)),
2915 directory_index: 0,
2916 timestamp: 0,
2917 size: 0,
2918 md5: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
2919 },
2920 FileEntry {
2921 path_name: AttributeValue::String(EndianSlice::new(b"file2", LittleEndian)),
2922 directory_index: 1,
2923 timestamp: 0,
2924 size: 0,
2925 md5: [
2926 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
2927 ],
2928 },
2929 ];
2930
2931 for format in [Format::Dwarf32, Format::Dwarf64] {
2932 let length = Label::new();
2933 let header_length = Label::new();
2934 let start = Label::new();
2935 let header_start = Label::new();
2936 let end = Label::new();
2937 let header_end = Label::new();
2938 let section = Section::with_endian(Endian::Little)
2939 .initial_length(format, &length, &start)
2940 .D16(5)
2941 .D8(4)
2943 .D8(0)
2945 .word_label(format.word_size(), &header_length)
2946 .mark(&header_start)
2947 .D8(1)
2949 .D8(1)
2951 .D8(1)
2953 .D8(0)
2955 .D8(1)
2957 .D8(expected_lengths.len() as u8 + 1)
2959 .append_bytes(expected_lengths)
2961 .D8(1)
2963 .uleb(constants::DW_LNCT_path.0 as u64)
2964 .uleb(constants::DW_FORM_string.0 as u64)
2965 .D8(2)
2967 .append_bytes(b"dir1\0")
2968 .append_bytes(b"dir2\0")
2969 .D8(3)
2971 .uleb(constants::DW_LNCT_path.0 as u64)
2972 .uleb(constants::DW_FORM_string.0 as u64)
2973 .uleb(constants::DW_LNCT_directory_index.0 as u64)
2974 .uleb(constants::DW_FORM_data1.0 as u64)
2975 .uleb(constants::DW_LNCT_MD5.0 as u64)
2976 .uleb(constants::DW_FORM_data16.0 as u64)
2977 .D8(2)
2979 .append_bytes(b"file1\0")
2980 .D8(0)
2981 .append_bytes(&expected_file_names[0].md5)
2982 .append_bytes(b"file2\0")
2983 .D8(1)
2984 .append_bytes(&expected_file_names[1].md5)
2985 .mark(&header_end)
2986 .append_bytes(expected_program)
2988 .mark(&end)
2989 .append_bytes(expected_rest);
2991 length.set_const((&end - &start) as u64);
2992 header_length.set_const((&header_end - &header_start) as u64);
2993 let section = section.get_contents().unwrap();
2994
2995 let input = &mut EndianSlice::new(§ion, LittleEndian);
2996
2997 let header = LineProgramHeader::parse(input, DebugLineOffset(0), 0, None, None)
2998 .expect("should parse header ok");
2999
3000 assert_eq!(header.raw_program_buf().slice(), expected_program);
3001 assert_eq!(input.slice(), expected_rest);
3002
3003 assert_eq!(header.offset, DebugLineOffset(0));
3004 assert_eq!(header.version(), 5);
3005 assert_eq!(header.address_size(), 4);
3006 assert_eq!(header.minimum_instruction_length(), 1);
3007 assert_eq!(header.maximum_operations_per_instruction(), 1);
3008 assert!(header.default_is_stmt());
3009 assert_eq!(header.line_base(), 0);
3010 assert_eq!(header.line_range(), 1);
3011 assert_eq!(header.opcode_base(), expected_lengths.len() as u8 + 1);
3012 assert_eq!(header.standard_opcode_lengths().slice(), expected_lengths);
3013 assert_eq!(
3014 header.directory_entry_format(),
3015 &[FileEntryFormat {
3016 content_type: constants::DW_LNCT_path,
3017 form: constants::DW_FORM_string,
3018 }]
3019 );
3020 assert_eq!(header.include_directories(), expected_include_directories);
3021 assert_eq!(header.directory(0), Some(expected_include_directories[0]));
3022 assert_eq!(
3023 header.file_name_entry_format(),
3024 &[
3025 FileEntryFormat {
3026 content_type: constants::DW_LNCT_path,
3027 form: constants::DW_FORM_string,
3028 },
3029 FileEntryFormat {
3030 content_type: constants::DW_LNCT_directory_index,
3031 form: constants::DW_FORM_data1,
3032 },
3033 FileEntryFormat {
3034 content_type: constants::DW_LNCT_MD5,
3035 form: constants::DW_FORM_data16,
3036 }
3037 ]
3038 );
3039 assert_eq!(header.file_names(), expected_file_names);
3040 assert_eq!(header.file(0), Some(&expected_file_names[0]));
3041 }
3042 }
3043
3044 #[test]
3045 fn test_sequences() {
3046 #[rustfmt::skip]
3047 let buf = [
3048 94, 0x00, 0x00, 0x00,
3050 0x04, 0x00,
3052 0x28, 0x00, 0x00, 0x00,
3054 0x01,
3056 0x01,
3058 0x01,
3060 0x00,
3062 0x01,
3064 0x03,
3066 0x01, 0x02,
3068 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00,
3070 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00,
3073 0x00,
3074 0x00,
3075 0x00,
3076 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00,
3078 0x01,
3079 0x00,
3080 0x00,
3081 0x00,
3083
3084 0, 5, constants::DW_LNE_set_address.0, 1, 0, 0, 0,
3085 constants::DW_LNS_copy.0,
3086 constants::DW_LNS_advance_pc.0, 1,
3087 constants::DW_LNS_copy.0,
3088 constants::DW_LNS_advance_pc.0, 2,
3089 0, 1, constants::DW_LNE_end_sequence.0,
3090
3091 0, 5, constants::DW_LNE_set_address.0, 0xff, 0xff, 0xff, 0xff,
3093 constants::DW_LNS_copy.0,
3094 constants::DW_LNS_advance_pc.0, 1,
3095 constants::DW_LNS_copy.0,
3096 constants::DW_LNS_advance_pc.0, 2,
3097 0, 1, constants::DW_LNE_end_sequence.0,
3098
3099 0, 5, constants::DW_LNE_set_address.0, 11, 0, 0, 0,
3100 constants::DW_LNS_copy.0,
3101 constants::DW_LNS_advance_pc.0, 1,
3102 constants::DW_LNS_copy.0,
3103 constants::DW_LNS_advance_pc.0, 2,
3104 0, 1, constants::DW_LNE_end_sequence.0,
3105 ];
3106 assert_eq!(buf[0] as usize, buf.len() - 4);
3107
3108 let rest = &mut EndianSlice::new(&buf, LittleEndian);
3109
3110 let header = LineProgramHeader::parse(rest, DebugLineOffset(0), 4, None, None)
3111 .expect("should parse header ok");
3112 let program = IncompleteLineProgram { header };
3113
3114 let sequences = program.sequences().unwrap().1;
3115 assert_eq!(sequences.len(), 2);
3116 assert_eq!(sequences[0].start, 1);
3117 assert_eq!(sequences[0].end, 4);
3118 assert_eq!(sequences[1].start, 11);
3119 assert_eq!(sequences[1].end, 14);
3120 }
3121}