diff options
Diffstat (limited to 'vendor/gimli/src/write/line.rs')
-rw-r--r-- | vendor/gimli/src/write/line.rs | 1957 |
1 files changed, 0 insertions, 1957 deletions
diff --git a/vendor/gimli/src/write/line.rs b/vendor/gimli/src/write/line.rs deleted file mode 100644 index c88b735..0000000 --- a/vendor/gimli/src/write/line.rs +++ /dev/null @@ -1,1957 +0,0 @@ -use alloc::vec::Vec; -use indexmap::{IndexMap, IndexSet}; -use std::ops::{Deref, DerefMut}; - -use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId}; -use crate::constants; -use crate::leb128; -use crate::write::{ - Address, DebugLineStrOffsets, DebugStrOffsets, Error, LineStringId, LineStringTable, Result, - Section, StringId, Writer, -}; - -/// The number assigned to the first special opcode. -// -// We output all instructions for all DWARF versions, since readers -// should be able to ignore instructions they don't support. -const OPCODE_BASE: u8 = 13; - -/// A line number program. -#[derive(Debug, Clone)] -pub struct LineProgram { - /// True if this line program was created with `LineProgram::none()`. - none: bool, - encoding: Encoding, - line_encoding: LineEncoding, - - /// A list of source directory path names. - /// - /// If a path is relative, then the directory is located relative to the working - /// directory of the compilation unit. - /// - /// The first entry is for the working directory of the compilation unit. - directories: IndexSet<LineString>, - - /// A list of source file entries. - /// - /// Each entry has a path name and a directory. - /// - /// If a path is a relative, then the file is located relative to the - /// directory. Otherwise the directory is meaningless. - /// - /// Does not include comp_file, even for version >= 5. - files: IndexMap<(LineString, DirectoryId), FileInfo>, - - /// The primary source file of the compilation unit. - /// This is required for version >= 5, but we never reference it elsewhere - /// because DWARF defines DW_AT_decl_file=0 to mean not specified. - comp_file: (LineString, FileInfo), - - /// True if the file entries may have valid timestamps. - /// - /// Entries may still have a timestamp of 0 even if this is set. - /// For version <= 4, this is ignored. - /// For version 5, this controls whether to emit `DW_LNCT_timestamp`. - pub file_has_timestamp: bool, - - /// True if the file entries may have valid sizes. - /// - /// Entries may still have a size of 0 even if this is set. - /// For version <= 4, this is ignored. - /// For version 5, this controls whether to emit `DW_LNCT_size`. - pub file_has_size: bool, - - /// True if the file entries have valid MD5 checksums. - /// - /// For version <= 4, this is ignored. - /// For version 5, this controls whether to emit `DW_LNCT_MD5`. - pub file_has_md5: bool, - - prev_row: LineRow, - row: LineRow, - // TODO: this probably should be either rows or sequences instead - instructions: Vec<LineInstruction>, - in_sequence: bool, -} - -impl LineProgram { - /// Create a new `LineProgram`. - /// - /// `comp_dir` defines the working directory of the compilation unit, - /// and must be the same as the `DW_AT_comp_dir` attribute - /// of the compilation unit DIE. - /// - /// `comp_file` and `comp_file_info` define the primary source file - /// of the compilation unit and must be the same as the `DW_AT_name` - /// attribute of the compilation unit DIE. - /// - /// # Panics - /// - /// Panics if `line_encoding.line_base` > 0. - /// - /// Panics if `line_encoding.line_base` + `line_encoding.line_range` <= 0. - /// - /// Panics if `comp_dir` is empty or contains a null byte. - /// - /// Panics if `comp_file` is empty or contains a null byte. - pub fn new( - encoding: Encoding, - line_encoding: LineEncoding, - comp_dir: LineString, - comp_file: LineString, - comp_file_info: Option<FileInfo>, - ) -> LineProgram { - // We require a special opcode for a line advance of 0. - // See the debug_asserts in generate_row(). - assert!(line_encoding.line_base <= 0); - assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0); - let mut program = LineProgram { - none: false, - encoding, - line_encoding, - directories: IndexSet::new(), - files: IndexMap::new(), - comp_file: (comp_file, comp_file_info.unwrap_or_default()), - prev_row: LineRow::initial_state(line_encoding), - row: LineRow::initial_state(line_encoding), - instructions: Vec::new(), - in_sequence: false, - file_has_timestamp: false, - file_has_size: false, - file_has_md5: false, - }; - // For all DWARF versions, directory index 0 is comp_dir. - // For version <= 4, the entry is implicit. We still add - // it here so that we use it, but we don't emit it. - program.add_directory(comp_dir); - program - } - - /// Create a new `LineProgram` with no fields set. - /// - /// This can be used when the `LineProgram` will not be used. - /// - /// You should not attempt to add files or line instructions to - /// this line program, or write it to the `.debug_line` section. - pub fn none() -> Self { - let line_encoding = LineEncoding::default(); - LineProgram { - none: true, - encoding: Encoding { - format: Format::Dwarf32, - version: 2, - address_size: 0, - }, - line_encoding, - directories: IndexSet::new(), - files: IndexMap::new(), - comp_file: (LineString::String(Vec::new()), FileInfo::default()), - prev_row: LineRow::initial_state(line_encoding), - row: LineRow::initial_state(line_encoding), - instructions: Vec::new(), - in_sequence: false, - file_has_timestamp: false, - file_has_size: false, - file_has_md5: false, - } - } - - /// Return true if this line program was created with `LineProgram::none()`. - #[inline] - pub fn is_none(&self) -> bool { - self.none - } - - /// Return the encoding parameters for this line program. - #[inline] - pub fn encoding(&self) -> Encoding { - self.encoding - } - - /// Return the DWARF version for this line program. - #[inline] - pub fn version(&self) -> u16 { - self.encoding.version - } - - /// Return the address size in bytes for this line program. - #[inline] - pub fn address_size(&self) -> u8 { - self.encoding.address_size - } - - /// Return the DWARF format for this line program. - #[inline] - pub fn format(&self) -> Format { - self.encoding.format - } - - /// Return the id for the working directory of the compilation unit. - #[inline] - pub fn default_directory(&self) -> DirectoryId { - DirectoryId(0) - } - - /// Add a directory entry and return its id. - /// - /// If the directory already exists, then return the id of the existing entry. - /// - /// If the path is relative, then the directory is located relative to the working - /// directory of the compilation unit. - /// - /// # Panics - /// - /// Panics if `directory` is empty or contains a null byte. - pub fn add_directory(&mut self, directory: LineString) -> DirectoryId { - if let LineString::String(ref val) = directory { - // For DWARF version <= 4, directories must not be empty. - // The first directory isn't emitted so skip the check for it. - if self.encoding.version <= 4 && !self.directories.is_empty() { - assert!(!val.is_empty()); - } - assert!(!val.contains(&0)); - } - let (index, _) = self.directories.insert_full(directory); - DirectoryId(index) - } - - /// Get a reference to a directory entry. - /// - /// # Panics - /// - /// Panics if `id` is invalid. - pub fn get_directory(&self, id: DirectoryId) -> &LineString { - self.directories.get_index(id.0).unwrap() - } - - /// Add a file entry and return its id. - /// - /// If the file already exists, then return the id of the existing entry. - /// - /// If the file path is relative, then the file is located relative - /// to the directory. Otherwise the directory is meaningless, but it - /// is still used as a key for file entries. - /// - /// If `info` is `None`, then new entries are assigned - /// default information, and existing entries are unmodified. - /// - /// If `info` is not `None`, then it is always assigned to the - /// entry, even if the entry already exists. - /// - /// # Panics - /// - /// Panics if 'file' is empty or contains a null byte. - pub fn add_file( - &mut self, - file: LineString, - directory: DirectoryId, - info: Option<FileInfo>, - ) -> FileId { - if let LineString::String(ref val) = file { - assert!(!val.is_empty()); - assert!(!val.contains(&0)); - } - - let key = (file, directory); - let index = if let Some(info) = info { - let (index, _) = self.files.insert_full(key, info); - index - } else { - let entry = self.files.entry(key); - let index = entry.index(); - entry.or_default(); - index - }; - FileId::new(index) - } - - /// Get a reference to a file entry. - /// - /// # Panics - /// - /// Panics if `id` is invalid. - pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) { - match id.index() { - None => (&self.comp_file.0, DirectoryId(0)), - Some(index) => self - .files - .get_index(index) - .map(|entry| (&(entry.0).0, (entry.0).1)) - .unwrap(), - } - } - - /// Get a reference to the info for a file entry. - /// - /// # Panics - /// - /// Panics if `id` is invalid. - pub fn get_file_info(&self, id: FileId) -> &FileInfo { - match id.index() { - None => &self.comp_file.1, - Some(index) => self.files.get_index(index).map(|entry| entry.1).unwrap(), - } - } - - /// Get a mutable reference to the info for a file entry. - /// - /// # Panics - /// - /// Panics if `id` is invalid. - pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo { - match id.index() { - None => &mut self.comp_file.1, - Some(index) => self - .files - .get_index_mut(index) - .map(|entry| entry.1) - .unwrap(), - } - } - - /// Begin a new sequence and set its base address. - /// - /// # Panics - /// - /// Panics if a sequence has already begun. - pub fn begin_sequence(&mut self, address: Option<Address>) { - assert!(!self.in_sequence); - self.in_sequence = true; - if let Some(address) = address { - self.instructions.push(LineInstruction::SetAddress(address)); - } - } - - /// End the sequence, and reset the row to its default values. - /// - /// Only the `address_offset` and op_index` fields of the current row are used. - /// - /// # Panics - /// - /// Panics if a sequence has not begun. - pub fn end_sequence(&mut self, address_offset: u64) { - assert!(self.in_sequence); - self.in_sequence = false; - self.row.address_offset = address_offset; - let op_advance = self.op_advance(); - if op_advance != 0 { - self.instructions - .push(LineInstruction::AdvancePc(op_advance)); - } - self.instructions.push(LineInstruction::EndSequence); - self.prev_row = LineRow::initial_state(self.line_encoding); - self.row = LineRow::initial_state(self.line_encoding); - } - - /// Return true if a sequence has begun. - #[inline] - pub fn in_sequence(&self) -> bool { - self.in_sequence - } - - /// Returns a reference to the data for the current row. - #[inline] - pub fn row(&mut self) -> &mut LineRow { - &mut self.row - } - - /// Generates the line number information instructions for the current row. - /// - /// After the instructions are generated, it sets `discriminator` to 0, and sets - /// `basic_block`, `prologue_end`, and `epilogue_begin` to false. - /// - /// # Panics - /// - /// Panics if a sequence has not begun. - /// Panics if the address_offset decreases. - pub fn generate_row(&mut self) { - assert!(self.in_sequence); - - // Output fields that are reset on every row. - if self.row.discriminator != 0 { - self.instructions - .push(LineInstruction::SetDiscriminator(self.row.discriminator)); - self.row.discriminator = 0; - } - if self.row.basic_block { - self.instructions.push(LineInstruction::SetBasicBlock); - self.row.basic_block = false; - } - if self.row.prologue_end { - self.instructions.push(LineInstruction::SetPrologueEnd); - self.row.prologue_end = false; - } - if self.row.epilogue_begin { - self.instructions.push(LineInstruction::SetEpilogueBegin); - self.row.epilogue_begin = false; - } - - // Output fields that are not reset on every row. - if self.row.is_statement != self.prev_row.is_statement { - self.instructions.push(LineInstruction::NegateStatement); - } - if self.row.file != self.prev_row.file { - self.instructions - .push(LineInstruction::SetFile(self.row.file)); - } - if self.row.column != self.prev_row.column { - self.instructions - .push(LineInstruction::SetColumn(self.row.column)); - } - if self.row.isa != self.prev_row.isa { - self.instructions - .push(LineInstruction::SetIsa(self.row.isa)); - } - - // Advance the line, address, and operation index. - let line_base = i64::from(self.line_encoding.line_base) as u64; - let line_range = u64::from(self.line_encoding.line_range); - let line_advance = self.row.line as i64 - self.prev_row.line as i64; - let op_advance = self.op_advance(); - - // Default to special advances of 0. - let special_base = u64::from(OPCODE_BASE); - // TODO: handle lack of special opcodes for 0 line advance - debug_assert!(self.line_encoding.line_base <= 0); - debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0); - let special_default = special_base.wrapping_sub(line_base); - let mut special = special_default; - let mut use_special = false; - - if line_advance != 0 { - let special_line = (line_advance as u64).wrapping_sub(line_base); - if special_line < line_range { - special = special_base + special_line; - use_special = true; - } else { - self.instructions - .push(LineInstruction::AdvanceLine(line_advance)); - } - } - - if op_advance != 0 { - // Using ConstAddPc can save a byte. - let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 { - (op_advance, false) - } else { - let op_range = (255 - special_base) / line_range; - (op_advance - op_range, true) - }; - - let special_op = special_op_advance * line_range; - if special + special_op <= 255 { - special += special_op; - use_special = true; - if const_add_pc { - self.instructions.push(LineInstruction::ConstAddPc); - } - } else { - self.instructions - .push(LineInstruction::AdvancePc(op_advance)); - } - } - - if use_special && special != special_default { - debug_assert!(special >= special_base); - debug_assert!(special <= 255); - self.instructions - .push(LineInstruction::Special(special as u8)); - } else { - self.instructions.push(LineInstruction::Copy); - } - - self.prev_row = self.row; - } - - fn op_advance(&self) -> u64 { - debug_assert!(self.row.address_offset >= self.prev_row.address_offset); - let mut address_advance = self.row.address_offset - self.prev_row.address_offset; - if self.line_encoding.minimum_instruction_length != 1 { - debug_assert_eq!( - self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length), - 0 - ); - address_advance /= u64::from(self.line_encoding.minimum_instruction_length); - } - address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction) - + self.row.op_index - - self.prev_row.op_index - } - - /// Returns true if the line number program has no instructions. - /// - /// Does not check the file or directory entries. - #[inline] - pub fn is_empty(&self) -> bool { - self.instructions.is_empty() - } - - /// Write the line number program to the given section. - /// - /// # Panics - /// - /// Panics if `self.is_none()`. - pub fn write<W: Writer>( - &self, - w: &mut DebugLine<W>, - encoding: Encoding, - debug_line_str_offsets: &DebugLineStrOffsets, - debug_str_offsets: &DebugStrOffsets, - ) -> Result<DebugLineOffset> { - assert!(!self.is_none()); - - if encoding.version < self.version() - || encoding.format != self.format() - || encoding.address_size != self.address_size() - { - return Err(Error::IncompatibleLineProgramEncoding); - } - - let offset = w.offset(); - - let length_offset = w.write_initial_length(self.format())?; - let length_base = w.len(); - - if self.version() < 2 || self.version() > 5 { - return Err(Error::UnsupportedVersion(self.version())); - } - w.write_u16(self.version())?; - - if self.version() >= 5 { - w.write_u8(self.address_size())?; - // Segment selector size. - w.write_u8(0)?; - } - - let header_length_offset = w.len(); - w.write_udata(0, self.format().word_size())?; - let header_length_base = w.len(); - - w.write_u8(self.line_encoding.minimum_instruction_length)?; - if self.version() >= 4 { - w.write_u8(self.line_encoding.maximum_operations_per_instruction)?; - } else if self.line_encoding.maximum_operations_per_instruction != 1 { - return Err(Error::NeedVersion(4)); - }; - w.write_u8(if self.line_encoding.default_is_stmt { - 1 - } else { - 0 - })?; - w.write_u8(self.line_encoding.line_base as u8)?; - w.write_u8(self.line_encoding.line_range)?; - w.write_u8(OPCODE_BASE)?; - w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?; - - if self.version() <= 4 { - // The first directory is stored as DW_AT_comp_dir. - for dir in self.directories.iter().skip(1) { - dir.write( - w, - constants::DW_FORM_string, - self.encoding, - debug_line_str_offsets, - debug_str_offsets, - )?; - } - w.write_u8(0)?; - - for ((file, dir), info) in self.files.iter() { - file.write( - w, - constants::DW_FORM_string, - self.encoding, - debug_line_str_offsets, - debug_str_offsets, - )?; - w.write_uleb128(dir.0 as u64)?; - w.write_uleb128(info.timestamp)?; - w.write_uleb128(info.size)?; - } - w.write_u8(0)?; - } else { - // Directory entry formats (only ever 1). - w.write_u8(1)?; - w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; - let dir_form = self.directories.get_index(0).unwrap().form(); - w.write_uleb128(dir_form.0.into())?; - - // Directory entries. - w.write_uleb128(self.directories.len() as u64)?; - for dir in self.directories.iter() { - dir.write( - w, - dir_form, - self.encoding, - debug_line_str_offsets, - debug_str_offsets, - )?; - } - - // File name entry formats. - let count = 2 - + if self.file_has_timestamp { 1 } else { 0 } - + if self.file_has_size { 1 } else { 0 } - + if self.file_has_md5 { 1 } else { 0 }; - w.write_u8(count)?; - w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; - let file_form = self.comp_file.0.form(); - w.write_uleb128(file_form.0.into())?; - w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?; - w.write_uleb128(constants::DW_FORM_udata.0.into())?; - if self.file_has_timestamp { - w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?; - w.write_uleb128(constants::DW_FORM_udata.0.into())?; - } - if self.file_has_size { - w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?; - w.write_uleb128(constants::DW_FORM_udata.0.into())?; - } - if self.file_has_md5 { - w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?; - w.write_uleb128(constants::DW_FORM_data16.0.into())?; - } - - // File name entries. - w.write_uleb128(self.files.len() as u64 + 1)?; - let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| { - file.write( - w, - file_form, - self.encoding, - debug_line_str_offsets, - debug_str_offsets, - )?; - w.write_uleb128(dir.0 as u64)?; - if self.file_has_timestamp { - w.write_uleb128(info.timestamp)?; - } - if self.file_has_size { - w.write_uleb128(info.size)?; - } - if self.file_has_md5 { - w.write(&info.md5)?; - } - Ok(()) - }; - write_file(&self.comp_file.0, DirectoryId(0), &self.comp_file.1)?; - for ((file, dir), info) in self.files.iter() { - write_file(file, *dir, info)?; - } - } - - let header_length = (w.len() - header_length_base) as u64; - w.write_udata_at( - header_length_offset, - header_length, - self.format().word_size(), - )?; - - for instruction in &self.instructions { - instruction.write(w, self.address_size())?; - } - - let length = (w.len() - length_base) as u64; - w.write_initial_length_at(length_offset, length, self.format())?; - - Ok(offset) - } -} - -/// A row in the line number table that corresponds to a machine instruction. -#[derive(Debug, Clone, Copy)] -pub struct LineRow { - /// The offset of the instruction from the start address of the sequence. - pub address_offset: u64, - /// The index of an operation within a VLIW instruction. - /// - /// The index of the first operation is 0. - /// Set to 0 for non-VLIW instructions. - pub op_index: u64, - - /// The source file corresponding to the instruction. - pub file: FileId, - /// The line number within the source file. - /// - /// Lines are numbered beginning at 1. Set to 0 if there is no source line. - pub line: u64, - /// The column number within the source line. - /// - /// Columns are numbered beginning at 1. Set to 0 for the "left edge" of the line. - pub column: u64, - /// An additional discriminator used to distinguish between source locations. - /// This value is assigned arbitrarily by the DWARF producer. - pub discriminator: u64, - - /// Set to true if the instruction is a recommended breakpoint for a statement. - pub is_statement: bool, - /// Set to true if the instruction is the beginning of a basic block. - pub basic_block: bool, - /// Set to true if the instruction is a recommended breakpoint at the entry of a - /// function. - pub prologue_end: bool, - /// Set to true if the instruction is a recommended breakpoint prior to the exit of - /// a function. - pub epilogue_begin: bool, - - /// The instruction set architecture of the instruction. - /// - /// Set to 0 for the default ISA. Other values are defined by the architecture ABI. - pub isa: u64, -} - -impl LineRow { - /// Return the initial state as specified in the DWARF standard. - fn initial_state(line_encoding: LineEncoding) -> Self { - LineRow { - address_offset: 0, - op_index: 0, - - file: FileId::initial_state(), - line: 1, - column: 0, - discriminator: 0, - - is_statement: line_encoding.default_is_stmt, - basic_block: false, - prologue_end: false, - epilogue_begin: false, - - isa: 0, - } - } -} - -/// An instruction in a line number program. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum LineInstruction { - // Special opcodes - Special(u8), - - // Standard opcodes - Copy, - AdvancePc(u64), - AdvanceLine(i64), - SetFile(FileId), - SetColumn(u64), - NegateStatement, - SetBasicBlock, - ConstAddPc, - // DW_LNS_fixed_advance_pc is not supported. - SetPrologueEnd, - SetEpilogueBegin, - SetIsa(u64), - - // Extended opcodes - EndSequence, - // TODO: this doubles the size of this enum. - SetAddress(Address), - // DW_LNE_define_file is not supported. - SetDiscriminator(u64), -} - -impl LineInstruction { - /// Write the line number instruction to the given section. - fn write<W: Writer>(self, w: &mut DebugLine<W>, address_size: u8) -> Result<()> { - use self::LineInstruction::*; - match self { - Special(val) => w.write_u8(val)?, - Copy => w.write_u8(constants::DW_LNS_copy.0)?, - AdvancePc(val) => { - w.write_u8(constants::DW_LNS_advance_pc.0)?; - w.write_uleb128(val)?; - } - AdvanceLine(val) => { - w.write_u8(constants::DW_LNS_advance_line.0)?; - w.write_sleb128(val)?; - } - SetFile(val) => { - w.write_u8(constants::DW_LNS_set_file.0)?; - w.write_uleb128(val.raw())?; - } - SetColumn(val) => { - w.write_u8(constants::DW_LNS_set_column.0)?; - w.write_uleb128(val)?; - } - NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?, - SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?, - ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?, - SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?, - SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?, - SetIsa(val) => { - w.write_u8(constants::DW_LNS_set_isa.0)?; - w.write_uleb128(val)?; - } - EndSequence => { - w.write_u8(0)?; - w.write_uleb128(1)?; - w.write_u8(constants::DW_LNE_end_sequence.0)?; - } - SetAddress(address) => { - w.write_u8(0)?; - w.write_uleb128(1 + u64::from(address_size))?; - w.write_u8(constants::DW_LNE_set_address.0)?; - w.write_address(address, address_size)?; - } - SetDiscriminator(val) => { - let mut bytes = [0u8; 10]; - // bytes is long enough so this will never fail. - let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap(); - w.write_u8(0)?; - w.write_uleb128(1 + len as u64)?; - w.write_u8(constants::DW_LNE_set_discriminator.0)?; - w.write(&bytes[..len])?; - } - } - Ok(()) - } -} - -/// A string value for use in defining paths in line number programs. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum LineString { - /// A slice of bytes representing a string. Must not include null bytes. - /// Not guaranteed to be UTF-8 or anything like that. - String(Vec<u8>), - - /// A reference to a string in the `.debug_str` section. - StringRef(StringId), - - /// A reference to a string in the `.debug_line_str` section. - LineStringRef(LineStringId), -} - -impl LineString { - /// Create a `LineString` using the normal form for the given encoding. - pub fn new<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self - where - T: Into<Vec<u8>>, - { - let val = val.into(); - if encoding.version <= 4 { - LineString::String(val) - } else { - LineString::LineStringRef(line_strings.add(val)) - } - } - - fn form(&self) -> constants::DwForm { - match *self { - LineString::String(..) => constants::DW_FORM_string, - LineString::StringRef(..) => constants::DW_FORM_strp, - LineString::LineStringRef(..) => constants::DW_FORM_line_strp, - } - } - - fn write<W: Writer>( - &self, - w: &mut DebugLine<W>, - form: constants::DwForm, - encoding: Encoding, - debug_line_str_offsets: &DebugLineStrOffsets, - debug_str_offsets: &DebugStrOffsets, - ) -> Result<()> { - if form != self.form() { - return Err(Error::LineStringFormMismatch); - } - - match *self { - LineString::String(ref val) => { - if encoding.version <= 4 { - debug_assert!(!val.is_empty()); - } - w.write(val)?; - w.write_u8(0)?; - } - LineString::StringRef(val) => { - if encoding.version < 5 { - return Err(Error::NeedVersion(5)); - } - w.write_offset( - debug_str_offsets.get(val).0, - SectionId::DebugStr, - encoding.format.word_size(), - )?; - } - LineString::LineStringRef(val) => { - if encoding.version < 5 { - return Err(Error::NeedVersion(5)); - } - w.write_offset( - debug_line_str_offsets.get(val).0, - SectionId::DebugLineStr, - encoding.format.word_size(), - )?; - } - } - Ok(()) - } -} - -/// An identifier for a directory in a `LineProgram`. -/// -/// Defaults to the working directory of the compilation unit. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct DirectoryId(usize); - -// Force FileId access via the methods. -mod id { - /// An identifier for a file in a `LineProgram`. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct FileId(usize); - - impl FileId { - /// Create a FileId given an index into `LineProgram::files`. - pub(crate) fn new(index: usize) -> Self { - FileId(index + 1) - } - - /// The index of the file in `LineProgram::files`. - pub(super) fn index(self) -> Option<usize> { - if self.0 == 0 { - None - } else { - Some(self.0 - 1) - } - } - - /// The initial state of the file register. - pub(super) fn initial_state() -> Self { - FileId(1) - } - - /// The raw value used when writing. - pub(crate) fn raw(self) -> u64 { - self.0 as u64 - } - - /// The id for file index 0 in DWARF version 5. - /// Only used when converting. - // Used for tests only. - #[allow(unused)] - pub(super) fn zero() -> Self { - FileId(0) - } - } -} -pub use self::id::*; - -/// Extra information for file in a `LineProgram`. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub struct FileInfo { - /// The implementation defined timestamp of the last modification of the file, - /// or 0 if not available. - pub timestamp: u64, - - /// The size of the file in bytes, or 0 if not available. - pub size: u64, - - /// A 16-byte MD5 digest of the file contents. - /// - /// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`. - pub md5: [u8; 16], -} - -define_section!( - DebugLine, - DebugLineOffset, - "A writable `.debug_line` section." -); - -#[cfg(feature = "read")] -mod convert { - use super::*; - use crate::read::{self, Reader}; - use crate::write::{self, ConvertError, ConvertResult}; - - impl LineProgram { - /// Create a line number program by reading the data from the given program. - /// - /// Return the program and a mapping from file index to `FileId`. - pub fn 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>)> { - // Create mappings in case the source has duplicate files or directories. - let mut dirs = Vec::new(); - let mut files = Vec::new(); - - let mut program = { - let from_header = from_program.header(); - let encoding = from_header.encoding(); - - let comp_dir = match from_header.directory(0) { - Some(comp_dir) => LineString::from(comp_dir, dwarf, line_strings, strings)?, - None => LineString::new(&[][..], encoding, line_strings), - }; - - let (comp_name, comp_file_info) = match from_header.file(0) { - Some(comp_file) => { - if comp_file.directory_index() != 0 { - return Err(ConvertError::InvalidDirectoryIndex); - } - ( - LineString::from(comp_file.path_name(), dwarf, line_strings, strings)?, - Some(FileInfo { - timestamp: comp_file.timestamp(), - size: comp_file.size(), - md5: *comp_file.md5(), - }), - ) - } - None => (LineString::new(&[][..], encoding, line_strings), None), - }; - - if from_header.line_base() > 0 { - return Err(ConvertError::InvalidLineBase); - } - let mut program = LineProgram::new( - encoding, - from_header.line_encoding(), - comp_dir, - comp_name, - comp_file_info, - ); - - let file_skip; - if from_header.version() <= 4 { - // The first directory is implicit. - dirs.push(DirectoryId(0)); - // A file index of 0 is invalid for version <= 4, but putting - // something there makes the indexing easier. - file_skip = 0; - files.push(FileId::zero()); - } else { - // We don't add the first file to `files`, but still allow - // it to be referenced from converted instructions. - file_skip = 1; - files.push(FileId::zero()); - } - - for from_dir in from_header.include_directories() { - let from_dir = - LineString::from(from_dir.clone(), dwarf, line_strings, strings)?; - dirs.push(program.add_directory(from_dir)); - } - - program.file_has_timestamp = from_header.file_has_timestamp(); - program.file_has_size = from_header.file_has_size(); - program.file_has_md5 = from_header.file_has_md5(); - for from_file in from_header.file_names().iter().skip(file_skip) { - let from_name = - LineString::from(from_file.path_name(), dwarf, line_strings, strings)?; - let from_dir = from_file.directory_index(); - if from_dir >= dirs.len() as u64 { - return Err(ConvertError::InvalidDirectoryIndex); - } - let from_dir = dirs[from_dir as usize]; - let from_info = Some(FileInfo { - timestamp: from_file.timestamp(), - size: from_file.size(), - md5: *from_file.md5(), - }); - files.push(program.add_file(from_name, from_dir, from_info)); - } - - program - }; - - // We can't use the `from_program.rows()` because that wouldn't let - // us preserve address relocations. - let mut from_row = read::LineRow::new(from_program.header()); - let mut instructions = from_program.header().instructions(); - let mut address = None; - while let Some(instruction) = instructions.next_instruction(from_program.header())? { - match instruction { - read::LineInstruction::SetAddress(val) => { - if program.in_sequence() { - return Err(ConvertError::UnsupportedLineInstruction); - } - match convert_address(val) { - Some(val) => address = Some(val), - None => return Err(ConvertError::InvalidAddress), - } - from_row.execute(read::LineInstruction::SetAddress(0), &mut from_program); - } - read::LineInstruction::DefineFile(_) => { - return Err(ConvertError::UnsupportedLineInstruction); - } - _ => { - if from_row.execute(instruction, &mut from_program) { - if !program.in_sequence() { - program.begin_sequence(address); - address = None; - } - if from_row.end_sequence() { - program.end_sequence(from_row.address()); - } else { - program.row().address_offset = from_row.address(); - program.row().op_index = from_row.op_index(); - program.row().file = { - let file = from_row.file_index(); - if file >= files.len() as u64 { - return Err(ConvertError::InvalidFileIndex); - } - if file == 0 && program.version() <= 4 { - return Err(ConvertError::InvalidFileIndex); - } - files[file as usize] - }; - program.row().line = match from_row.line() { - Some(line) => line.get(), - None => 0, - }; - program.row().column = match from_row.column() { - read::ColumnType::LeftEdge => 0, - read::ColumnType::Column(val) => val.get(), - }; - program.row().discriminator = from_row.discriminator(); - program.row().is_statement = from_row.is_stmt(); - program.row().basic_block = from_row.basic_block(); - program.row().prologue_end = from_row.prologue_end(); - program.row().epilogue_begin = from_row.epilogue_begin(); - program.row().isa = from_row.isa(); - program.generate_row(); - } - from_row.reset(from_program.header()); - } - } - }; - } - Ok((program, files)) - } - } - - impl LineString { - fn 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> { - Ok(match from_attr { - read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()), - read::AttributeValue::DebugStrRef(offset) => { - let r = dwarf.debug_str.get_str(offset)?; - let id = strings.add(r.to_slice()?); - LineString::StringRef(id) - } - read::AttributeValue::DebugLineStrRef(offset) => { - let r = dwarf.debug_line_str.get_str(offset)?; - let id = line_strings.add(r.to_slice()?); - LineString::LineStringRef(id) - } - _ => return Err(ConvertError::UnsupportedLineStringForm), - }) - } - } -} - -#[cfg(test)] -#[cfg(feature = "read")] -mod tests { - use super::*; - use crate::read; - use crate::write::{DebugLineStr, DebugStr, EndianVec, StringTable}; - use crate::LittleEndian; - - #[test] - fn test_line_program_table() { - let dir1 = LineString::String(b"dir1".to_vec()); - let file1 = LineString::String(b"file1".to_vec()); - let dir2 = LineString::String(b"dir2".to_vec()); - let file2 = LineString::String(b"file2".to_vec()); - - let mut programs = Vec::new(); - for &version in &[2, 3, 4, 5] { - for &address_size in &[4, 8] { - for &format in &[Format::Dwarf32, Format::Dwarf64] { - let encoding = Encoding { - format, - version, - address_size, - }; - let mut program = LineProgram::new( - encoding, - LineEncoding::default(), - dir1.clone(), - file1.clone(), - None, - ); - - { - assert_eq!(&dir1, program.get_directory(program.default_directory())); - program.file_has_timestamp = true; - program.file_has_size = true; - if encoding.version >= 5 { - program.file_has_md5 = true; - } - - let dir_id = program.add_directory(dir2.clone()); - assert_eq!(&dir2, program.get_directory(dir_id)); - assert_eq!(dir_id, program.add_directory(dir2.clone())); - - let file_info = FileInfo { - timestamp: 1, - size: 2, - md5: if encoding.version >= 5 { - [3; 16] - } else { - [0; 16] - }, - }; - let file_id = program.add_file(file2.clone(), dir_id, Some(file_info)); - assert_eq!((&file2, dir_id), program.get_file(file_id)); - assert_eq!(file_info, *program.get_file_info(file_id)); - - program.get_file_info_mut(file_id).size = 3; - assert_ne!(file_info, *program.get_file_info(file_id)); - assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None)); - assert_ne!(file_info, *program.get_file_info(file_id)); - assert_eq!( - file_id, - program.add_file(file2.clone(), dir_id, Some(file_info)) - ); - assert_eq!(file_info, *program.get_file_info(file_id)); - - programs.push((program, file_id, encoding)); - } - } - } - } - - let debug_line_str_offsets = DebugLineStrOffsets::none(); - let debug_str_offsets = DebugStrOffsets::none(); - let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); - let mut debug_line_offsets = Vec::new(); - for (program, _, encoding) in &programs { - debug_line_offsets.push( - program - .write( - &mut debug_line, - *encoding, - &debug_line_str_offsets, - &debug_str_offsets, - ) - .unwrap(), - ); - } - - let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); - - let convert_address = &|address| Some(Address::Constant(address)); - for ((program, file_id, encoding), offset) in programs.iter().zip(debug_line_offsets.iter()) - { - let read_program = read_debug_line - .program( - *offset, - encoding.address_size, - Some(read::EndianSlice::new(b"dir1", LittleEndian)), - Some(read::EndianSlice::new(b"file1", LittleEndian)), - ) - .unwrap(); - - let dwarf = read::Dwarf::default(); - let mut convert_line_strings = LineStringTable::default(); - let mut convert_strings = StringTable::default(); - let (convert_program, convert_files) = LineProgram::from( - read_program, - &dwarf, - &mut convert_line_strings, - &mut convert_strings, - convert_address, - ) - .unwrap(); - assert_eq!(convert_program.version(), program.version()); - assert_eq!(convert_program.address_size(), program.address_size()); - assert_eq!(convert_program.format(), program.format()); - - let convert_file_id = convert_files[file_id.raw() as usize]; - let (file, dir) = program.get_file(*file_id); - let (convert_file, convert_dir) = convert_program.get_file(convert_file_id); - assert_eq!(file, convert_file); - assert_eq!( - program.get_directory(dir), - convert_program.get_directory(convert_dir) - ); - assert_eq!( - program.get_file_info(*file_id), - convert_program.get_file_info(convert_file_id) - ); - } - } - - #[test] - fn test_line_row() { - let dir1 = &b"dir1"[..]; - let file1 = &b"file1"[..]; - let file2 = &b"file2"[..]; - let convert_address = &|address| Some(Address::Constant(address)); - - let debug_line_str_offsets = DebugLineStrOffsets::none(); - let debug_str_offsets = DebugStrOffsets::none(); - - for &version in &[2, 3, 4, 5] { - for &address_size in &[4, 8] { - for &format in &[Format::Dwarf32, Format::Dwarf64] { - let encoding = Encoding { - format, - version, - address_size, - }; - let line_base = -5; - let line_range = 14; - let neg_line_base = (-line_base) as u8; - let mut program = LineProgram::new( - encoding, - LineEncoding { - line_base, - line_range, - ..Default::default() - }, - LineString::String(dir1.to_vec()), - LineString::String(file1.to_vec()), - None, - ); - let dir_id = program.default_directory(); - program.add_file(LineString::String(file1.to_vec()), dir_id, None); - let file_id = - program.add_file(LineString::String(file2.to_vec()), dir_id, None); - - // Test sequences. - { - let mut program = program.clone(); - let address = Address::Constant(0x12); - program.begin_sequence(Some(address)); - assert_eq!( - program.instructions, - vec![LineInstruction::SetAddress(address)] - ); - } - - { - let mut program = program.clone(); - program.begin_sequence(None); - assert_eq!(program.instructions, Vec::new()); - } - - { - let mut program = program.clone(); - program.begin_sequence(None); - program.end_sequence(0x1234); - assert_eq!( - program.instructions, - vec![ - LineInstruction::AdvancePc(0x1234), - LineInstruction::EndSequence - ] - ); - } - - // Create a base program. - program.begin_sequence(None); - program.row.line = 0x1000; - program.generate_row(); - let base_row = program.row; - let base_instructions = program.instructions.clone(); - - // Create test cases. - let mut tests = Vec::new(); - - let row = base_row; - tests.push((row, vec![LineInstruction::Copy])); - - let mut row = base_row; - row.line -= u64::from(neg_line_base); - tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)])); - - let mut row = base_row; - row.line += u64::from(line_range) - 1; - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)], - )); - - let mut row = base_row; - row.line += u64::from(line_range); - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![ - LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)), - LineInstruction::Copy, - ], - )); - - let mut row = base_row; - row.address_offset = 1; - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![LineInstruction::Special(OPCODE_BASE + line_range)], - )); - - let op_range = (255 - OPCODE_BASE) / line_range; - let mut row = base_row; - row.address_offset = u64::from(op_range); - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![LineInstruction::Special( - OPCODE_BASE + op_range * line_range, - )], - )); - - let mut row = base_row; - row.address_offset = u64::from(op_range); - row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); - row.line -= u64::from(neg_line_base); - tests.push((row, vec![LineInstruction::Special(255)])); - - let mut row = base_row; - row.address_offset = u64::from(op_range); - row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![LineInstruction::ConstAddPc, LineInstruction::Copy], - )); - - let mut row = base_row; - row.address_offset = u64::from(op_range); - row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![ - LineInstruction::ConstAddPc, - LineInstruction::Special(OPCODE_BASE + 6), - ], - )); - - let mut row = base_row; - row.address_offset = u64::from(op_range) * 2; - row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)], - )); - - let mut row = base_row; - row.address_offset = u64::from(op_range) * 2; - row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![ - LineInstruction::AdvancePc(row.address_offset), - LineInstruction::Copy, - ], - )); - - let mut row = base_row; - row.address_offset = u64::from(op_range) * 2; - row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; - row.line -= u64::from(neg_line_base); - tests.push(( - row, - vec![ - LineInstruction::AdvancePc(row.address_offset), - LineInstruction::Special(OPCODE_BASE + 6), - ], - )); - - let mut row = base_row; - row.address_offset = 0x1234; - tests.push(( - row, - vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy], - )); - - let mut row = base_row; - row.line += 0x1234; - tests.push(( - row, - vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy], - )); - - let mut row = base_row; - row.file = file_id; - tests.push(( - row, - vec![LineInstruction::SetFile(file_id), LineInstruction::Copy], - )); - - let mut row = base_row; - row.column = 0x1234; - tests.push(( - row, - vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy], - )); - - let mut row = base_row; - row.discriminator = 0x1234; - tests.push(( - row, - vec![ - LineInstruction::SetDiscriminator(0x1234), - LineInstruction::Copy, - ], - )); - - let mut row = base_row; - row.is_statement = !row.is_statement; - tests.push(( - row, - vec![LineInstruction::NegateStatement, LineInstruction::Copy], - )); - - let mut row = base_row; - row.basic_block = true; - tests.push(( - row, - vec![LineInstruction::SetBasicBlock, LineInstruction::Copy], - )); - - let mut row = base_row; - row.prologue_end = true; - tests.push(( - row, - vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy], - )); - - let mut row = base_row; - row.epilogue_begin = true; - tests.push(( - row, - vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy], - )); - - let mut row = base_row; - row.isa = 0x1234; - tests.push(( - row, - vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy], - )); - - for test in tests { - // Test generate_row(). - let mut program = program.clone(); - program.row = test.0; - program.generate_row(); - assert_eq!( - &program.instructions[base_instructions.len()..], - &test.1[..] - ); - - // Test LineProgram::from(). - let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); - let debug_line_offset = program - .write( - &mut debug_line, - encoding, - &debug_line_str_offsets, - &debug_str_offsets, - ) - .unwrap(); - - let read_debug_line = - read::DebugLine::new(debug_line.slice(), LittleEndian); - let read_program = read_debug_line - .program( - debug_line_offset, - address_size, - Some(read::EndianSlice::new(dir1, LittleEndian)), - Some(read::EndianSlice::new(file1, LittleEndian)), - ) - .unwrap(); - - let dwarf = read::Dwarf::default(); - let mut convert_line_strings = LineStringTable::default(); - let mut convert_strings = StringTable::default(); - let (convert_program, _convert_files) = LineProgram::from( - read_program, - &dwarf, - &mut convert_line_strings, - &mut convert_strings, - convert_address, - ) - .unwrap(); - assert_eq!( - &convert_program.instructions[base_instructions.len()..], - &test.1[..] - ); - } - } - } - } - } - - #[test] - fn test_line_instruction() { - let dir1 = &b"dir1"[..]; - let file1 = &b"file1"[..]; - - let debug_line_str_offsets = DebugLineStrOffsets::none(); - let debug_str_offsets = DebugStrOffsets::none(); - - for &version in &[2, 3, 4, 5] { - for &address_size in &[4, 8] { - for &format in &[Format::Dwarf32, Format::Dwarf64] { - let encoding = Encoding { - format, - version, - address_size, - }; - let mut program = LineProgram::new( - encoding, - LineEncoding::default(), - LineString::String(dir1.to_vec()), - LineString::String(file1.to_vec()), - None, - ); - let dir_id = program.default_directory(); - let file_id = - program.add_file(LineString::String(file1.to_vec()), dir_id, None); - - for &(ref inst, ref expect_inst) in &[ - ( - LineInstruction::Special(OPCODE_BASE), - read::LineInstruction::Special(OPCODE_BASE), - ), - ( - LineInstruction::Special(255), - read::LineInstruction::Special(255), - ), - (LineInstruction::Copy, read::LineInstruction::Copy), - ( - LineInstruction::AdvancePc(0x12), - read::LineInstruction::AdvancePc(0x12), - ), - ( - LineInstruction::AdvanceLine(0x12), - read::LineInstruction::AdvanceLine(0x12), - ), - ( - LineInstruction::SetFile(file_id), - read::LineInstruction::SetFile(file_id.raw()), - ), - ( - LineInstruction::SetColumn(0x12), - read::LineInstruction::SetColumn(0x12), - ), - ( - LineInstruction::NegateStatement, - read::LineInstruction::NegateStatement, - ), - ( - LineInstruction::SetBasicBlock, - read::LineInstruction::SetBasicBlock, - ), - ( - LineInstruction::ConstAddPc, - read::LineInstruction::ConstAddPc, - ), - ( - LineInstruction::SetPrologueEnd, - read::LineInstruction::SetPrologueEnd, - ), - ( - LineInstruction::SetEpilogueBegin, - read::LineInstruction::SetEpilogueBegin, - ), - ( - LineInstruction::SetIsa(0x12), - read::LineInstruction::SetIsa(0x12), - ), - ( - LineInstruction::EndSequence, - read::LineInstruction::EndSequence, - ), - ( - LineInstruction::SetAddress(Address::Constant(0x12)), - read::LineInstruction::SetAddress(0x12), - ), - ( - LineInstruction::SetDiscriminator(0x12), - read::LineInstruction::SetDiscriminator(0x12), - ), - ][..] - { - let mut program = program.clone(); - program.instructions.push(*inst); - - let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); - let debug_line_offset = program - .write( - &mut debug_line, - encoding, - &debug_line_str_offsets, - &debug_str_offsets, - ) - .unwrap(); - - let read_debug_line = - read::DebugLine::new(debug_line.slice(), LittleEndian); - let read_program = read_debug_line - .program( - debug_line_offset, - address_size, - Some(read::EndianSlice::new(dir1, LittleEndian)), - Some(read::EndianSlice::new(file1, LittleEndian)), - ) - .unwrap(); - let read_header = read_program.header(); - let mut read_insts = read_header.instructions(); - assert_eq!( - *expect_inst, - read_insts.next_instruction(read_header).unwrap().unwrap() - ); - assert_eq!(None, read_insts.next_instruction(read_header).unwrap()); - } - } - } - } - } - - // Test that the address/line advance is correct. We don't test for optimality. - #[test] - fn test_advance() { - let encoding = Encoding { - format: Format::Dwarf32, - version: 4, - address_size: 8, - }; - - let dir1 = &b"dir1"[..]; - let file1 = &b"file1"[..]; - - let addresses = 0..50; - let lines = -10..25i64; - - let debug_line_str_offsets = DebugLineStrOffsets::none(); - let debug_str_offsets = DebugStrOffsets::none(); - - for minimum_instruction_length in vec![1, 4] { - for maximum_operations_per_instruction in vec![1, 3] { - for line_base in vec![-5, 0] { - for line_range in vec![10, 20] { - let line_encoding = LineEncoding { - minimum_instruction_length, - maximum_operations_per_instruction, - line_base, - line_range, - default_is_stmt: true, - }; - let mut program = LineProgram::new( - encoding, - line_encoding, - LineString::String(dir1.to_vec()), - LineString::String(file1.to_vec()), - None, - ); - for address_advance in addresses.clone() { - program.begin_sequence(Some(Address::Constant(0x1000))); - program.row().line = 0x10000; - program.generate_row(); - for line_advance in lines.clone() { - { - let row = program.row(); - row.address_offset += - address_advance * u64::from(minimum_instruction_length); - row.line = row.line.wrapping_add(line_advance as u64); - } - program.generate_row(); - } - let address_offset = program.row().address_offset - + u64::from(minimum_instruction_length); - program.end_sequence(address_offset); - } - - let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); - let debug_line_offset = program - .write( - &mut debug_line, - encoding, - &debug_line_str_offsets, - &debug_str_offsets, - ) - .unwrap(); - - let read_debug_line = - read::DebugLine::new(debug_line.slice(), LittleEndian); - let read_program = read_debug_line - .program( - debug_line_offset, - 8, - Some(read::EndianSlice::new(dir1, LittleEndian)), - Some(read::EndianSlice::new(file1, LittleEndian)), - ) - .unwrap(); - - let mut rows = read_program.rows(); - for address_advance in addresses.clone() { - let mut address; - let mut line; - { - let row = rows.next_row().unwrap().unwrap().1; - address = row.address(); - line = row.line().unwrap().get(); - } - assert_eq!(address, 0x1000); - assert_eq!(line, 0x10000); - for line_advance in lines.clone() { - let row = rows.next_row().unwrap().unwrap().1; - assert_eq!( - row.address() - address, - address_advance * u64::from(minimum_instruction_length) - ); - assert_eq!( - (row.line().unwrap().get() as i64) - (line as i64), - line_advance - ); - address = row.address(); - line = row.line().unwrap().get(); - } - let row = rows.next_row().unwrap().unwrap().1; - assert!(row.end_sequence()); - } - } - } - } - } - } - - #[test] - fn test_line_string() { - let version = 5; - - let file = b"file1"; - - let mut strings = StringTable::default(); - let string_id = strings.add("file2"); - let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); - let debug_str_offsets = strings.write(&mut debug_str).unwrap(); - - let mut line_strings = LineStringTable::default(); - let line_string_id = line_strings.add("file3"); - let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian)); - let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap(); - - for &address_size in &[4, 8] { - for &format in &[Format::Dwarf32, Format::Dwarf64] { - let encoding = Encoding { - format, - version, - address_size, - }; - - for (file, expect_file) in vec![ - ( - LineString::String(file.to_vec()), - read::AttributeValue::String(read::EndianSlice::new(file, LittleEndian)), - ), - ( - LineString::StringRef(string_id), - read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)), - ), - ( - LineString::LineStringRef(line_string_id), - read::AttributeValue::DebugLineStrRef( - debug_line_str_offsets.get(line_string_id), - ), - ), - ] { - let program = LineProgram::new( - encoding, - LineEncoding::default(), - LineString::String(b"dir".to_vec()), - file, - None, - ); - - let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); - let debug_line_offset = program - .write( - &mut debug_line, - encoding, - &debug_line_str_offsets, - &debug_str_offsets, - ) - .unwrap(); - - let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); - let read_program = read_debug_line - .program(debug_line_offset, address_size, None, None) - .unwrap(); - let read_header = read_program.header(); - assert_eq!(read_header.file(0).unwrap().path_name(), expect_file); - } - } - } - } - - #[test] - fn test_missing_comp_dir() { - let debug_line_str_offsets = DebugLineStrOffsets::none(); - let debug_str_offsets = DebugStrOffsets::none(); - - for &version in &[2, 3, 4, 5] { - for &address_size in &[4, 8] { - for &format in &[Format::Dwarf32, Format::Dwarf64] { - let encoding = Encoding { - format, - version, - address_size, - }; - let program = LineProgram::new( - encoding, - LineEncoding::default(), - LineString::String(Vec::new()), - LineString::String(Vec::new()), - None, - ); - - let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); - let debug_line_offset = program - .write( - &mut debug_line, - encoding, - &debug_line_str_offsets, - &debug_str_offsets, - ) - .unwrap(); - - let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); - let read_program = read_debug_line - .program( - debug_line_offset, - address_size, - // Testing missing comp_dir/comp_name. - None, - None, - ) - .unwrap(); - - let dwarf = read::Dwarf::default(); - let mut convert_line_strings = LineStringTable::default(); - let mut convert_strings = StringTable::default(); - let convert_address = &|address| Some(Address::Constant(address)); - LineProgram::from( - read_program, - &dwarf, - &mut convert_line_strings, - &mut convert_strings, - convert_address, - ) - .unwrap(); - } - } - } - } -} |