1use alloc::vec::Vec;
2use indexmap::{IndexMap, IndexSet};
3use std::ops::{Deref, DerefMut};
4
5use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId};
6use crate::constants;
7use crate::leb128;
8use crate::write::{
9 Address, DebugLineStrOffsets, DebugStrOffsets, Error, LineStringId, LineStringTable, Result,
10 Section, StringId, Writer,
11};
12
13/// The number assigned to the first special opcode.
14//
15// We output all instructions for all DWARF versions, since readers
16// should be able to ignore instructions they don't support.
17const OPCODE_BASE: u8 = 13;
18
19/// A line number program.
20#[derive(Debug, Clone)]
21pub struct LineProgram {
22 /// True if this line program was created with `LineProgram::none()`.
23 none: bool,
24 encoding: Encoding,
25 line_encoding: LineEncoding,
26
27 /// A list of source directory path names.
28 ///
29 /// If a path is relative, then the directory is located relative to the working
30 /// directory of the compilation unit.
31 ///
32 /// The first entry is for the working directory of the compilation unit.
33 directories: IndexSet<LineString>,
34
35 /// A list of source file entries.
36 ///
37 /// Each entry has a path name and a directory.
38 ///
39 /// If a path is a relative, then the file is located relative to the
40 /// directory. Otherwise the directory is meaningless.
41 ///
42 /// Does not include comp_file, even for version >= 5.
43 files: IndexMap<(LineString, DirectoryId), FileInfo>,
44
45 /// The primary source file of the compilation unit.
46 /// This is required for version >= 5, but we never reference it elsewhere
47 /// because DWARF defines DW_AT_decl_file=0 to mean not specified.
48 comp_file: (LineString, FileInfo),
49
50 /// True if the file entries may have valid timestamps.
51 ///
52 /// Entries may still have a timestamp of 0 even if this is set.
53 /// For version <= 4, this is ignored.
54 /// For version 5, this controls whether to emit `DW_LNCT_timestamp`.
55 pub file_has_timestamp: bool,
56
57 /// True if the file entries may have valid sizes.
58 ///
59 /// Entries may still have a size of 0 even if this is set.
60 /// For version <= 4, this is ignored.
61 /// For version 5, this controls whether to emit `DW_LNCT_size`.
62 pub file_has_size: bool,
63
64 /// True if the file entries have valid MD5 checksums.
65 ///
66 /// For version <= 4, this is ignored.
67 /// For version 5, this controls whether to emit `DW_LNCT_MD5`.
68 pub file_has_md5: bool,
69
70 prev_row: LineRow,
71 row: LineRow,
72 // TODO: this probably should be either rows or sequences instead
73 instructions: Vec<LineInstruction>,
74 in_sequence: bool,
75}
76
77impl LineProgram {
78 /// Create a new `LineProgram`.
79 ///
80 /// `comp_dir` defines the working directory of the compilation unit,
81 /// and must be the same as the `DW_AT_comp_dir` attribute
82 /// of the compilation unit DIE.
83 ///
84 /// `comp_file` and `comp_file_info` define the primary source file
85 /// of the compilation unit and must be the same as the `DW_AT_name`
86 /// attribute of the compilation unit DIE.
87 ///
88 /// # Panics
89 ///
90 /// Panics if `line_encoding.line_base` > 0.
91 ///
92 /// Panics if `line_encoding.line_base` + `line_encoding.line_range` <= 0.
93 ///
94 /// Panics if `comp_dir` is empty or contains a null byte.
95 ///
96 /// Panics if `comp_file` is empty or contains a null byte.
97 pub fn new(
98 encoding: Encoding,
99 line_encoding: LineEncoding,
100 comp_dir: LineString,
101 comp_file: LineString,
102 comp_file_info: Option<FileInfo>,
103 ) -> LineProgram {
104 // We require a special opcode for a line advance of 0.
105 // See the debug_asserts in generate_row().
106 assert!(line_encoding.line_base <= 0);
107 assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0);
108 let mut program = LineProgram {
109 none: false,
110 encoding,
111 line_encoding,
112 directories: IndexSet::new(),
113 files: IndexMap::new(),
114 comp_file: (comp_file, comp_file_info.unwrap_or_default()),
115 prev_row: LineRow::initial_state(line_encoding),
116 row: LineRow::initial_state(line_encoding),
117 instructions: Vec::new(),
118 in_sequence: false,
119 file_has_timestamp: false,
120 file_has_size: false,
121 file_has_md5: false,
122 };
123 // For all DWARF versions, directory index 0 is comp_dir.
124 // For version <= 4, the entry is implicit. We still add
125 // it here so that we use it, but we don't emit it.
126 program.add_directory(comp_dir);
127 program
128 }
129
130 /// Create a new `LineProgram` with no fields set.
131 ///
132 /// This can be used when the `LineProgram` will not be used.
133 ///
134 /// You should not attempt to add files or line instructions to
135 /// this line program, or write it to the `.debug_line` section.
136 pub fn none() -> Self {
137 let line_encoding = LineEncoding::default();
138 LineProgram {
139 none: true,
140 encoding: Encoding {
141 format: Format::Dwarf32,
142 version: 2,
143 address_size: 0,
144 },
145 line_encoding,
146 directories: IndexSet::new(),
147 files: IndexMap::new(),
148 comp_file: (LineString::String(Vec::new()), FileInfo::default()),
149 prev_row: LineRow::initial_state(line_encoding),
150 row: LineRow::initial_state(line_encoding),
151 instructions: Vec::new(),
152 in_sequence: false,
153 file_has_timestamp: false,
154 file_has_size: false,
155 file_has_md5: false,
156 }
157 }
158
159 /// Return true if this line program was created with `LineProgram::none()`.
160 #[inline]
161 pub fn is_none(&self) -> bool {
162 self.none
163 }
164
165 /// Return the encoding parameters for this line program.
166 #[inline]
167 pub fn encoding(&self) -> Encoding {
168 self.encoding
169 }
170
171 /// Return the DWARF version for this line program.
172 #[inline]
173 pub fn version(&self) -> u16 {
174 self.encoding.version
175 }
176
177 /// Return the address size in bytes for this line program.
178 #[inline]
179 pub fn address_size(&self) -> u8 {
180 self.encoding.address_size
181 }
182
183 /// Return the DWARF format for this line program.
184 #[inline]
185 pub fn format(&self) -> Format {
186 self.encoding.format
187 }
188
189 /// Return the id for the working directory of the compilation unit.
190 #[inline]
191 pub fn default_directory(&self) -> DirectoryId {
192 DirectoryId(0)
193 }
194
195 /// Add a directory entry and return its id.
196 ///
197 /// If the directory already exists, then return the id of the existing entry.
198 ///
199 /// If the path is relative, then the directory is located relative to the working
200 /// directory of the compilation unit.
201 ///
202 /// # Panics
203 ///
204 /// Panics if `directory` is empty or contains a null byte.
205 pub fn add_directory(&mut self, directory: LineString) -> DirectoryId {
206 if let LineString::String(ref val) = directory {
207 // For DWARF version <= 4, directories must not be empty.
208 // The first directory isn't emitted so skip the check for it.
209 if self.encoding.version <= 4 && !self.directories.is_empty() {
210 assert!(!val.is_empty());
211 }
212 assert!(!val.contains(&0));
213 }
214 let (index, _) = self.directories.insert_full(directory);
215 DirectoryId(index)
216 }
217
218 /// Get a reference to a directory entry.
219 ///
220 /// # Panics
221 ///
222 /// Panics if `id` is invalid.
223 pub fn get_directory(&self, id: DirectoryId) -> &LineString {
224 self.directories.get_index(id.0).unwrap()
225 }
226
227 /// Add a file entry and return its id.
228 ///
229 /// If the file already exists, then return the id of the existing entry.
230 ///
231 /// If the file path is relative, then the file is located relative
232 /// to the directory. Otherwise the directory is meaningless, but it
233 /// is still used as a key for file entries.
234 ///
235 /// If `info` is `None`, then new entries are assigned
236 /// default information, and existing entries are unmodified.
237 ///
238 /// If `info` is not `None`, then it is always assigned to the
239 /// entry, even if the entry already exists.
240 ///
241 /// # Panics
242 ///
243 /// Panics if 'file' is empty or contains a null byte.
244 pub fn add_file(
245 &mut self,
246 file: LineString,
247 directory: DirectoryId,
248 info: Option<FileInfo>,
249 ) -> FileId {
250 if let LineString::String(ref val) = file {
251 assert!(!val.is_empty());
252 assert!(!val.contains(&0));
253 }
254
255 let key = (file, directory);
256 let index = if let Some(info) = info {
257 let (index, _) = self.files.insert_full(key, info);
258 index
259 } else {
260 let entry = self.files.entry(key);
261 let index = entry.index();
262 entry.or_default();
263 index
264 };
265 FileId::new(index)
266 }
267
268 /// Get a reference to a file entry.
269 ///
270 /// # Panics
271 ///
272 /// Panics if `id` is invalid.
273 pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) {
274 match id.index() {
275 None => (&self.comp_file.0, DirectoryId(0)),
276 Some(index) => self
277 .files
278 .get_index(index)
279 .map(|entry| (&(entry.0).0, (entry.0).1))
280 .unwrap(),
281 }
282 }
283
284 /// Get a reference to the info for a file entry.
285 ///
286 /// # Panics
287 ///
288 /// Panics if `id` is invalid.
289 pub fn get_file_info(&self, id: FileId) -> &FileInfo {
290 match id.index() {
291 None => &self.comp_file.1,
292 Some(index) => self.files.get_index(index).map(|entry| entry.1).unwrap(),
293 }
294 }
295
296 /// Get a mutable reference to the info for a file entry.
297 ///
298 /// # Panics
299 ///
300 /// Panics if `id` is invalid.
301 pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo {
302 match id.index() {
303 None => &mut self.comp_file.1,
304 Some(index) => self
305 .files
306 .get_index_mut(index)
307 .map(|entry| entry.1)
308 .unwrap(),
309 }
310 }
311
312 /// Begin a new sequence and set its base address.
313 ///
314 /// # Panics
315 ///
316 /// Panics if a sequence has already begun.
317 pub fn begin_sequence(&mut self, address: Option<Address>) {
318 assert!(!self.in_sequence);
319 self.in_sequence = true;
320 if let Some(address) = address {
321 self.instructions.push(LineInstruction::SetAddress(address));
322 }
323 }
324
325 /// End the sequence, and reset the row to its default values.
326 ///
327 /// Only the `address_offset` and op_index` fields of the current row are used.
328 ///
329 /// # Panics
330 ///
331 /// Panics if a sequence has not begun.
332 pub fn end_sequence(&mut self, address_offset: u64) {
333 assert!(self.in_sequence);
334 self.in_sequence = false;
335 self.row.address_offset = address_offset;
336 let op_advance = self.op_advance();
337 if op_advance != 0 {
338 self.instructions
339 .push(LineInstruction::AdvancePc(op_advance));
340 }
341 self.instructions.push(LineInstruction::EndSequence);
342 self.prev_row = LineRow::initial_state(self.line_encoding);
343 self.row = LineRow::initial_state(self.line_encoding);
344 }
345
346 /// Return true if a sequence has begun.
347 #[inline]
348 pub fn in_sequence(&self) -> bool {
349 self.in_sequence
350 }
351
352 /// Returns a reference to the data for the current row.
353 #[inline]
354 pub fn row(&mut self) -> &mut LineRow {
355 &mut self.row
356 }
357
358 /// Generates the line number information instructions for the current row.
359 ///
360 /// After the instructions are generated, it sets `discriminator` to 0, and sets
361 /// `basic_block`, `prologue_end`, and `epilogue_begin` to false.
362 ///
363 /// # Panics
364 ///
365 /// Panics if a sequence has not begun.
366 /// Panics if the address_offset decreases.
367 pub fn generate_row(&mut self) {
368 assert!(self.in_sequence);
369
370 // Output fields that are reset on every row.
371 if self.row.discriminator != 0 {
372 self.instructions
373 .push(LineInstruction::SetDiscriminator(self.row.discriminator));
374 self.row.discriminator = 0;
375 }
376 if self.row.basic_block {
377 self.instructions.push(LineInstruction::SetBasicBlock);
378 self.row.basic_block = false;
379 }
380 if self.row.prologue_end {
381 self.instructions.push(LineInstruction::SetPrologueEnd);
382 self.row.prologue_end = false;
383 }
384 if self.row.epilogue_begin {
385 self.instructions.push(LineInstruction::SetEpilogueBegin);
386 self.row.epilogue_begin = false;
387 }
388
389 // Output fields that are not reset on every row.
390 if self.row.is_statement != self.prev_row.is_statement {
391 self.instructions.push(LineInstruction::NegateStatement);
392 }
393 if self.row.file != self.prev_row.file {
394 self.instructions
395 .push(LineInstruction::SetFile(self.row.file));
396 }
397 if self.row.column != self.prev_row.column {
398 self.instructions
399 .push(LineInstruction::SetColumn(self.row.column));
400 }
401 if self.row.isa != self.prev_row.isa {
402 self.instructions
403 .push(LineInstruction::SetIsa(self.row.isa));
404 }
405
406 // Advance the line, address, and operation index.
407 let line_base = i64::from(self.line_encoding.line_base) as u64;
408 let line_range = u64::from(self.line_encoding.line_range);
409 let line_advance = self.row.line as i64 - self.prev_row.line as i64;
410 let op_advance = self.op_advance();
411
412 // Default to special advances of 0.
413 let special_base = u64::from(OPCODE_BASE);
414 // TODO: handle lack of special opcodes for 0 line advance
415 debug_assert!(self.line_encoding.line_base <= 0);
416 debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0);
417 let special_default = special_base.wrapping_sub(line_base);
418 let mut special = special_default;
419 let mut use_special = false;
420
421 if line_advance != 0 {
422 let special_line = (line_advance as u64).wrapping_sub(line_base);
423 if special_line < line_range {
424 special = special_base + special_line;
425 use_special = true;
426 } else {
427 self.instructions
428 .push(LineInstruction::AdvanceLine(line_advance));
429 }
430 }
431
432 if op_advance != 0 {
433 // Using ConstAddPc can save a byte.
434 let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 {
435 (op_advance, false)
436 } else {
437 let op_range = (255 - special_base) / line_range;
438 (op_advance - op_range, true)
439 };
440
441 let special_op = special_op_advance * line_range;
442 if special + special_op <= 255 {
443 special += special_op;
444 use_special = true;
445 if const_add_pc {
446 self.instructions.push(LineInstruction::ConstAddPc);
447 }
448 } else {
449 self.instructions
450 .push(LineInstruction::AdvancePc(op_advance));
451 }
452 }
453
454 if use_special && special != special_default {
455 debug_assert!(special >= special_base);
456 debug_assert!(special <= 255);
457 self.instructions
458 .push(LineInstruction::Special(special as u8));
459 } else {
460 self.instructions.push(LineInstruction::Copy);
461 }
462
463 self.prev_row = self.row;
464 }
465
466 fn op_advance(&self) -> u64 {
467 debug_assert!(self.row.address_offset >= self.prev_row.address_offset);
468 let mut address_advance = self.row.address_offset - self.prev_row.address_offset;
469 if self.line_encoding.minimum_instruction_length != 1 {
470 debug_assert_eq!(
471 self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length),
472 0
473 );
474 address_advance /= u64::from(self.line_encoding.minimum_instruction_length);
475 }
476 address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction)
477 + self.row.op_index
478 - self.prev_row.op_index
479 }
480
481 /// Returns true if the line number program has no instructions.
482 ///
483 /// Does not check the file or directory entries.
484 #[inline]
485 pub fn is_empty(&self) -> bool {
486 self.instructions.is_empty()
487 }
488
489 /// Write the line number program to the given section.
490 ///
491 /// # Panics
492 ///
493 /// Panics if `self.is_none()`.
494 pub fn write<W: Writer>(
495 &self,
496 w: &mut DebugLine<W>,
497 encoding: Encoding,
498 debug_line_str_offsets: &DebugLineStrOffsets,
499 debug_str_offsets: &DebugStrOffsets,
500 ) -> Result<DebugLineOffset> {
501 assert!(!self.is_none());
502
503 if encoding.version < self.version()
504 || encoding.format != self.format()
505 || encoding.address_size != self.address_size()
506 {
507 return Err(Error::IncompatibleLineProgramEncoding);
508 }
509
510 let offset = w.offset();
511
512 let length_offset = w.write_initial_length(self.format())?;
513 let length_base = w.len();
514
515 if self.version() < 2 || self.version() > 5 {
516 return Err(Error::UnsupportedVersion(self.version()));
517 }
518 w.write_u16(self.version())?;
519
520 if self.version() >= 5 {
521 w.write_u8(self.address_size())?;
522 // Segment selector size.
523 w.write_u8(0)?;
524 }
525
526 let header_length_offset = w.len();
527 w.write_udata(0, self.format().word_size())?;
528 let header_length_base = w.len();
529
530 w.write_u8(self.line_encoding.minimum_instruction_length)?;
531 if self.version() >= 4 {
532 w.write_u8(self.line_encoding.maximum_operations_per_instruction)?;
533 } else if self.line_encoding.maximum_operations_per_instruction != 1 {
534 return Err(Error::NeedVersion(4));
535 };
536 w.write_u8(if self.line_encoding.default_is_stmt {
537 1
538 } else {
539 0
540 })?;
541 w.write_u8(self.line_encoding.line_base as u8)?;
542 w.write_u8(self.line_encoding.line_range)?;
543 w.write_u8(OPCODE_BASE)?;
544 w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?;
545
546 if self.version() <= 4 {
547 // The first directory is stored as DW_AT_comp_dir.
548 for dir in self.directories.iter().skip(1) {
549 dir.write(
550 w,
551 constants::DW_FORM_string,
552 self.encoding,
553 debug_line_str_offsets,
554 debug_str_offsets,
555 )?;
556 }
557 w.write_u8(0)?;
558
559 for ((file, dir), info) in self.files.iter() {
560 file.write(
561 w,
562 constants::DW_FORM_string,
563 self.encoding,
564 debug_line_str_offsets,
565 debug_str_offsets,
566 )?;
567 w.write_uleb128(dir.0 as u64)?;
568 w.write_uleb128(info.timestamp)?;
569 w.write_uleb128(info.size)?;
570 }
571 w.write_u8(0)?;
572 } else {
573 // Directory entry formats (only ever 1).
574 w.write_u8(1)?;
575 w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
576 let dir_form = self.directories.get_index(0).unwrap().form();
577 w.write_uleb128(dir_form.0.into())?;
578
579 // Directory entries.
580 w.write_uleb128(self.directories.len() as u64)?;
581 for dir in self.directories.iter() {
582 dir.write(
583 w,
584 dir_form,
585 self.encoding,
586 debug_line_str_offsets,
587 debug_str_offsets,
588 )?;
589 }
590
591 // File name entry formats.
592 let count = 2
593 + if self.file_has_timestamp { 1 } else { 0 }
594 + if self.file_has_size { 1 } else { 0 }
595 + if self.file_has_md5 { 1 } else { 0 };
596 w.write_u8(count)?;
597 w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
598 let file_form = self.comp_file.0.form();
599 w.write_uleb128(file_form.0.into())?;
600 w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?;
601 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
602 if self.file_has_timestamp {
603 w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?;
604 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
605 }
606 if self.file_has_size {
607 w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?;
608 w.write_uleb128(constants::DW_FORM_udata.0.into())?;
609 }
610 if self.file_has_md5 {
611 w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?;
612 w.write_uleb128(constants::DW_FORM_data16.0.into())?;
613 }
614
615 // File name entries.
616 w.write_uleb128(self.files.len() as u64 + 1)?;
617 let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| {
618 file.write(
619 w,
620 file_form,
621 self.encoding,
622 debug_line_str_offsets,
623 debug_str_offsets,
624 )?;
625 w.write_uleb128(dir.0 as u64)?;
626 if self.file_has_timestamp {
627 w.write_uleb128(info.timestamp)?;
628 }
629 if self.file_has_size {
630 w.write_uleb128(info.size)?;
631 }
632 if self.file_has_md5 {
633 w.write(&info.md5)?;
634 }
635 Ok(())
636 };
637 write_file(&self.comp_file.0, DirectoryId(0), &self.comp_file.1)?;
638 for ((file, dir), info) in self.files.iter() {
639 write_file(file, *dir, info)?;
640 }
641 }
642
643 let header_length = (w.len() - header_length_base) as u64;
644 w.write_udata_at(
645 header_length_offset,
646 header_length,
647 self.format().word_size(),
648 )?;
649
650 for instruction in &self.instructions {
651 instruction.write(w, self.address_size())?;
652 }
653
654 let length = (w.len() - length_base) as u64;
655 w.write_initial_length_at(length_offset, length, self.format())?;
656
657 Ok(offset)
658 }
659}
660
661/// A row in the line number table that corresponds to a machine instruction.
662#[derive(Debug, Clone, Copy)]
663pub struct LineRow {
664 /// The offset of the instruction from the start address of the sequence.
665 pub address_offset: u64,
666 /// The index of an operation within a VLIW instruction.
667 ///
668 /// The index of the first operation is 0.
669 /// Set to 0 for non-VLIW instructions.
670 pub op_index: u64,
671
672 /// The source file corresponding to the instruction.
673 pub file: FileId,
674 /// The line number within the source file.
675 ///
676 /// Lines are numbered beginning at 1. Set to 0 if there is no source line.
677 pub line: u64,
678 /// The column number within the source line.
679 ///
680 /// Columns are numbered beginning at 1. Set to 0 for the "left edge" of the line.
681 pub column: u64,
682 /// An additional discriminator used to distinguish between source locations.
683 /// This value is assigned arbitrarily by the DWARF producer.
684 pub discriminator: u64,
685
686 /// Set to true if the instruction is a recommended breakpoint for a statement.
687 pub is_statement: bool,
688 /// Set to true if the instruction is the beginning of a basic block.
689 pub basic_block: bool,
690 /// Set to true if the instruction is a recommended breakpoint at the entry of a
691 /// function.
692 pub prologue_end: bool,
693 /// Set to true if the instruction is a recommended breakpoint prior to the exit of
694 /// a function.
695 pub epilogue_begin: bool,
696
697 /// The instruction set architecture of the instruction.
698 ///
699 /// Set to 0 for the default ISA. Other values are defined by the architecture ABI.
700 pub isa: u64,
701}
702
703impl LineRow {
704 /// Return the initial state as specified in the DWARF standard.
705 fn initial_state(line_encoding: LineEncoding) -> Self {
706 LineRow {
707 address_offset: 0,
708 op_index: 0,
709
710 file: FileId::initial_state(),
711 line: 1,
712 column: 0,
713 discriminator: 0,
714
715 is_statement: line_encoding.default_is_stmt,
716 basic_block: false,
717 prologue_end: false,
718 epilogue_begin: false,
719
720 isa: 0,
721 }
722 }
723}
724
725/// An instruction in a line number program.
726#[derive(Debug, Clone, Copy, PartialEq, Eq)]
727enum LineInstruction {
728 // Special opcodes
729 Special(u8),
730
731 // Standard opcodes
732 Copy,
733 AdvancePc(u64),
734 AdvanceLine(i64),
735 SetFile(FileId),
736 SetColumn(u64),
737 NegateStatement,
738 SetBasicBlock,
739 ConstAddPc,
740 // DW_LNS_fixed_advance_pc is not supported.
741 SetPrologueEnd,
742 SetEpilogueBegin,
743 SetIsa(u64),
744
745 // Extended opcodes
746 EndSequence,
747 // TODO: this doubles the size of this enum.
748 SetAddress(Address),
749 // DW_LNE_define_file is not supported.
750 SetDiscriminator(u64),
751}
752
753impl LineInstruction {
754 /// Write the line number instruction to the given section.
755 fn write<W: Writer>(self, w: &mut DebugLine<W>, address_size: u8) -> Result<()> {
756 use self::LineInstruction::*;
757 match self {
758 Special(val) => w.write_u8(val)?,
759 Copy => w.write_u8(constants::DW_LNS_copy.0)?,
760 AdvancePc(val) => {
761 w.write_u8(constants::DW_LNS_advance_pc.0)?;
762 w.write_uleb128(val)?;
763 }
764 AdvanceLine(val) => {
765 w.write_u8(constants::DW_LNS_advance_line.0)?;
766 w.write_sleb128(val)?;
767 }
768 SetFile(val) => {
769 w.write_u8(constants::DW_LNS_set_file.0)?;
770 w.write_uleb128(val.raw())?;
771 }
772 SetColumn(val) => {
773 w.write_u8(constants::DW_LNS_set_column.0)?;
774 w.write_uleb128(val)?;
775 }
776 NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?,
777 SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?,
778 ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?,
779 SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?,
780 SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?,
781 SetIsa(val) => {
782 w.write_u8(constants::DW_LNS_set_isa.0)?;
783 w.write_uleb128(val)?;
784 }
785 EndSequence => {
786 w.write_u8(0)?;
787 w.write_uleb128(1)?;
788 w.write_u8(constants::DW_LNE_end_sequence.0)?;
789 }
790 SetAddress(address) => {
791 w.write_u8(0)?;
792 w.write_uleb128(1 + u64::from(address_size))?;
793 w.write_u8(constants::DW_LNE_set_address.0)?;
794 w.write_address(address, address_size)?;
795 }
796 SetDiscriminator(val) => {
797 let mut bytes = [0u8; 10];
798 // bytes is long enough so this will never fail.
799 let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap();
800 w.write_u8(0)?;
801 w.write_uleb128(1 + len as u64)?;
802 w.write_u8(constants::DW_LNE_set_discriminator.0)?;
803 w.write(&bytes[..len])?;
804 }
805 }
806 Ok(())
807 }
808}
809
810/// A string value for use in defining paths in line number programs.
811#[derive(Debug, Clone, PartialEq, Eq, Hash)]
812pub enum LineString {
813 /// A slice of bytes representing a string. Must not include null bytes.
814 /// Not guaranteed to be UTF-8 or anything like that.
815 String(Vec<u8>),
816
817 /// A reference to a string in the `.debug_str` section.
818 StringRef(StringId),
819
820 /// A reference to a string in the `.debug_line_str` section.
821 LineStringRef(LineStringId),
822}
823
824impl LineString {
825 /// Create a `LineString` using the normal form for the given encoding.
826 pub fn new<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self
827 where
828 T: Into<Vec<u8>>,
829 {
830 let val = val.into();
831 if encoding.version <= 4 {
832 LineString::String(val)
833 } else {
834 LineString::LineStringRef(line_strings.add(val))
835 }
836 }
837
838 fn form(&self) -> constants::DwForm {
839 match *self {
840 LineString::String(..) => constants::DW_FORM_string,
841 LineString::StringRef(..) => constants::DW_FORM_strp,
842 LineString::LineStringRef(..) => constants::DW_FORM_line_strp,
843 }
844 }
845
846 fn write<W: Writer>(
847 &self,
848 w: &mut DebugLine<W>,
849 form: constants::DwForm,
850 encoding: Encoding,
851 debug_line_str_offsets: &DebugLineStrOffsets,
852 debug_str_offsets: &DebugStrOffsets,
853 ) -> Result<()> {
854 if form != self.form() {
855 return Err(Error::LineStringFormMismatch);
856 }
857
858 match *self {
859 LineString::String(ref val) => {
860 if encoding.version <= 4 {
861 debug_assert!(!val.is_empty());
862 }
863 w.write(val)?;
864 w.write_u8(0)?;
865 }
866 LineString::StringRef(val) => {
867 if encoding.version < 5 {
868 return Err(Error::NeedVersion(5));
869 }
870 w.write_offset(
871 debug_str_offsets.get(val).0,
872 SectionId::DebugStr,
873 encoding.format.word_size(),
874 )?;
875 }
876 LineString::LineStringRef(val) => {
877 if encoding.version < 5 {
878 return Err(Error::NeedVersion(5));
879 }
880 w.write_offset(
881 debug_line_str_offsets.get(val).0,
882 SectionId::DebugLineStr,
883 encoding.format.word_size(),
884 )?;
885 }
886 }
887 Ok(())
888 }
889}
890
891/// An identifier for a directory in a `LineProgram`.
892///
893/// Defaults to the working directory of the compilation unit.
894#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
895pub struct DirectoryId(usize);
896
897// Force FileId access via the methods.
898mod id {
899 /// An identifier for a file in a `LineProgram`.
900 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
901 pub struct FileId(usize);
902
903 impl FileId {
904 /// Create a FileId given an index into `LineProgram::files`.
905 pub(crate) fn new(index: usize) -> Self {
906 FileId(index + 1)
907 }
908
909 /// The index of the file in `LineProgram::files`.
910 pub(super) fn index(self) -> Option<usize> {
911 if self.0 == 0 {
912 None
913 } else {
914 Some(self.0 - 1)
915 }
916 }
917
918 /// The initial state of the file register.
919 pub(super) fn initial_state() -> Self {
920 FileId(1)
921 }
922
923 /// The raw value used when writing.
924 pub(crate) fn raw(self) -> u64 {
925 self.0 as u64
926 }
927
928 /// The id for file index 0 in DWARF version 5.
929 /// Only used when converting.
930 // Used for tests only.
931 #[allow(unused)]
932 pub(super) fn zero() -> Self {
933 FileId(0)
934 }
935 }
936}
937pub use self::id::*;
938
939/// Extra information for file in a `LineProgram`.
940#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
941pub struct FileInfo {
942 /// The implementation defined timestamp of the last modification of the file,
943 /// or 0 if not available.
944 pub timestamp: u64,
945
946 /// The size of the file in bytes, or 0 if not available.
947 pub size: u64,
948
949 /// A 16-byte MD5 digest of the file contents.
950 ///
951 /// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`.
952 pub md5: [u8; 16],
953}
954
955define_section!(
956 DebugLine,
957 DebugLineOffset,
958 "A writable `.debug_line` section."
959);
960
961#[cfg(feature = "read")]
962mod convert {
963 use super::*;
964 use crate::read::{self, Reader};
965 use crate::write::{self, ConvertError, ConvertResult};
966
967 impl LineProgram {
968 /// Create a line number program by reading the data from the given program.
969 ///
970 /// Return the program and a mapping from file index to `FileId`.
971 pub fn from<R: Reader<Offset = usize>>(
972 mut from_program: read::IncompleteLineProgram<R>,
973 dwarf: &read::Dwarf<R>,
974 line_strings: &mut write::LineStringTable,
975 strings: &mut write::StringTable,
976 convert_address: &dyn Fn(u64) -> Option<Address>,
977 ) -> ConvertResult<(LineProgram, Vec<FileId>)> {
978 // Create mappings in case the source has duplicate files or directories.
979 let mut dirs = Vec::new();
980 let mut files = Vec::new();
981
982 let mut program = {
983 let from_header = from_program.header();
984 let encoding = from_header.encoding();
985
986 let comp_dir = match from_header.directory(0) {
987 Some(comp_dir) => LineString::from(comp_dir, dwarf, line_strings, strings)?,
988 None => LineString::new(&[][..], encoding, line_strings),
989 };
990
991 let (comp_name, comp_file_info) = match from_header.file(0) {
992 Some(comp_file) => {
993 if comp_file.directory_index() != 0 {
994 return Err(ConvertError::InvalidDirectoryIndex);
995 }
996 (
997 LineString::from(comp_file.path_name(), dwarf, line_strings, strings)?,
998 Some(FileInfo {
999 timestamp: comp_file.timestamp(),
1000 size: comp_file.size(),
1001 md5: *comp_file.md5(),
1002 }),
1003 )
1004 }
1005 None => (LineString::new(&[][..], encoding, line_strings), None),
1006 };
1007
1008 if from_header.line_base() > 0 {
1009 return Err(ConvertError::InvalidLineBase);
1010 }
1011 let mut program = LineProgram::new(
1012 encoding,
1013 from_header.line_encoding(),
1014 comp_dir,
1015 comp_name,
1016 comp_file_info,
1017 );
1018
1019 let file_skip;
1020 if from_header.version() <= 4 {
1021 // The first directory is implicit.
1022 dirs.push(DirectoryId(0));
1023 // A file index of 0 is invalid for version <= 4, but putting
1024 // something there makes the indexing easier.
1025 file_skip = 0;
1026 files.push(FileId::zero());
1027 } else {
1028 // We don't add the first file to `files`, but still allow
1029 // it to be referenced from converted instructions.
1030 file_skip = 1;
1031 files.push(FileId::zero());
1032 }
1033
1034 for from_dir in from_header.include_directories() {
1035 let from_dir =
1036 LineString::from(from_dir.clone(), dwarf, line_strings, strings)?;
1037 dirs.push(program.add_directory(from_dir));
1038 }
1039
1040 program.file_has_timestamp = from_header.file_has_timestamp();
1041 program.file_has_size = from_header.file_has_size();
1042 program.file_has_md5 = from_header.file_has_md5();
1043 for from_file in from_header.file_names().iter().skip(file_skip) {
1044 let from_name =
1045 LineString::from(from_file.path_name(), dwarf, line_strings, strings)?;
1046 let from_dir = from_file.directory_index();
1047 if from_dir >= dirs.len() as u64 {
1048 return Err(ConvertError::InvalidDirectoryIndex);
1049 }
1050 let from_dir = dirs[from_dir as usize];
1051 let from_info = Some(FileInfo {
1052 timestamp: from_file.timestamp(),
1053 size: from_file.size(),
1054 md5: *from_file.md5(),
1055 });
1056 files.push(program.add_file(from_name, from_dir, from_info));
1057 }
1058
1059 program
1060 };
1061
1062 // We can't use the `from_program.rows()` because that wouldn't let
1063 // us preserve address relocations.
1064 let mut from_row = read::LineRow::new(from_program.header());
1065 let mut instructions = from_program.header().instructions();
1066 let mut address = None;
1067 while let Some(instruction) = instructions.next_instruction(from_program.header())? {
1068 match instruction {
1069 read::LineInstruction::SetAddress(val) => {
1070 if program.in_sequence() {
1071 return Err(ConvertError::UnsupportedLineInstruction);
1072 }
1073 match convert_address(val) {
1074 Some(val) => address = Some(val),
1075 None => return Err(ConvertError::InvalidAddress),
1076 }
1077 from_row.execute(read::LineInstruction::SetAddress(0), &mut from_program);
1078 }
1079 read::LineInstruction::DefineFile(_) => {
1080 return Err(ConvertError::UnsupportedLineInstruction);
1081 }
1082 _ => {
1083 if from_row.execute(instruction, &mut from_program) {
1084 if !program.in_sequence() {
1085 program.begin_sequence(address);
1086 address = None;
1087 }
1088 if from_row.end_sequence() {
1089 program.end_sequence(from_row.address());
1090 } else {
1091 program.row().address_offset = from_row.address();
1092 program.row().op_index = from_row.op_index();
1093 program.row().file = {
1094 let file = from_row.file_index();
1095 if file >= files.len() as u64 {
1096 return Err(ConvertError::InvalidFileIndex);
1097 }
1098 if file == 0 && program.version() <= 4 {
1099 return Err(ConvertError::InvalidFileIndex);
1100 }
1101 files[file as usize]
1102 };
1103 program.row().line = match from_row.line() {
1104 Some(line) => line.get(),
1105 None => 0,
1106 };
1107 program.row().column = match from_row.column() {
1108 read::ColumnType::LeftEdge => 0,
1109 read::ColumnType::Column(val) => val.get(),
1110 };
1111 program.row().discriminator = from_row.discriminator();
1112 program.row().is_statement = from_row.is_stmt();
1113 program.row().basic_block = from_row.basic_block();
1114 program.row().prologue_end = from_row.prologue_end();
1115 program.row().epilogue_begin = from_row.epilogue_begin();
1116 program.row().isa = from_row.isa();
1117 program.generate_row();
1118 }
1119 from_row.reset(from_program.header());
1120 }
1121 }
1122 };
1123 }
1124 Ok((program, files))
1125 }
1126 }
1127
1128 impl LineString {
1129 fn from<R: Reader<Offset = usize>>(
1130 from_attr: read::AttributeValue<R>,
1131 dwarf: &read::Dwarf<R>,
1132 line_strings: &mut write::LineStringTable,
1133 strings: &mut write::StringTable,
1134 ) -> ConvertResult<LineString> {
1135 Ok(match from_attr {
1136 read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()),
1137 read::AttributeValue::DebugStrRef(offset) => {
1138 let r = dwarf.debug_str.get_str(offset)?;
1139 let id = strings.add(r.to_slice()?);
1140 LineString::StringRef(id)
1141 }
1142 read::AttributeValue::DebugLineStrRef(offset) => {
1143 let r = dwarf.debug_line_str.get_str(offset)?;
1144 let id = line_strings.add(r.to_slice()?);
1145 LineString::LineStringRef(id)
1146 }
1147 _ => return Err(ConvertError::UnsupportedLineStringForm),
1148 })
1149 }
1150 }
1151}
1152
1153#[cfg(test)]
1154#[cfg(feature = "read")]
1155mod tests {
1156 use super::*;
1157 use crate::read;
1158 use crate::write::{DebugLineStr, DebugStr, EndianVec, StringTable};
1159 use crate::LittleEndian;
1160
1161 #[test]
1162 fn test_line_program_table() {
1163 let dir1 = LineString::String(b"dir1".to_vec());
1164 let file1 = LineString::String(b"file1".to_vec());
1165 let dir2 = LineString::String(b"dir2".to_vec());
1166 let file2 = LineString::String(b"file2".to_vec());
1167
1168 let mut programs = Vec::new();
1169 for &version in &[2, 3, 4, 5] {
1170 for &address_size in &[4, 8] {
1171 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1172 let encoding = Encoding {
1173 format,
1174 version,
1175 address_size,
1176 };
1177 let mut program = LineProgram::new(
1178 encoding,
1179 LineEncoding::default(),
1180 dir1.clone(),
1181 file1.clone(),
1182 None,
1183 );
1184
1185 {
1186 assert_eq!(&dir1, program.get_directory(program.default_directory()));
1187 program.file_has_timestamp = true;
1188 program.file_has_size = true;
1189 if encoding.version >= 5 {
1190 program.file_has_md5 = true;
1191 }
1192
1193 let dir_id = program.add_directory(dir2.clone());
1194 assert_eq!(&dir2, program.get_directory(dir_id));
1195 assert_eq!(dir_id, program.add_directory(dir2.clone()));
1196
1197 let file_info = FileInfo {
1198 timestamp: 1,
1199 size: 2,
1200 md5: if encoding.version >= 5 {
1201 [3; 16]
1202 } else {
1203 [0; 16]
1204 },
1205 };
1206 let file_id = program.add_file(file2.clone(), dir_id, Some(file_info));
1207 assert_eq!((&file2, dir_id), program.get_file(file_id));
1208 assert_eq!(file_info, *program.get_file_info(file_id));
1209
1210 program.get_file_info_mut(file_id).size = 3;
1211 assert_ne!(file_info, *program.get_file_info(file_id));
1212 assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None));
1213 assert_ne!(file_info, *program.get_file_info(file_id));
1214 assert_eq!(
1215 file_id,
1216 program.add_file(file2.clone(), dir_id, Some(file_info))
1217 );
1218 assert_eq!(file_info, *program.get_file_info(file_id));
1219
1220 programs.push((program, file_id, encoding));
1221 }
1222 }
1223 }
1224 }
1225
1226 let debug_line_str_offsets = DebugLineStrOffsets::none();
1227 let debug_str_offsets = DebugStrOffsets::none();
1228 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1229 let mut debug_line_offsets = Vec::new();
1230 for (program, _, encoding) in &programs {
1231 debug_line_offsets.push(
1232 program
1233 .write(
1234 &mut debug_line,
1235 *encoding,
1236 &debug_line_str_offsets,
1237 &debug_str_offsets,
1238 )
1239 .unwrap(),
1240 );
1241 }
1242
1243 let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
1244
1245 let convert_address = &|address| Some(Address::Constant(address));
1246 for ((program, file_id, encoding), offset) in programs.iter().zip(debug_line_offsets.iter())
1247 {
1248 let read_program = read_debug_line
1249 .program(
1250 *offset,
1251 encoding.address_size,
1252 Some(read::EndianSlice::new(b"dir1", LittleEndian)),
1253 Some(read::EndianSlice::new(b"file1", LittleEndian)),
1254 )
1255 .unwrap();
1256
1257 let dwarf = read::Dwarf::default();
1258 let mut convert_line_strings = LineStringTable::default();
1259 let mut convert_strings = StringTable::default();
1260 let (convert_program, convert_files) = LineProgram::from(
1261 read_program,
1262 &dwarf,
1263 &mut convert_line_strings,
1264 &mut convert_strings,
1265 convert_address,
1266 )
1267 .unwrap();
1268 assert_eq!(convert_program.version(), program.version());
1269 assert_eq!(convert_program.address_size(), program.address_size());
1270 assert_eq!(convert_program.format(), program.format());
1271
1272 let convert_file_id = convert_files[file_id.raw() as usize];
1273 let (file, dir) = program.get_file(*file_id);
1274 let (convert_file, convert_dir) = convert_program.get_file(convert_file_id);
1275 assert_eq!(file, convert_file);
1276 assert_eq!(
1277 program.get_directory(dir),
1278 convert_program.get_directory(convert_dir)
1279 );
1280 assert_eq!(
1281 program.get_file_info(*file_id),
1282 convert_program.get_file_info(convert_file_id)
1283 );
1284 }
1285 }
1286
1287 #[test]
1288 fn test_line_row() {
1289 let dir1 = &b"dir1"[..];
1290 let file1 = &b"file1"[..];
1291 let file2 = &b"file2"[..];
1292 let convert_address = &|address| Some(Address::Constant(address));
1293
1294 let debug_line_str_offsets = DebugLineStrOffsets::none();
1295 let debug_str_offsets = DebugStrOffsets::none();
1296
1297 for &version in &[2, 3, 4, 5] {
1298 for &address_size in &[4, 8] {
1299 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1300 let encoding = Encoding {
1301 format,
1302 version,
1303 address_size,
1304 };
1305 let line_base = -5;
1306 let line_range = 14;
1307 let neg_line_base = (-line_base) as u8;
1308 let mut program = LineProgram::new(
1309 encoding,
1310 LineEncoding {
1311 line_base,
1312 line_range,
1313 ..Default::default()
1314 },
1315 LineString::String(dir1.to_vec()),
1316 LineString::String(file1.to_vec()),
1317 None,
1318 );
1319 let dir_id = program.default_directory();
1320 program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1321 let file_id =
1322 program.add_file(LineString::String(file2.to_vec()), dir_id, None);
1323
1324 // Test sequences.
1325 {
1326 let mut program = program.clone();
1327 let address = Address::Constant(0x12);
1328 program.begin_sequence(Some(address));
1329 assert_eq!(
1330 program.instructions,
1331 vec![LineInstruction::SetAddress(address)]
1332 );
1333 }
1334
1335 {
1336 let mut program = program.clone();
1337 program.begin_sequence(None);
1338 assert_eq!(program.instructions, Vec::new());
1339 }
1340
1341 {
1342 let mut program = program.clone();
1343 program.begin_sequence(None);
1344 program.end_sequence(0x1234);
1345 assert_eq!(
1346 program.instructions,
1347 vec![
1348 LineInstruction::AdvancePc(0x1234),
1349 LineInstruction::EndSequence
1350 ]
1351 );
1352 }
1353
1354 // Create a base program.
1355 program.begin_sequence(None);
1356 program.row.line = 0x1000;
1357 program.generate_row();
1358 let base_row = program.row;
1359 let base_instructions = program.instructions.clone();
1360
1361 // Create test cases.
1362 let mut tests = Vec::new();
1363
1364 let row = base_row;
1365 tests.push((row, vec![LineInstruction::Copy]));
1366
1367 let mut row = base_row;
1368 row.line -= u64::from(neg_line_base);
1369 tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)]));
1370
1371 let mut row = base_row;
1372 row.line += u64::from(line_range) - 1;
1373 row.line -= u64::from(neg_line_base);
1374 tests.push((
1375 row,
1376 vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)],
1377 ));
1378
1379 let mut row = base_row;
1380 row.line += u64::from(line_range);
1381 row.line -= u64::from(neg_line_base);
1382 tests.push((
1383 row,
1384 vec![
1385 LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)),
1386 LineInstruction::Copy,
1387 ],
1388 ));
1389
1390 let mut row = base_row;
1391 row.address_offset = 1;
1392 row.line -= u64::from(neg_line_base);
1393 tests.push((
1394 row,
1395 vec![LineInstruction::Special(OPCODE_BASE + line_range)],
1396 ));
1397
1398 let op_range = (255 - OPCODE_BASE) / line_range;
1399 let mut row = base_row;
1400 row.address_offset = u64::from(op_range);
1401 row.line -= u64::from(neg_line_base);
1402 tests.push((
1403 row,
1404 vec![LineInstruction::Special(
1405 OPCODE_BASE + op_range * line_range,
1406 )],
1407 ));
1408
1409 let mut row = base_row;
1410 row.address_offset = u64::from(op_range);
1411 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
1412 row.line -= u64::from(neg_line_base);
1413 tests.push((row, vec![LineInstruction::Special(255)]));
1414
1415 let mut row = base_row;
1416 row.address_offset = u64::from(op_range);
1417 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
1418 row.line -= u64::from(neg_line_base);
1419 tests.push((
1420 row,
1421 vec![LineInstruction::ConstAddPc, LineInstruction::Copy],
1422 ));
1423
1424 let mut row = base_row;
1425 row.address_offset = u64::from(op_range);
1426 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
1427 row.line -= u64::from(neg_line_base);
1428 tests.push((
1429 row,
1430 vec![
1431 LineInstruction::ConstAddPc,
1432 LineInstruction::Special(OPCODE_BASE + 6),
1433 ],
1434 ));
1435
1436 let mut row = base_row;
1437 row.address_offset = u64::from(op_range) * 2;
1438 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range);
1439 row.line -= u64::from(neg_line_base);
1440 tests.push((
1441 row,
1442 vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)],
1443 ));
1444
1445 let mut row = base_row;
1446 row.address_offset = u64::from(op_range) * 2;
1447 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1;
1448 row.line -= u64::from(neg_line_base);
1449 tests.push((
1450 row,
1451 vec![
1452 LineInstruction::AdvancePc(row.address_offset),
1453 LineInstruction::Copy,
1454 ],
1455 ));
1456
1457 let mut row = base_row;
1458 row.address_offset = u64::from(op_range) * 2;
1459 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2;
1460 row.line -= u64::from(neg_line_base);
1461 tests.push((
1462 row,
1463 vec![
1464 LineInstruction::AdvancePc(row.address_offset),
1465 LineInstruction::Special(OPCODE_BASE + 6),
1466 ],
1467 ));
1468
1469 let mut row = base_row;
1470 row.address_offset = 0x1234;
1471 tests.push((
1472 row,
1473 vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy],
1474 ));
1475
1476 let mut row = base_row;
1477 row.line += 0x1234;
1478 tests.push((
1479 row,
1480 vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy],
1481 ));
1482
1483 let mut row = base_row;
1484 row.file = file_id;
1485 tests.push((
1486 row,
1487 vec![LineInstruction::SetFile(file_id), LineInstruction::Copy],
1488 ));
1489
1490 let mut row = base_row;
1491 row.column = 0x1234;
1492 tests.push((
1493 row,
1494 vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy],
1495 ));
1496
1497 let mut row = base_row;
1498 row.discriminator = 0x1234;
1499 tests.push((
1500 row,
1501 vec![
1502 LineInstruction::SetDiscriminator(0x1234),
1503 LineInstruction::Copy,
1504 ],
1505 ));
1506
1507 let mut row = base_row;
1508 row.is_statement = !row.is_statement;
1509 tests.push((
1510 row,
1511 vec![LineInstruction::NegateStatement, LineInstruction::Copy],
1512 ));
1513
1514 let mut row = base_row;
1515 row.basic_block = true;
1516 tests.push((
1517 row,
1518 vec![LineInstruction::SetBasicBlock, LineInstruction::Copy],
1519 ));
1520
1521 let mut row = base_row;
1522 row.prologue_end = true;
1523 tests.push((
1524 row,
1525 vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy],
1526 ));
1527
1528 let mut row = base_row;
1529 row.epilogue_begin = true;
1530 tests.push((
1531 row,
1532 vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy],
1533 ));
1534
1535 let mut row = base_row;
1536 row.isa = 0x1234;
1537 tests.push((
1538 row,
1539 vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy],
1540 ));
1541
1542 for test in tests {
1543 // Test generate_row().
1544 let mut program = program.clone();
1545 program.row = test.0;
1546 program.generate_row();
1547 assert_eq!(
1548 &program.instructions[base_instructions.len()..],
1549 &test.1[..]
1550 );
1551
1552 // Test LineProgram::from().
1553 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1554 let debug_line_offset = program
1555 .write(
1556 &mut debug_line,
1557 encoding,
1558 &debug_line_str_offsets,
1559 &debug_str_offsets,
1560 )
1561 .unwrap();
1562
1563 let read_debug_line =
1564 read::DebugLine::new(debug_line.slice(), LittleEndian);
1565 let read_program = read_debug_line
1566 .program(
1567 debug_line_offset,
1568 address_size,
1569 Some(read::EndianSlice::new(dir1, LittleEndian)),
1570 Some(read::EndianSlice::new(file1, LittleEndian)),
1571 )
1572 .unwrap();
1573
1574 let dwarf = read::Dwarf::default();
1575 let mut convert_line_strings = LineStringTable::default();
1576 let mut convert_strings = StringTable::default();
1577 let (convert_program, _convert_files) = LineProgram::from(
1578 read_program,
1579 &dwarf,
1580 &mut convert_line_strings,
1581 &mut convert_strings,
1582 convert_address,
1583 )
1584 .unwrap();
1585 assert_eq!(
1586 &convert_program.instructions[base_instructions.len()..],
1587 &test.1[..]
1588 );
1589 }
1590 }
1591 }
1592 }
1593 }
1594
1595 #[test]
1596 fn test_line_instruction() {
1597 let dir1 = &b"dir1"[..];
1598 let file1 = &b"file1"[..];
1599
1600 let debug_line_str_offsets = DebugLineStrOffsets::none();
1601 let debug_str_offsets = DebugStrOffsets::none();
1602
1603 for &version in &[2, 3, 4, 5] {
1604 for &address_size in &[4, 8] {
1605 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1606 let encoding = Encoding {
1607 format,
1608 version,
1609 address_size,
1610 };
1611 let mut program = LineProgram::new(
1612 encoding,
1613 LineEncoding::default(),
1614 LineString::String(dir1.to_vec()),
1615 LineString::String(file1.to_vec()),
1616 None,
1617 );
1618 let dir_id = program.default_directory();
1619 let file_id =
1620 program.add_file(LineString::String(file1.to_vec()), dir_id, None);
1621
1622 for &(ref inst, ref expect_inst) in &[
1623 (
1624 LineInstruction::Special(OPCODE_BASE),
1625 read::LineInstruction::Special(OPCODE_BASE),
1626 ),
1627 (
1628 LineInstruction::Special(255),
1629 read::LineInstruction::Special(255),
1630 ),
1631 (LineInstruction::Copy, read::LineInstruction::Copy),
1632 (
1633 LineInstruction::AdvancePc(0x12),
1634 read::LineInstruction::AdvancePc(0x12),
1635 ),
1636 (
1637 LineInstruction::AdvanceLine(0x12),
1638 read::LineInstruction::AdvanceLine(0x12),
1639 ),
1640 (
1641 LineInstruction::SetFile(file_id),
1642 read::LineInstruction::SetFile(file_id.raw()),
1643 ),
1644 (
1645 LineInstruction::SetColumn(0x12),
1646 read::LineInstruction::SetColumn(0x12),
1647 ),
1648 (
1649 LineInstruction::NegateStatement,
1650 read::LineInstruction::NegateStatement,
1651 ),
1652 (
1653 LineInstruction::SetBasicBlock,
1654 read::LineInstruction::SetBasicBlock,
1655 ),
1656 (
1657 LineInstruction::ConstAddPc,
1658 read::LineInstruction::ConstAddPc,
1659 ),
1660 (
1661 LineInstruction::SetPrologueEnd,
1662 read::LineInstruction::SetPrologueEnd,
1663 ),
1664 (
1665 LineInstruction::SetEpilogueBegin,
1666 read::LineInstruction::SetEpilogueBegin,
1667 ),
1668 (
1669 LineInstruction::SetIsa(0x12),
1670 read::LineInstruction::SetIsa(0x12),
1671 ),
1672 (
1673 LineInstruction::EndSequence,
1674 read::LineInstruction::EndSequence,
1675 ),
1676 (
1677 LineInstruction::SetAddress(Address::Constant(0x12)),
1678 read::LineInstruction::SetAddress(0x12),
1679 ),
1680 (
1681 LineInstruction::SetDiscriminator(0x12),
1682 read::LineInstruction::SetDiscriminator(0x12),
1683 ),
1684 ][..]
1685 {
1686 let mut program = program.clone();
1687 program.instructions.push(*inst);
1688
1689 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1690 let debug_line_offset = program
1691 .write(
1692 &mut debug_line,
1693 encoding,
1694 &debug_line_str_offsets,
1695 &debug_str_offsets,
1696 )
1697 .unwrap();
1698
1699 let read_debug_line =
1700 read::DebugLine::new(debug_line.slice(), LittleEndian);
1701 let read_program = read_debug_line
1702 .program(
1703 debug_line_offset,
1704 address_size,
1705 Some(read::EndianSlice::new(dir1, LittleEndian)),
1706 Some(read::EndianSlice::new(file1, LittleEndian)),
1707 )
1708 .unwrap();
1709 let read_header = read_program.header();
1710 let mut read_insts = read_header.instructions();
1711 assert_eq!(
1712 *expect_inst,
1713 read_insts.next_instruction(read_header).unwrap().unwrap()
1714 );
1715 assert_eq!(None, read_insts.next_instruction(read_header).unwrap());
1716 }
1717 }
1718 }
1719 }
1720 }
1721
1722 // Test that the address/line advance is correct. We don't test for optimality.
1723 #[test]
1724 fn test_advance() {
1725 let encoding = Encoding {
1726 format: Format::Dwarf32,
1727 version: 4,
1728 address_size: 8,
1729 };
1730
1731 let dir1 = &b"dir1"[..];
1732 let file1 = &b"file1"[..];
1733
1734 let addresses = 0..50;
1735 let lines = -10..25i64;
1736
1737 let debug_line_str_offsets = DebugLineStrOffsets::none();
1738 let debug_str_offsets = DebugStrOffsets::none();
1739
1740 for minimum_instruction_length in vec![1, 4] {
1741 for maximum_operations_per_instruction in vec![1, 3] {
1742 for line_base in vec![-5, 0] {
1743 for line_range in vec![10, 20] {
1744 let line_encoding = LineEncoding {
1745 minimum_instruction_length,
1746 maximum_operations_per_instruction,
1747 line_base,
1748 line_range,
1749 default_is_stmt: true,
1750 };
1751 let mut program = LineProgram::new(
1752 encoding,
1753 line_encoding,
1754 LineString::String(dir1.to_vec()),
1755 LineString::String(file1.to_vec()),
1756 None,
1757 );
1758 for address_advance in addresses.clone() {
1759 program.begin_sequence(Some(Address::Constant(0x1000)));
1760 program.row().line = 0x10000;
1761 program.generate_row();
1762 for line_advance in lines.clone() {
1763 {
1764 let row = program.row();
1765 row.address_offset +=
1766 address_advance * u64::from(minimum_instruction_length);
1767 row.line = row.line.wrapping_add(line_advance as u64);
1768 }
1769 program.generate_row();
1770 }
1771 let address_offset = program.row().address_offset
1772 + u64::from(minimum_instruction_length);
1773 program.end_sequence(address_offset);
1774 }
1775
1776 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1777 let debug_line_offset = program
1778 .write(
1779 &mut debug_line,
1780 encoding,
1781 &debug_line_str_offsets,
1782 &debug_str_offsets,
1783 )
1784 .unwrap();
1785
1786 let read_debug_line =
1787 read::DebugLine::new(debug_line.slice(), LittleEndian);
1788 let read_program = read_debug_line
1789 .program(
1790 debug_line_offset,
1791 8,
1792 Some(read::EndianSlice::new(dir1, LittleEndian)),
1793 Some(read::EndianSlice::new(file1, LittleEndian)),
1794 )
1795 .unwrap();
1796
1797 let mut rows = read_program.rows();
1798 for address_advance in addresses.clone() {
1799 let mut address;
1800 let mut line;
1801 {
1802 let row = rows.next_row().unwrap().unwrap().1;
1803 address = row.address();
1804 line = row.line().unwrap().get();
1805 }
1806 assert_eq!(address, 0x1000);
1807 assert_eq!(line, 0x10000);
1808 for line_advance in lines.clone() {
1809 let row = rows.next_row().unwrap().unwrap().1;
1810 assert_eq!(
1811 row.address() - address,
1812 address_advance * u64::from(minimum_instruction_length)
1813 );
1814 assert_eq!(
1815 (row.line().unwrap().get() as i64) - (line as i64),
1816 line_advance
1817 );
1818 address = row.address();
1819 line = row.line().unwrap().get();
1820 }
1821 let row = rows.next_row().unwrap().unwrap().1;
1822 assert!(row.end_sequence());
1823 }
1824 }
1825 }
1826 }
1827 }
1828 }
1829
1830 #[test]
1831 fn test_line_string() {
1832 let version = 5;
1833
1834 let file = b"file1";
1835
1836 let mut strings = StringTable::default();
1837 let string_id = strings.add("file2");
1838 let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
1839 let debug_str_offsets = strings.write(&mut debug_str).unwrap();
1840
1841 let mut line_strings = LineStringTable::default();
1842 let line_string_id = line_strings.add("file3");
1843 let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian));
1844 let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap();
1845
1846 for &address_size in &[4, 8] {
1847 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1848 let encoding = Encoding {
1849 format,
1850 version,
1851 address_size,
1852 };
1853
1854 for (file, expect_file) in vec![
1855 (
1856 LineString::String(file.to_vec()),
1857 read::AttributeValue::String(read::EndianSlice::new(file, LittleEndian)),
1858 ),
1859 (
1860 LineString::StringRef(string_id),
1861 read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)),
1862 ),
1863 (
1864 LineString::LineStringRef(line_string_id),
1865 read::AttributeValue::DebugLineStrRef(
1866 debug_line_str_offsets.get(line_string_id),
1867 ),
1868 ),
1869 ] {
1870 let program = LineProgram::new(
1871 encoding,
1872 LineEncoding::default(),
1873 LineString::String(b"dir".to_vec()),
1874 file,
1875 None,
1876 );
1877
1878 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1879 let debug_line_offset = program
1880 .write(
1881 &mut debug_line,
1882 encoding,
1883 &debug_line_str_offsets,
1884 &debug_str_offsets,
1885 )
1886 .unwrap();
1887
1888 let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
1889 let read_program = read_debug_line
1890 .program(debug_line_offset, address_size, None, None)
1891 .unwrap();
1892 let read_header = read_program.header();
1893 assert_eq!(read_header.file(0).unwrap().path_name(), expect_file);
1894 }
1895 }
1896 }
1897 }
1898
1899 #[test]
1900 fn test_missing_comp_dir() {
1901 let debug_line_str_offsets = DebugLineStrOffsets::none();
1902 let debug_str_offsets = DebugStrOffsets::none();
1903
1904 for &version in &[2, 3, 4, 5] {
1905 for &address_size in &[4, 8] {
1906 for &format in &[Format::Dwarf32, Format::Dwarf64] {
1907 let encoding = Encoding {
1908 format,
1909 version,
1910 address_size,
1911 };
1912 let program = LineProgram::new(
1913 encoding,
1914 LineEncoding::default(),
1915 LineString::String(Vec::new()),
1916 LineString::String(Vec::new()),
1917 None,
1918 );
1919
1920 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian));
1921 let debug_line_offset = program
1922 .write(
1923 &mut debug_line,
1924 encoding,
1925 &debug_line_str_offsets,
1926 &debug_str_offsets,
1927 )
1928 .unwrap();
1929
1930 let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian);
1931 let read_program = read_debug_line
1932 .program(
1933 debug_line_offset,
1934 address_size,
1935 // Testing missing comp_dir/comp_name.
1936 None,
1937 None,
1938 )
1939 .unwrap();
1940
1941 let dwarf = read::Dwarf::default();
1942 let mut convert_line_strings = LineStringTable::default();
1943 let mut convert_strings = StringTable::default();
1944 let convert_address = &|address| Some(Address::Constant(address));
1945 LineProgram::from(
1946 read_program,
1947 &dwarf,
1948 &mut convert_line_strings,
1949 &mut convert_strings,
1950 convert_address,
1951 )
1952 .unwrap();
1953 }
1954 }
1955 }
1956 }
1957}
1958