aboutsummaryrefslogtreecommitdiff
path: root/vendor/gimli/src/write/line.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gimli/src/write/line.rs')
-rw-r--r--vendor/gimli/src/write/line.rs1957
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();
- }
- }
- }
- }
-}