From 950780ec971210960955ef2e4419375e90093a5c Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Mon, 19 Nov 2018 15:22:08 +1000 Subject: [PATCH] Add read and write features --- .travis.yml | 22 +- Cargo.toml | 6 +- ci/script.sh | 15 +- src/leb128.rs | 3 +- src/lib.rs | 12 +- src/write/mod.rs | 104 +++++----- src/write/unit.rs | 501 ++++++++++++++++++++++++---------------------- 7 files changed, 352 insertions(+), 311 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1535c3f88..978c93d83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,9 @@ sudo: false language: rust -cache: cargo +cache: + directories: + - /home/travis/.cargo os: - linux @@ -17,11 +19,7 @@ rust: addons: apt: packages: - - libcurl4-openssl-dev - - libelf-dev - - libdw-dev - - binutils-dev - - libiberty-dev # same + - libssl-dev before_script: - if [[ -e ~/.local/bin ]]; then export PATH=~/.local/bin:$PATH; fi @@ -33,9 +31,8 @@ script: ./ci/script.sh env: matrix: - GIMLI_JOB="test" GIMLI_PROFILE= - - GIMLI_JOB="test" GIMLI_PROFILE="--release" - - GIMLI_JOB="examples" GIMLI_PROFILE= - - GIMLI_JOB="examples" GIMLI_PROFILE="--release" + - GIMLI_JOB="build" GIMLI_PROFILE="--no-default-features --features read,std" + - GIMLI_JOB="build" GIMLI_PROFILE="--no-default-features --features write" matrix: fast_finish: true @@ -49,14 +46,12 @@ matrix: - rust: stable os: linux env: GIMLI_JOB="doc" GIMLI_PROFILE= - # Benching should only happen on nightly with --release. + # Benching should only happen on nightly. - rust: nightly - env: GIMLI_JOB="bench" GIMLI_PROFILE="--release" + env: GIMLI_JOB="bench" GIMLI_PROFILE= # The no-std/alloc build should only happen on nightly. - rust: nightly env: GIMLI_JOB="alloc" GIMLI_PROFILE= - - rust: nightly - env: GIMLI_JOB="alloc" GIMLI_PROFILE="--release" # Build a 32 bit target. - rust: stable sudo: required @@ -75,4 +70,3 @@ matrix: - GIMLI_PROFILE= allow_failures: - env: GIMLI_JOB="alloc" GIMLI_PROFILE= - - env: GIMLI_JOB="alloc" GIMLI_PROFILE="--release" diff --git a/Cargo.toml b/Cargo.toml index 0db5fde30..58b73a5ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,8 @@ test-assembler = "0.1.3" typed-arena = "1" [features] -std = ["fallible-iterator/std", "stable_deref_trait/std", "indexmap"] +read = [] +write = ["std", "indexmap"] +std = ["fallible-iterator/std", "stable_deref_trait/std"] alloc = ["fallible-iterator/alloc", "stable_deref_trait/alloc"] -default = ["std"] +default = ["read", "write", "std"] diff --git a/ci/script.sh b/ci/script.sh index a0d221400..dadfae126 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -3,13 +3,16 @@ set -ex case "$GIMLI_JOB" in - "test") + "build") cargo build $GIMLI_PROFILE - cargo test $GIMLI_PROFILE + cargo build --release $GIMLI_PROFILE ;; - "examples") + "test") cargo build $GIMLI_PROFILE + cargo test $GIMLI_PROFILE + cargo build --release $GIMLI_PROFILE + cargo test --release $GIMLI_PROFILE case "$TRAVIS_OS_NAME" in "osx") with_debug_info=$(find ./target/debug -type f | grep DWARF | grep gimli | head -n 1) @@ -21,7 +24,8 @@ case "$GIMLI_JOB" in echo "Error! Unknown \$TRAVIS_OS_NAME: $TRAVIS_OS_NAME" exit 1 esac - cargo run --example dwarfdump -- "$with_debug_info" > /dev/null + cargo run --example dwarfdump -- "$with_debug_info" > /dev/null + cargo run --release --example dwarfdump -- "$with_debug_info" > /dev/null ;; "doc") @@ -30,7 +34,8 @@ case "$GIMLI_JOB" in "alloc") test "$TRAVIS_RUST_VERSION" == "nightly" - cargo build --no-default-features --features alloc $GIMLI_PROFILE + cargo build --no-default-features --features read,alloc $GIMLI_PROFILE + cargo build --release --no-default-features --features read,alloc $GIMLI_PROFILE ;; "bench") diff --git a/src/leb128.rs b/src/leb128.rs index 094297b30..88b4d67ae 100644 --- a/src/leb128.rs +++ b/src/leb128.rs @@ -59,6 +59,7 @@ fn low_bits_of_u64(val: u64) -> u8 { /// A module for reading signed and unsigned integers that have been LEB128 /// encoded. +#[cfg(feature = "read")] pub mod read { use super::{low_bits_of_byte, CONTINUATION_BIT, SIGN_BIT}; use read::{Error, Reader, Result}; @@ -119,7 +120,7 @@ pub mod read { } /// A module for writing integers encoded as LEB128. -#[cfg(feature = "std")] +#[cfg(feature = "write")] pub mod write { use super::{low_bits_of_u64, CONTINUATION_BIT}; use std::io; diff --git a/src/lib.rs b/src/lib.rs index 983d5a7c3..baf400b6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -167,6 +167,12 @@ //! * `alloc`: Nightly only. Enables usage of the unstable, nightly-only //! `#![feature(alloc)]` Rust feature that allows `gimli` to use boxes and //! collection types in a `#[no_std]` environment. +//! +//! * `read`: Enabled by default. Enables the `read` module. Requires +//! either `alloc` or `std` to also be enabled. +//! +//! * `write`: Enabled by default. Enables the `write` module. Automatically +//! enables `std` too. #![deny(missing_docs)] #![deny(missing_debug_implementations)] // Allow clippy warnings when we aren't building with clippy. @@ -194,7 +200,7 @@ extern crate core as std; extern crate arrayvec; extern crate byteorder; extern crate fallible_iterator; -#[cfg(feature = "std")] +#[cfg(feature = "write")] extern crate indexmap; extern crate stable_deref_trait; @@ -239,11 +245,13 @@ pub use endianity::{BigEndian, Endianity, LittleEndian, NativeEndian, RunTimeEnd pub mod leb128; +#[cfg(feature = "read")] pub mod read; // For backwards compat. +#[cfg(feature = "read")] pub use read::*; -#[cfg(feature = "std")] +#[cfg(feature = "write")] pub mod write; #[cfg(test)] diff --git a/src/write/mod.rs b/src/write/mod.rs index d2126b40a..04443a3ea 100644 --- a/src/write/mod.rs +++ b/src/write/mod.rs @@ -5,8 +5,6 @@ use std::fmt; use std::ops::DerefMut; use std::result; -use read; - mod endian_vec; pub use self::endian_vec::*; @@ -70,53 +68,6 @@ impl error::Error for Error {} /// The result of a write. pub type Result = result::Result; -/// An error that occurred when converting a read value into a write value. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ConvertError { - /// An error occurred when reading. - Read(read::Error), - /// Writing of this attribute value is not implemented yet. - UnsupportedAttributeValue, - /// This attribute value is an invalid name/form combination. - InvalidAttributeValue, - /// A `.debug_info` reference does not refer to a valid entry. - InvalidDebugInfoOffset, - /// An address could not be converted. - InvalidAddress, -} - -impl fmt::Display for ConvertError { - fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { - use self::ConvertError::*; - match *self { - Read(ref e) => e.fmt(f), - UnsupportedAttributeValue => { - write!(f, "Writing of this attribute value is not implemented yet.") - } - InvalidAttributeValue => write!( - f, - "This attribute value is an invalid name/form combination." - ), - InvalidDebugInfoOffset => write!( - f, - "A `.debug_info` reference does not refer to a valid entry." - ), - InvalidAddress => write!(f, "An address could not be converted."), - } - } -} - -impl error::Error for ConvertError {} - -impl From for ConvertError { - fn from(e: read::Error) -> Self { - ConvertError::Read(e) - } -} - -/// The result of a conversion. -pub type ConvertResult = result::Result; - /// An identifier for a DWARF section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SectionId { @@ -186,3 +137,58 @@ pub enum Address { addend: i64, }, } + +#[cfg(feature = "read")] +mod convert { + use super::*; + use read; + + /// An error that occurred when converting a read value into a write value. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum ConvertError { + /// An error occurred when reading. + Read(read::Error), + /// Writing of this attribute value is not implemented yet. + UnsupportedAttributeValue, + /// This attribute value is an invalid name/form combination. + InvalidAttributeValue, + /// A `.debug_info` reference does not refer to a valid entry. + InvalidDebugInfoOffset, + /// An address could not be converted. + InvalidAddress, + } + + impl fmt::Display for ConvertError { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + use self::ConvertError::*; + match *self { + Read(ref e) => e.fmt(f), + UnsupportedAttributeValue => { + write!(f, "Writing of this attribute value is not implemented yet.") + } + InvalidAttributeValue => write!( + f, + "This attribute value is an invalid name/form combination." + ), + InvalidDebugInfoOffset => write!( + f, + "A `.debug_info` reference does not refer to a valid entry." + ), + InvalidAddress => write!(f, "An address could not be converted."), + } + } + } + + impl error::Error for ConvertError {} + + impl From for ConvertError { + fn from(e: read::Error) -> Self { + ConvertError::Read(e) + } + } + + /// The result of a conversion. + pub type ConvertResult = result::Result; +} +#[cfg(feature = "read")] +pub use self::convert::*; diff --git a/src/write/unit.rs b/src/write/unit.rs index de4ec68c6..b271f58f3 100644 --- a/src/write/unit.rs +++ b/src/write/unit.rs @@ -1,4 +1,3 @@ -use collections::HashMap; use std::ops::{Deref, DerefMut}; use std::{slice, usize}; use vec::Vec; @@ -8,11 +7,9 @@ use common::{ DebugTypeSignature, Format, LocationListsOffset, RangeListsOffset, }; use constants; -use read::{self, Reader}; use write::{ - self, Abbreviation, AbbreviationTable, Address, AttributeSpecification, ConvertError, - ConvertResult, DebugAbbrev, DebugStrOffsets, Error, Result, Section, SectionId, StringId, - Writer, + Abbreviation, AbbreviationTable, Address, AttributeSpecification, DebugAbbrev, DebugStrOffsets, + Error, Result, Section, SectionId, StringId, Writer, }; /// An identifier for a unit in a `UnitTable`. @@ -107,66 +104,6 @@ impl UnitTable { Ok(offsets) } - - /// Create a compilation unit table by reading the data in the given sections. - /// - /// This also updates the given string table with the strings that are read - /// from the `debug_str` section. - /// - /// `convert_address` is a function to convert read addresses into the `Address` - /// type. For non-relocatable addresses, this function may simply return - /// `Address::Absolute(address)`. For relocatable addresses, it is the caller's - /// responsibility to determine the symbol and addend corresponding to the address - /// and return `Address::Relative { symbol, addend }`. - pub fn from>( - debug_info: &read::DebugInfo, - debug_abbrev: &read::DebugAbbrev, - debug_str: &read::DebugStr, - strings: &mut write::StringTable, - convert_address: &Fn(u64) -> Option
, - ) -> ConvertResult { - let mut units = Vec::new(); - let mut unit_entry_offsets = HashMap::new(); - - let mut from_units = debug_info.units(); - while let Some(ref from_unit) = from_units.next()? { - CompilationUnit::from( - from_unit, - &mut units, - debug_abbrev, - debug_str, - strings, - convert_address, - &mut unit_entry_offsets, - )?; - } - - // Convert all DebugInfoOffset to UnitEntryId - for unit in &mut units { - for entry in &mut unit.entries { - for attr in &mut entry.attrs { - let id = match attr.value { - AttributeValue::DebugInfoRef(ref offset) => { - match unit_entry_offsets.get(offset) { - Some(id) => Some(*id), - None => return Err(ConvertError::InvalidDebugInfoOffset), - } - } - _ => None, - }; - if let Some((unit_id, entry_id)) = id { - if unit_id == unit.id { - attr.value = AttributeValue::ThisUnitEntryRef(entry_id) - } else { - attr.value = AttributeValue::AnyUnitEntryRef((unit_id, entry_id)) - } - } - } - } - } - - Ok(UnitTable { units }) - } } /// A compilation unit's debugging information. @@ -342,45 +279,6 @@ impl CompilationUnit { Ok(offsets) } - - /// Create an entry by reading the data in the given sections. - fn from>( - from_unit: &read::CompilationUnitHeader, - units: &mut Vec, - debug_abbrev: &read::DebugAbbrev, - debug_str: &read::DebugStr, - strings: &mut write::StringTable, - convert_address: &Fn(u64) -> Option
, - unit_entry_offsets: &mut HashMap, - ) -> ConvertResult { - let id = UnitId(units.len()); - let version = from_unit.version(); - let address_size = from_unit.address_size(); - let format = from_unit.format(); - let mut entries = Vec::new(); - let abbreviations = from_unit.abbreviations(debug_abbrev)?; - let mut from_tree = from_unit.entries_tree(&abbreviations, None)?; - let root = DebuggingInformationEntry::from( - from_tree.root()?, - from_unit, - &mut entries, - None, - id, - debug_str, - strings, - convert_address, - unit_entry_offsets, - )?; - units.push(CompilationUnit { - id, - version, - address_size, - format, - entries, - root, - }); - Ok(id) - } } /// A Debugging Information Entry (DIE). @@ -565,58 +463,6 @@ impl DebuggingInformationEntry { } Ok(()) } - - /// Create an entry by reading the data in the given sections. - fn from>( - from: read::EntriesTreeNode, - from_unit: &read::CompilationUnitHeader, - entries: &mut Vec, - parent: Option, - unit_id: UnitId, - debug_str: &read::DebugStr, - strings: &mut write::StringTable, - convert_address: &Fn(u64) -> Option
, - unit_entry_offsets: &mut HashMap, - ) -> ConvertResult { - let id = { - let from = from.entry(); - let entry = DebuggingInformationEntry::new(entries, parent, from.tag()); - let entry = &mut entries[entry.0]; - - let offset = from.offset().to_debug_info_offset(from_unit); - unit_entry_offsets.insert(offset, (unit_id, entry.id)); - - let mut from_attrs = from.attrs(); - while let Some(from_attr) = from_attrs.next()? { - if from_attr.name() == constants::DW_AT_sibling { - // This may point to a null entry, so we have to treat it differently. - entry.set_sibling(true); - } else { - let attr = - Attribute::from(from_attr, from_unit, debug_str, strings, convert_address)?; - entry.set(attr.name, attr.value); - } - } - - entry.id - }; - - let mut from_children = from.children(); - while let Some(from_child) = from_children.next()? { - DebuggingInformationEntry::from( - from_child, - from_unit, - entries, - Some(id), - unit_id, - debug_str, - strings, - convert_address, - unit_entry_offsets, - )?; - } - Ok(id) - } } /// An attribute in a `DebuggingInformationEntry`, consisting of a name and @@ -667,22 +513,6 @@ impl Attribute { self.value .write(w, unit, strings, unit_refs, debug_info_refs) } - - /// Create an attribute by reading the data in the given sections. - fn from>( - from: read::Attribute, - from_unit: &read::CompilationUnitHeader, - debug_str: &read::DebugStr, - strings: &mut write::StringTable, - convert_address: &Fn(u64) -> Option
, - ) -> ConvertResult { - let value = - AttributeValue::from(from.value(), from_unit, debug_str, strings, convert_address)?; - Ok(Attribute { - name: from.name(), - value, - }) - } } /// The value of an attribute in a `DebuggingInformationEntry`. @@ -1106,72 +936,6 @@ impl AttributeValue { } Ok(()) } - - /// Create an attribute value by reading the data in the given sections. - fn from>( - from: read::AttributeValue, - from_unit: &read::CompilationUnitHeader, - debug_str: &read::DebugStr, - strings: &mut write::StringTable, - convert_address: &Fn(u64) -> Option
, - ) -> ConvertResult { - let to = match from { - read::AttributeValue::Addr(val) => match convert_address(val) { - Some(val) => AttributeValue::Address(val), - None => return Err(ConvertError::InvalidAddress), - }, - read::AttributeValue::Block(r) => AttributeValue::Block(r.to_slice()?.into()), - read::AttributeValue::Data1(val) => AttributeValue::Data1(val), - // TODO: do we need to handle endian? - read::AttributeValue::Data2((val, _endian)) => AttributeValue::Data2(val), - read::AttributeValue::Data4((val, _endian)) => AttributeValue::Data4(val), - read::AttributeValue::Data8((val, _endian)) => AttributeValue::Data8(val), - read::AttributeValue::Sdata(val) => AttributeValue::Sdata(val), - read::AttributeValue::Udata(val) => AttributeValue::Udata(val), - // TODO: addresses and offsets in expressions need special handling. - read::AttributeValue::Exprloc(read::Expression(val)) => { - AttributeValue::Exprloc(Expression(val.to_slice()?.into())) - } - // TODO: it would be nice to preserve the flag form. - read::AttributeValue::Flag(val) => AttributeValue::Flag(val), - read::AttributeValue::UnitRef(val) => { - AttributeValue::DebugInfoRef(val.to_debug_info_offset(from_unit)) - } - read::AttributeValue::DebugInfoRef(val) => AttributeValue::DebugInfoRef(val), - read::AttributeValue::DebugInfoRefSup(val) => AttributeValue::DebugInfoRefSup(val), - read::AttributeValue::DebugLineRef(val) => AttributeValue::DebugLineRef(val), - read::AttributeValue::DebugMacinfoRef(val) => AttributeValue::DebugMacinfoRef(val), - read::AttributeValue::LocationListsRef(val) => AttributeValue::LocationListsRef(val), - read::AttributeValue::RangeListsRef(val) => AttributeValue::RangeListsRef(val), - read::AttributeValue::DebugTypesRef(val) => AttributeValue::DebugTypesRef(val), - read::AttributeValue::DebugStrRef(offset) => { - let r = debug_str.get_str(offset)?; - let id = strings.add(r.to_slice()?); - AttributeValue::StringRef(id) - } - read::AttributeValue::DebugStrRefSup(val) => AttributeValue::DebugStrRefSup(val), - read::AttributeValue::String(r) => AttributeValue::String(r.to_slice()?.into()), - read::AttributeValue::Encoding(val) => AttributeValue::Encoding(val), - read::AttributeValue::DecimalSign(val) => AttributeValue::DecimalSign(val), - read::AttributeValue::Endianity(val) => AttributeValue::Endianity(val), - read::AttributeValue::Accessibility(val) => AttributeValue::Accessibility(val), - read::AttributeValue::Visibility(val) => AttributeValue::Visibility(val), - read::AttributeValue::Virtuality(val) => AttributeValue::Virtuality(val), - read::AttributeValue::Language(val) => AttributeValue::Language(val), - read::AttributeValue::AddressClass(val) => AttributeValue::AddressClass(val), - read::AttributeValue::IdentifierCase(val) => AttributeValue::IdentifierCase(val), - read::AttributeValue::CallingConvention(val) => AttributeValue::CallingConvention(val), - read::AttributeValue::Inline(val) => AttributeValue::Inline(val), - read::AttributeValue::Ordering(val) => AttributeValue::Ordering(val), - // TODO: validation - read::AttributeValue::FileIndex(val) => AttributeValue::FileIndex(val as usize), - // Should always be a more specific section reference. - read::AttributeValue::SecOffset(_) => { - return Err(ConvertError::InvalidAttributeValue); - } - }; - Ok(to) - } } /// A writable `.debug_info` section. @@ -1249,10 +1013,271 @@ impl UnitOffsets { } } +#[cfg(feature = "read")] +mod convert { + use super::*; + use collections::HashMap; + use read::{self, Reader}; + use write::{self, ConvertError, ConvertResult}; + + impl UnitTable { + /// Create a compilation unit table by reading the data in the given sections. + /// + /// This also updates the given string table with the strings that are read + /// from the `debug_str` section. + /// + /// `convert_address` is a function to convert read addresses into the `Address` + /// type. For non-relocatable addresses, this function may simply return + /// `Address::Absolute(address)`. For relocatable addresses, it is the caller's + /// responsibility to determine the symbol and addend corresponding to the address + /// and return `Address::Relative { symbol, addend }`. + pub fn from>( + debug_info: &read::DebugInfo, + debug_abbrev: &read::DebugAbbrev, + debug_str: &read::DebugStr, + strings: &mut write::StringTable, + convert_address: &Fn(u64) -> Option
, + ) -> ConvertResult { + let mut units = Vec::new(); + let mut unit_entry_offsets = HashMap::new(); + + let mut from_units = debug_info.units(); + while let Some(ref from_unit) = from_units.next()? { + CompilationUnit::from( + from_unit, + &mut units, + debug_abbrev, + debug_str, + strings, + convert_address, + &mut unit_entry_offsets, + )?; + } + + // Convert all DebugInfoOffset to UnitEntryId + for unit in &mut units { + for entry in &mut unit.entries { + for attr in &mut entry.attrs { + let id = match attr.value { + AttributeValue::DebugInfoRef(ref offset) => { + match unit_entry_offsets.get(offset) { + Some(id) => Some(*id), + None => return Err(ConvertError::InvalidDebugInfoOffset), + } + } + _ => None, + }; + if let Some((unit_id, entry_id)) = id { + if unit_id == unit.id { + attr.value = AttributeValue::ThisUnitEntryRef(entry_id) + } else { + attr.value = AttributeValue::AnyUnitEntryRef((unit_id, entry_id)) + } + } + } + } + } + + Ok(UnitTable { units }) + } + } + + impl CompilationUnit { + /// Create an entry by reading the data in the given sections. + pub(crate) fn from>( + from_unit: &read::CompilationUnitHeader, + units: &mut Vec, + debug_abbrev: &read::DebugAbbrev, + debug_str: &read::DebugStr, + strings: &mut write::StringTable, + convert_address: &Fn(u64) -> Option
, + unit_entry_offsets: &mut HashMap, + ) -> ConvertResult { + let id = UnitId(units.len()); + let version = from_unit.version(); + let address_size = from_unit.address_size(); + let format = from_unit.format(); + let mut entries = Vec::new(); + let abbreviations = from_unit.abbreviations(debug_abbrev)?; + let mut from_tree = from_unit.entries_tree(&abbreviations, None)?; + let root = DebuggingInformationEntry::from( + from_tree.root()?, + from_unit, + &mut entries, + None, + id, + debug_str, + strings, + convert_address, + unit_entry_offsets, + )?; + units.push(CompilationUnit { + id, + version, + address_size, + format, + entries, + root, + }); + Ok(id) + } + } + + impl DebuggingInformationEntry { + /// Create an entry by reading the data in the given sections. + pub(crate) fn from>( + from: read::EntriesTreeNode, + from_unit: &read::CompilationUnitHeader, + entries: &mut Vec, + parent: Option, + unit_id: UnitId, + debug_str: &read::DebugStr, + strings: &mut write::StringTable, + convert_address: &Fn(u64) -> Option
, + unit_entry_offsets: &mut HashMap, + ) -> ConvertResult { + let id = { + let from = from.entry(); + let entry = DebuggingInformationEntry::new(entries, parent, from.tag()); + let entry = &mut entries[entry.0]; + + let offset = from.offset().to_debug_info_offset(from_unit); + unit_entry_offsets.insert(offset, (unit_id, entry.id)); + + let mut from_attrs = from.attrs(); + while let Some(from_attr) = from_attrs.next()? { + if from_attr.name() == constants::DW_AT_sibling { + // This may point to a null entry, so we have to treat it differently. + entry.set_sibling(true); + } else { + let attr = Attribute::from( + from_attr, + from_unit, + debug_str, + strings, + convert_address, + )?; + entry.set(attr.name, attr.value); + } + } + + entry.id + }; + + let mut from_children = from.children(); + while let Some(from_child) = from_children.next()? { + DebuggingInformationEntry::from( + from_child, + from_unit, + entries, + Some(id), + unit_id, + debug_str, + strings, + convert_address, + unit_entry_offsets, + )?; + } + Ok(id) + } + } + + impl Attribute { + /// Create an attribute by reading the data in the given sections. + pub(crate) fn from>( + from: read::Attribute, + from_unit: &read::CompilationUnitHeader, + debug_str: &read::DebugStr, + strings: &mut write::StringTable, + convert_address: &Fn(u64) -> Option
, + ) -> ConvertResult { + let value = + AttributeValue::from(from.value(), from_unit, debug_str, strings, convert_address)?; + Ok(Attribute { + name: from.name(), + value, + }) + } + } + + impl AttributeValue { + /// Create an attribute value by reading the data in the given sections. + pub(crate) fn from>( + from: read::AttributeValue, + from_unit: &read::CompilationUnitHeader, + debug_str: &read::DebugStr, + strings: &mut write::StringTable, + convert_address: &Fn(u64) -> Option
, + ) -> ConvertResult { + let to = match from { + read::AttributeValue::Addr(val) => match convert_address(val) { + Some(val) => AttributeValue::Address(val), + None => return Err(ConvertError::InvalidAddress), + }, + read::AttributeValue::Block(r) => AttributeValue::Block(r.to_slice()?.into()), + read::AttributeValue::Data1(val) => AttributeValue::Data1(val), + // TODO: do we need to handle endian? + read::AttributeValue::Data2((val, _endian)) => AttributeValue::Data2(val), + read::AttributeValue::Data4((val, _endian)) => AttributeValue::Data4(val), + read::AttributeValue::Data8((val, _endian)) => AttributeValue::Data8(val), + read::AttributeValue::Sdata(val) => AttributeValue::Sdata(val), + read::AttributeValue::Udata(val) => AttributeValue::Udata(val), + // TODO: addresses and offsets in expressions need special handling. + read::AttributeValue::Exprloc(read::Expression(val)) => { + AttributeValue::Exprloc(Expression(val.to_slice()?.into())) + } + // TODO: it would be nice to preserve the flag form. + read::AttributeValue::Flag(val) => AttributeValue::Flag(val), + read::AttributeValue::UnitRef(val) => { + AttributeValue::DebugInfoRef(val.to_debug_info_offset(from_unit)) + } + read::AttributeValue::DebugInfoRef(val) => AttributeValue::DebugInfoRef(val), + read::AttributeValue::DebugInfoRefSup(val) => AttributeValue::DebugInfoRefSup(val), + read::AttributeValue::DebugLineRef(val) => AttributeValue::DebugLineRef(val), + read::AttributeValue::DebugMacinfoRef(val) => AttributeValue::DebugMacinfoRef(val), + read::AttributeValue::LocationListsRef(val) => { + AttributeValue::LocationListsRef(val) + } + read::AttributeValue::RangeListsRef(val) => AttributeValue::RangeListsRef(val), + read::AttributeValue::DebugTypesRef(val) => AttributeValue::DebugTypesRef(val), + read::AttributeValue::DebugStrRef(offset) => { + let r = debug_str.get_str(offset)?; + let id = strings.add(r.to_slice()?); + AttributeValue::StringRef(id) + } + read::AttributeValue::DebugStrRefSup(val) => AttributeValue::DebugStrRefSup(val), + read::AttributeValue::String(r) => AttributeValue::String(r.to_slice()?.into()), + read::AttributeValue::Encoding(val) => AttributeValue::Encoding(val), + read::AttributeValue::DecimalSign(val) => AttributeValue::DecimalSign(val), + read::AttributeValue::Endianity(val) => AttributeValue::Endianity(val), + read::AttributeValue::Accessibility(val) => AttributeValue::Accessibility(val), + read::AttributeValue::Visibility(val) => AttributeValue::Visibility(val), + read::AttributeValue::Virtuality(val) => AttributeValue::Virtuality(val), + read::AttributeValue::Language(val) => AttributeValue::Language(val), + read::AttributeValue::AddressClass(val) => AttributeValue::AddressClass(val), + read::AttributeValue::IdentifierCase(val) => AttributeValue::IdentifierCase(val), + read::AttributeValue::CallingConvention(val) => { + AttributeValue::CallingConvention(val) + } + read::AttributeValue::Inline(val) => AttributeValue::Inline(val), + read::AttributeValue::Ordering(val) => AttributeValue::Ordering(val), + // TODO: validation + read::AttributeValue::FileIndex(val) => AttributeValue::FileIndex(val as usize), + // Should always be a more specific section reference. + read::AttributeValue::SecOffset(_) => { + return Err(ConvertError::InvalidAttributeValue); + } + }; + Ok(to) + } + } +} + #[cfg(test)] mod tests { use super::*; use constants; + use read; use std::mem; use write::{DebugStr, EndianVec, StringTable}; use LittleEndian;