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