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