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 = from_row.line().unwrap_or(0); 1106 program.row().column = match from_row.column() { 1107 read::ColumnType::LeftEdge => 0, 1108 read::ColumnType::Column(val) => val, 1109 }; 1110 program.row().discriminator = from_row.discriminator(); 1111 program.row().is_statement = from_row.is_stmt(); 1112 program.row().basic_block = from_row.basic_block(); 1113 program.row().prologue_end = from_row.prologue_end(); 1114 program.row().epilogue_begin = from_row.epilogue_begin(); 1115 program.row().isa = from_row.isa(); 1116 program.generate_row(); 1117 } 1118 from_row.reset(from_program.header()); 1119 } 1120 } 1121 }; 1122 } 1123 Ok((program, files)) 1124 } 1125 } 1126 1127 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>1128 fn from<R: Reader<Offset = usize>>( 1129 from_attr: read::AttributeValue<R>, 1130 dwarf: &read::Dwarf<R>, 1131 line_strings: &mut write::LineStringTable, 1132 strings: &mut write::StringTable, 1133 ) -> ConvertResult<LineString> { 1134 Ok(match from_attr { 1135 read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()), 1136 read::AttributeValue::DebugStrRef(offset) => { 1137 let r = dwarf.debug_str.get_str(offset)?; 1138 let id = strings.add(r.to_slice()?); 1139 LineString::StringRef(id) 1140 } 1141 read::AttributeValue::DebugLineStrRef(offset) => { 1142 let r = dwarf.debug_line_str.get_str(offset)?; 1143 let id = line_strings.add(r.to_slice()?); 1144 LineString::LineStringRef(id) 1145 } 1146 _ => return Err(ConvertError::UnsupportedLineStringForm), 1147 }) 1148 } 1149 } 1150 } 1151 1152 #[cfg(test)] 1153 #[cfg(feature = "read")] 1154 mod tests { 1155 use super::*; 1156 use crate::read; 1157 use crate::write::{DebugLineStr, DebugStr, EndianVec, StringTable}; 1158 use crate::LittleEndian; 1159 1160 #[test] test_line_program_table()1161 fn test_line_program_table() { 1162 let dir1 = LineString::String(b"dir1".to_vec()); 1163 let file1 = LineString::String(b"file1".to_vec()); 1164 let dir2 = LineString::String(b"dir2".to_vec()); 1165 let file2 = LineString::String(b"file2".to_vec()); 1166 1167 let mut programs = Vec::new(); 1168 for &version in &[2, 3, 4, 5] { 1169 for &address_size in &[4, 8] { 1170 for &format in &[Format::Dwarf32, Format::Dwarf64] { 1171 let encoding = Encoding { 1172 format, 1173 version, 1174 address_size, 1175 }; 1176 let mut program = LineProgram::new( 1177 encoding, 1178 LineEncoding::default(), 1179 dir1.clone(), 1180 file1.clone(), 1181 None, 1182 ); 1183 1184 { 1185 assert_eq!(&dir1, program.get_directory(program.default_directory())); 1186 program.file_has_timestamp = true; 1187 program.file_has_size = true; 1188 if encoding.version >= 5 { 1189 program.file_has_md5 = true; 1190 } 1191 1192 let dir_id = program.add_directory(dir2.clone()); 1193 assert_eq!(&dir2, program.get_directory(dir_id)); 1194 assert_eq!(dir_id, program.add_directory(dir2.clone())); 1195 1196 let file_info = FileInfo { 1197 timestamp: 1, 1198 size: 2, 1199 md5: if encoding.version >= 5 { 1200 [3; 16] 1201 } else { 1202 [0; 16] 1203 }, 1204 }; 1205 let file_id = program.add_file(file2.clone(), dir_id, Some(file_info)); 1206 assert_eq!((&file2, dir_id), program.get_file(file_id)); 1207 assert_eq!(file_info, *program.get_file_info(file_id)); 1208 1209 program.get_file_info_mut(file_id).size = 3; 1210 assert_ne!(file_info, *program.get_file_info(file_id)); 1211 assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None)); 1212 assert_ne!(file_info, *program.get_file_info(file_id)); 1213 assert_eq!( 1214 file_id, 1215 program.add_file(file2.clone(), dir_id, Some(file_info)) 1216 ); 1217 assert_eq!(file_info, *program.get_file_info(file_id)); 1218 1219 programs.push((program, file_id, encoding)); 1220 } 1221 } 1222 } 1223 } 1224 1225 let debug_line_str_offsets = DebugLineStrOffsets::none(); 1226 let debug_str_offsets = DebugStrOffsets::none(); 1227 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); 1228 let mut debug_line_offsets = Vec::new(); 1229 for (program, _, encoding) in &programs { 1230 debug_line_offsets.push( 1231 program 1232 .write( 1233 &mut debug_line, 1234 *encoding, 1235 &debug_line_str_offsets, 1236 &debug_str_offsets, 1237 ) 1238 .unwrap(), 1239 ); 1240 } 1241 1242 let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); 1243 1244 let convert_address = &|address| Some(Address::Constant(address)); 1245 for ((program, file_id, encoding), offset) in programs.iter().zip(debug_line_offsets.iter()) 1246 { 1247 let read_program = read_debug_line 1248 .program( 1249 *offset, 1250 encoding.address_size, 1251 Some(read::EndianSlice::new(b"dir1", LittleEndian)), 1252 Some(read::EndianSlice::new(b"file1", LittleEndian)), 1253 ) 1254 .unwrap(); 1255 1256 let dwarf = read::Dwarf::default(); 1257 let mut convert_line_strings = LineStringTable::default(); 1258 let mut convert_strings = StringTable::default(); 1259 let (convert_program, convert_files) = LineProgram::from( 1260 read_program, 1261 &dwarf, 1262 &mut convert_line_strings, 1263 &mut convert_strings, 1264 convert_address, 1265 ) 1266 .unwrap(); 1267 assert_eq!(convert_program.version(), program.version()); 1268 assert_eq!(convert_program.address_size(), program.address_size()); 1269 assert_eq!(convert_program.format(), program.format()); 1270 1271 let convert_file_id = convert_files[file_id.raw() as usize]; 1272 let (file, dir) = program.get_file(*file_id); 1273 let (convert_file, convert_dir) = convert_program.get_file(convert_file_id); 1274 assert_eq!(file, convert_file); 1275 assert_eq!( 1276 program.get_directory(dir), 1277 convert_program.get_directory(convert_dir) 1278 ); 1279 assert_eq!( 1280 program.get_file_info(*file_id), 1281 convert_program.get_file_info(convert_file_id) 1282 ); 1283 } 1284 } 1285 1286 #[test] test_line_row()1287 fn test_line_row() { 1288 let dir1 = &b"dir1"[..]; 1289 let file1 = &b"file1"[..]; 1290 let file2 = &b"file2"[..]; 1291 let convert_address = &|address| Some(Address::Constant(address)); 1292 1293 let debug_line_str_offsets = DebugLineStrOffsets::none(); 1294 let debug_str_offsets = DebugStrOffsets::none(); 1295 1296 for &version in &[2, 3, 4, 5] { 1297 for &address_size in &[4, 8] { 1298 for &format in &[Format::Dwarf32, Format::Dwarf64] { 1299 let encoding = Encoding { 1300 format, 1301 version, 1302 address_size, 1303 }; 1304 let line_base = -5; 1305 let line_range = 14; 1306 let neg_line_base = (-line_base) as u8; 1307 let mut program = LineProgram::new( 1308 encoding, 1309 LineEncoding { 1310 line_base, 1311 line_range, 1312 ..Default::default() 1313 }, 1314 LineString::String(dir1.to_vec()), 1315 LineString::String(file1.to_vec()), 1316 None, 1317 ); 1318 let dir_id = program.default_directory(); 1319 program.add_file(LineString::String(file1.to_vec()), dir_id, None); 1320 let file_id = 1321 program.add_file(LineString::String(file2.to_vec()), dir_id, None); 1322 1323 // Test sequences. 1324 { 1325 let mut program = program.clone(); 1326 let address = Address::Constant(0x12); 1327 program.begin_sequence(Some(address)); 1328 assert_eq!( 1329 program.instructions, 1330 vec![LineInstruction::SetAddress(address)] 1331 ); 1332 } 1333 1334 { 1335 let mut program = program.clone(); 1336 program.begin_sequence(None); 1337 assert_eq!(program.instructions, Vec::new()); 1338 } 1339 1340 { 1341 let mut program = program.clone(); 1342 program.begin_sequence(None); 1343 program.end_sequence(0x1234); 1344 assert_eq!( 1345 program.instructions, 1346 vec![ 1347 LineInstruction::AdvancePc(0x1234), 1348 LineInstruction::EndSequence 1349 ] 1350 ); 1351 } 1352 1353 // Create a base program. 1354 program.begin_sequence(None); 1355 program.row.line = 0x1000; 1356 program.generate_row(); 1357 let base_row = program.row; 1358 let base_instructions = program.instructions.clone(); 1359 1360 // Create test cases. 1361 let mut tests = Vec::new(); 1362 1363 let row = base_row; 1364 tests.push((row, vec![LineInstruction::Copy])); 1365 1366 let mut row = base_row; 1367 row.line -= u64::from(neg_line_base); 1368 tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)])); 1369 1370 let mut row = base_row; 1371 row.line += u64::from(line_range) - 1; 1372 row.line -= u64::from(neg_line_base); 1373 tests.push(( 1374 row, 1375 vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)], 1376 )); 1377 1378 let mut row = base_row; 1379 row.line += u64::from(line_range); 1380 row.line -= u64::from(neg_line_base); 1381 tests.push(( 1382 row, 1383 vec![ 1384 LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)), 1385 LineInstruction::Copy, 1386 ], 1387 )); 1388 1389 let mut row = base_row; 1390 row.address_offset = 1; 1391 row.line -= u64::from(neg_line_base); 1392 tests.push(( 1393 row, 1394 vec![LineInstruction::Special(OPCODE_BASE + line_range)], 1395 )); 1396 1397 let op_range = (255 - OPCODE_BASE) / line_range; 1398 let mut row = base_row; 1399 row.address_offset = u64::from(op_range); 1400 row.line -= u64::from(neg_line_base); 1401 tests.push(( 1402 row, 1403 vec![LineInstruction::Special( 1404 OPCODE_BASE + op_range * line_range, 1405 )], 1406 )); 1407 1408 let mut row = base_row; 1409 row.address_offset = u64::from(op_range); 1410 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); 1411 row.line -= u64::from(neg_line_base); 1412 tests.push((row, vec![LineInstruction::Special(255)])); 1413 1414 let mut row = base_row; 1415 row.address_offset = u64::from(op_range); 1416 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; 1417 row.line -= u64::from(neg_line_base); 1418 tests.push(( 1419 row, 1420 vec![LineInstruction::ConstAddPc, LineInstruction::Copy], 1421 )); 1422 1423 let mut row = base_row; 1424 row.address_offset = u64::from(op_range); 1425 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; 1426 row.line -= u64::from(neg_line_base); 1427 tests.push(( 1428 row, 1429 vec![ 1430 LineInstruction::ConstAddPc, 1431 LineInstruction::Special(OPCODE_BASE + 6), 1432 ], 1433 )); 1434 1435 let mut row = base_row; 1436 row.address_offset = u64::from(op_range) * 2; 1437 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); 1438 row.line -= u64::from(neg_line_base); 1439 tests.push(( 1440 row, 1441 vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)], 1442 )); 1443 1444 let mut row = base_row; 1445 row.address_offset = u64::from(op_range) * 2; 1446 row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; 1447 row.line -= u64::from(neg_line_base); 1448 tests.push(( 1449 row, 1450 vec![ 1451 LineInstruction::AdvancePc(row.address_offset), 1452 LineInstruction::Copy, 1453 ], 1454 )); 1455 1456 let mut row = base_row; 1457 row.address_offset = u64::from(op_range) * 2; 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::AdvancePc(row.address_offset), 1464 LineInstruction::Special(OPCODE_BASE + 6), 1465 ], 1466 )); 1467 1468 let mut row = base_row; 1469 row.address_offset = 0x1234; 1470 tests.push(( 1471 row, 1472 vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy], 1473 )); 1474 1475 let mut row = base_row; 1476 row.line += 0x1234; 1477 tests.push(( 1478 row, 1479 vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy], 1480 )); 1481 1482 let mut row = base_row; 1483 row.file = file_id; 1484 tests.push(( 1485 row, 1486 vec![LineInstruction::SetFile(file_id), LineInstruction::Copy], 1487 )); 1488 1489 let mut row = base_row; 1490 row.column = 0x1234; 1491 tests.push(( 1492 row, 1493 vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy], 1494 )); 1495 1496 let mut row = base_row; 1497 row.discriminator = 0x1234; 1498 tests.push(( 1499 row, 1500 vec![ 1501 LineInstruction::SetDiscriminator(0x1234), 1502 LineInstruction::Copy, 1503 ], 1504 )); 1505 1506 let mut row = base_row; 1507 row.is_statement = !row.is_statement; 1508 tests.push(( 1509 row, 1510 vec![LineInstruction::NegateStatement, LineInstruction::Copy], 1511 )); 1512 1513 let mut row = base_row; 1514 row.basic_block = true; 1515 tests.push(( 1516 row, 1517 vec![LineInstruction::SetBasicBlock, LineInstruction::Copy], 1518 )); 1519 1520 let mut row = base_row; 1521 row.prologue_end = true; 1522 tests.push(( 1523 row, 1524 vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy], 1525 )); 1526 1527 let mut row = base_row; 1528 row.epilogue_begin = true; 1529 tests.push(( 1530 row, 1531 vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy], 1532 )); 1533 1534 let mut row = base_row; 1535 row.isa = 0x1234; 1536 tests.push(( 1537 row, 1538 vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy], 1539 )); 1540 1541 for test in tests { 1542 // Test generate_row(). 1543 let mut program = program.clone(); 1544 program.row = test.0; 1545 program.generate_row(); 1546 assert_eq!( 1547 &program.instructions[base_instructions.len()..], 1548 &test.1[..] 1549 ); 1550 1551 // Test LineProgram::from(). 1552 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); 1553 let debug_line_offset = program 1554 .write( 1555 &mut debug_line, 1556 encoding, 1557 &debug_line_str_offsets, 1558 &debug_str_offsets, 1559 ) 1560 .unwrap(); 1561 1562 let read_debug_line = 1563 read::DebugLine::new(debug_line.slice(), LittleEndian); 1564 let read_program = read_debug_line 1565 .program( 1566 debug_line_offset, 1567 address_size, 1568 Some(read::EndianSlice::new(dir1, LittleEndian)), 1569 Some(read::EndianSlice::new(file1, LittleEndian)), 1570 ) 1571 .unwrap(); 1572 1573 let dwarf = read::Dwarf::default(); 1574 let mut convert_line_strings = LineStringTable::default(); 1575 let mut convert_strings = StringTable::default(); 1576 let (convert_program, _convert_files) = LineProgram::from( 1577 read_program, 1578 &dwarf, 1579 &mut convert_line_strings, 1580 &mut convert_strings, 1581 convert_address, 1582 ) 1583 .unwrap(); 1584 assert_eq!( 1585 &convert_program.instructions[base_instructions.len()..], 1586 &test.1[..] 1587 ); 1588 } 1589 } 1590 } 1591 } 1592 } 1593 1594 #[test] test_line_instruction()1595 fn test_line_instruction() { 1596 let dir1 = &b"dir1"[..]; 1597 let file1 = &b"file1"[..]; 1598 1599 let debug_line_str_offsets = DebugLineStrOffsets::none(); 1600 let debug_str_offsets = DebugStrOffsets::none(); 1601 1602 for &version in &[2, 3, 4, 5] { 1603 for &address_size in &[4, 8] { 1604 for &format in &[Format::Dwarf32, Format::Dwarf64] { 1605 let encoding = Encoding { 1606 format, 1607 version, 1608 address_size, 1609 }; 1610 let mut program = LineProgram::new( 1611 encoding, 1612 LineEncoding::default(), 1613 LineString::String(dir1.to_vec()), 1614 LineString::String(file1.to_vec()), 1615 None, 1616 ); 1617 let dir_id = program.default_directory(); 1618 let file_id = 1619 program.add_file(LineString::String(file1.to_vec()), dir_id, None); 1620 1621 for &(ref inst, ref expect_inst) in &[ 1622 ( 1623 LineInstruction::Special(OPCODE_BASE), 1624 read::LineInstruction::Special(OPCODE_BASE), 1625 ), 1626 ( 1627 LineInstruction::Special(255), 1628 read::LineInstruction::Special(255), 1629 ), 1630 (LineInstruction::Copy, read::LineInstruction::Copy), 1631 ( 1632 LineInstruction::AdvancePc(0x12), 1633 read::LineInstruction::AdvancePc(0x12), 1634 ), 1635 ( 1636 LineInstruction::AdvanceLine(0x12), 1637 read::LineInstruction::AdvanceLine(0x12), 1638 ), 1639 ( 1640 LineInstruction::SetFile(file_id), 1641 read::LineInstruction::SetFile(file_id.raw()), 1642 ), 1643 ( 1644 LineInstruction::SetColumn(0x12), 1645 read::LineInstruction::SetColumn(0x12), 1646 ), 1647 ( 1648 LineInstruction::NegateStatement, 1649 read::LineInstruction::NegateStatement, 1650 ), 1651 ( 1652 LineInstruction::SetBasicBlock, 1653 read::LineInstruction::SetBasicBlock, 1654 ), 1655 ( 1656 LineInstruction::ConstAddPc, 1657 read::LineInstruction::ConstAddPc, 1658 ), 1659 ( 1660 LineInstruction::SetPrologueEnd, 1661 read::LineInstruction::SetPrologueEnd, 1662 ), 1663 ( 1664 LineInstruction::SetEpilogueBegin, 1665 read::LineInstruction::SetEpilogueBegin, 1666 ), 1667 ( 1668 LineInstruction::SetIsa(0x12), 1669 read::LineInstruction::SetIsa(0x12), 1670 ), 1671 ( 1672 LineInstruction::EndSequence, 1673 read::LineInstruction::EndSequence, 1674 ), 1675 ( 1676 LineInstruction::SetAddress(Address::Constant(0x12)), 1677 read::LineInstruction::SetAddress(0x12), 1678 ), 1679 ( 1680 LineInstruction::SetDiscriminator(0x12), 1681 read::LineInstruction::SetDiscriminator(0x12), 1682 ), 1683 ][..] 1684 { 1685 let mut program = program.clone(); 1686 program.instructions.push(*inst); 1687 1688 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); 1689 let debug_line_offset = program 1690 .write( 1691 &mut debug_line, 1692 encoding, 1693 &debug_line_str_offsets, 1694 &debug_str_offsets, 1695 ) 1696 .unwrap(); 1697 1698 let read_debug_line = 1699 read::DebugLine::new(debug_line.slice(), LittleEndian); 1700 let read_program = read_debug_line 1701 .program( 1702 debug_line_offset, 1703 address_size, 1704 Some(read::EndianSlice::new(dir1, LittleEndian)), 1705 Some(read::EndianSlice::new(file1, LittleEndian)), 1706 ) 1707 .unwrap(); 1708 let read_header = read_program.header(); 1709 let mut read_insts = read_header.instructions(); 1710 assert_eq!( 1711 *expect_inst, 1712 read_insts.next_instruction(read_header).unwrap().unwrap() 1713 ); 1714 assert_eq!(None, read_insts.next_instruction(read_header).unwrap()); 1715 } 1716 } 1717 } 1718 } 1719 } 1720 1721 // Test that the address/line advance is correct. We don't test for optimality. 1722 #[test] 1723 #[allow(clippy::useless_vec)] test_advance()1724 fn test_advance() { 1725 let encoding = Encoding { 1726 format: Format::Dwarf32, 1727 version: 4, 1728 address_size: 8, 1729 }; 1730 1731 let dir1 = &b"dir1"[..]; 1732 let file1 = &b"file1"[..]; 1733 1734 let addresses = 0..50; 1735 let lines = -10..25i64; 1736 1737 let debug_line_str_offsets = DebugLineStrOffsets::none(); 1738 let debug_str_offsets = DebugStrOffsets::none(); 1739 1740 for minimum_instruction_length in vec![1, 4] { 1741 for maximum_operations_per_instruction in vec![1, 3] { 1742 for line_base in vec![-5, 0] { 1743 for line_range in vec![10, 20] { 1744 let line_encoding = LineEncoding { 1745 minimum_instruction_length, 1746 maximum_operations_per_instruction, 1747 line_base, 1748 line_range, 1749 default_is_stmt: true, 1750 }; 1751 let mut program = LineProgram::new( 1752 encoding, 1753 line_encoding, 1754 LineString::String(dir1.to_vec()), 1755 LineString::String(file1.to_vec()), 1756 None, 1757 ); 1758 for address_advance in addresses.clone() { 1759 program.begin_sequence(Some(Address::Constant(0x1000))); 1760 program.row().line = 0x10000; 1761 program.generate_row(); 1762 for line_advance in lines.clone() { 1763 { 1764 let row = program.row(); 1765 row.address_offset += 1766 address_advance * u64::from(minimum_instruction_length); 1767 row.line = row.line.wrapping_add(line_advance as u64); 1768 } 1769 program.generate_row(); 1770 } 1771 let address_offset = program.row().address_offset 1772 + u64::from(minimum_instruction_length); 1773 program.end_sequence(address_offset); 1774 } 1775 1776 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); 1777 let debug_line_offset = program 1778 .write( 1779 &mut debug_line, 1780 encoding, 1781 &debug_line_str_offsets, 1782 &debug_str_offsets, 1783 ) 1784 .unwrap(); 1785 1786 let read_debug_line = 1787 read::DebugLine::new(debug_line.slice(), LittleEndian); 1788 let read_program = read_debug_line 1789 .program( 1790 debug_line_offset, 1791 8, 1792 Some(read::EndianSlice::new(dir1, LittleEndian)), 1793 Some(read::EndianSlice::new(file1, LittleEndian)), 1794 ) 1795 .unwrap(); 1796 1797 let mut rows = read_program.rows(); 1798 for address_advance in addresses.clone() { 1799 let mut address; 1800 let mut line; 1801 { 1802 let row = rows.next_row().unwrap().unwrap().1; 1803 address = row.address(); 1804 line = row.line().unwrap(); 1805 } 1806 assert_eq!(address, 0x1000); 1807 assert_eq!(line, 0x10000); 1808 for line_advance in lines.clone() { 1809 let row = rows.next_row().unwrap().unwrap().1; 1810 assert_eq!( 1811 row.address() - address, 1812 address_advance * u64::from(minimum_instruction_length) 1813 ); 1814 assert_eq!( 1815 (row.line().unwrap() as i64) - (line as i64), 1816 line_advance 1817 ); 1818 address = row.address(); 1819 line = row.line().unwrap(); 1820 } 1821 let row = rows.next_row().unwrap().unwrap().1; 1822 assert!(row.end_sequence()); 1823 } 1824 } 1825 } 1826 } 1827 } 1828 } 1829 1830 #[test] test_line_string()1831 fn test_line_string() { 1832 let version = 5; 1833 1834 let file = b"file1"; 1835 1836 let mut strings = StringTable::default(); 1837 let string_id = strings.add("file2"); 1838 let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); 1839 let debug_str_offsets = strings.write(&mut debug_str).unwrap(); 1840 1841 let mut line_strings = LineStringTable::default(); 1842 let line_string_id = line_strings.add("file3"); 1843 let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian)); 1844 let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap(); 1845 1846 for &address_size in &[4, 8] { 1847 for &format in &[Format::Dwarf32, Format::Dwarf64] { 1848 let encoding = Encoding { 1849 format, 1850 version, 1851 address_size, 1852 }; 1853 1854 for (file, expect_file) in vec![ 1855 ( 1856 LineString::String(file.to_vec()), 1857 read::AttributeValue::String(read::EndianSlice::new(file, LittleEndian)), 1858 ), 1859 ( 1860 LineString::StringRef(string_id), 1861 read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)), 1862 ), 1863 ( 1864 LineString::LineStringRef(line_string_id), 1865 read::AttributeValue::DebugLineStrRef( 1866 debug_line_str_offsets.get(line_string_id), 1867 ), 1868 ), 1869 ] { 1870 let program = LineProgram::new( 1871 encoding, 1872 LineEncoding::default(), 1873 LineString::String(b"dir".to_vec()), 1874 file, 1875 None, 1876 ); 1877 1878 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); 1879 let debug_line_offset = program 1880 .write( 1881 &mut debug_line, 1882 encoding, 1883 &debug_line_str_offsets, 1884 &debug_str_offsets, 1885 ) 1886 .unwrap(); 1887 1888 let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); 1889 let read_program = read_debug_line 1890 .program(debug_line_offset, address_size, None, None) 1891 .unwrap(); 1892 let read_header = read_program.header(); 1893 assert_eq!(read_header.file(0).unwrap().path_name(), expect_file); 1894 } 1895 } 1896 } 1897 } 1898 1899 #[test] test_missing_comp_dir()1900 fn test_missing_comp_dir() { 1901 let debug_line_str_offsets = DebugLineStrOffsets::none(); 1902 let debug_str_offsets = DebugStrOffsets::none(); 1903 1904 for &version in &[2, 3, 4, 5] { 1905 for &address_size in &[4, 8] { 1906 for &format in &[Format::Dwarf32, Format::Dwarf64] { 1907 let encoding = Encoding { 1908 format, 1909 version, 1910 address_size, 1911 }; 1912 let program = LineProgram::new( 1913 encoding, 1914 LineEncoding::default(), 1915 LineString::String(Vec::new()), 1916 LineString::String(Vec::new()), 1917 None, 1918 ); 1919 1920 let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); 1921 let debug_line_offset = program 1922 .write( 1923 &mut debug_line, 1924 encoding, 1925 &debug_line_str_offsets, 1926 &debug_str_offsets, 1927 ) 1928 .unwrap(); 1929 1930 let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); 1931 let read_program = read_debug_line 1932 .program( 1933 debug_line_offset, 1934 address_size, 1935 // Testing missing comp_dir/comp_name. 1936 None, 1937 None, 1938 ) 1939 .unwrap(); 1940 1941 let dwarf = read::Dwarf::default(); 1942 let mut convert_line_strings = LineStringTable::default(); 1943 let mut convert_strings = StringTable::default(); 1944 let convert_address = &|address| Some(Address::Constant(address)); 1945 LineProgram::from( 1946 read_program, 1947 &dwarf, 1948 &mut convert_line_strings, 1949 &mut convert_strings, 1950 convert_address, 1951 ) 1952 .unwrap(); 1953 } 1954 } 1955 } 1956 } 1957 } 1958