use endianity::{EndianBuf, Endianity};
use fallible_iterator::FallibleIterator;
use lookup::{DebugLookup, LookupEntryIter, LookupParser};
use parser::{parse_initial_length, Error, Format, Result};
use reader::{Reader, ReaderOffset};
use unit::{parse_debug_info_offset, DebugInfoOffset};
use std::cmp::Ordering;
use std::marker::PhantomData;
use Section;
#[derive(Debug, Clone, PartialEq, Eq)]
struct ArangeHeader<T = usize> {
format: Format,
length: T,
version: u16,
offset: DebugInfoOffset<T>,
address_size: u8,
segment_size: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArangeEntry<T: Copy = usize> {
segment: Option<u64>,
address: u64,
length: u64,
unit_header_offset: DebugInfoOffset<T>,
}
impl<T: Copy> ArangeEntry<T> {
#[inline]
pub fn segment(&self) -> Option<u64> {
self.segment
}
#[inline]
pub fn address(&self) -> u64 {
self.address
}
#[inline]
pub fn length(&self) -> u64 {
self.length
}
#[inline]
pub fn debug_info_offset(&self) -> DebugInfoOffset<T> {
self.unit_header_offset
}
}
impl<T: Copy + Ord> PartialOrd for ArangeEntry<T> {
fn partial_cmp(&self, other: &ArangeEntry<T>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: Copy + Ord> Ord for ArangeEntry<T> {
fn cmp(&self, other: &ArangeEntry<T>) -> Ordering {
match (
self.segment.cmp(&other.segment),
self.address.cmp(&other.address),
self.length.cmp(&other.length),
) {
(Ordering::Equal, Ordering::Equal, Ordering::Equal) => Ordering::Equal,
(Ordering::Less, _, _) |
(Ordering::Equal, Ordering::Less, _) |
(Ordering::Equal, Ordering::Equal, Ordering::Less) => Ordering::Less,
(Ordering::Greater, _, _) |
(Ordering::Equal, Ordering::Greater, _) |
(Ordering::Equal, Ordering::Equal, Ordering::Greater) => Ordering::Greater,
}
}
}
#[derive(Clone, Debug)]
struct ArangeParser<R: Reader> {
phantom: PhantomData<R>,
}
impl<R: Reader> LookupParser<R> for ArangeParser<R> {
type Header = ArangeHeader<R::Offset>;
type Entry = ArangeEntry<R::Offset>;
fn parse_header(input: &mut R) -> Result<(R, Self::Header)> {
let (length, format) = parse_initial_length(input)?;
let length = R::Offset::from_u64(length)?;
let mut rest = input.split(length)?;
let version = rest.read_u16()?;
if version != 2 {
return Err(Error::UnknownVersion(version as u64));
}
let offset = parse_debug_info_offset(&mut rest, format)?;
let address_size = rest.read_u8()?;
let segment_size = rest.read_u8()?;
let header_length = match format {
Format::Dwarf32 => 4 + 2 + 4 + 1 + 1,
Format::Dwarf64 => 12 + 2 + 8 + 1 + 1,
};
let tuple_length = 2 * address_size + segment_size;
let padding = if header_length % tuple_length == 0 {
0
} else {
tuple_length - header_length % tuple_length
};
rest.skip(R::Offset::from_u8(padding))?;
Ok((
rest,
ArangeHeader {
format: format,
length: length,
version: version,
offset: offset,
address_size: address_size,
segment_size: segment_size,
},
))
}
fn parse_entry(input: &mut R, header: &Self::Header) -> Result<Option<Self::Entry>> {
let address_size = header.address_size;
let segment_size = header.segment_size;
let tuple_length = R::Offset::from_u8(2 * address_size + segment_size);
if tuple_length > input.len() {
input.empty();
return Ok(None);
}
let segment = if segment_size != 0 {
input.read_address(segment_size)?
} else {
0
};
let address = input.read_address(address_size)?;
let length = input.read_address(address_size)?;
match (segment, address, length) {
(0, 0, 0) => Self::parse_entry(input, header),
_ => Ok(Some(ArangeEntry {
segment: if segment_size != 0 {
Some(segment)
} else {
None
},
address: address,
length: length,
unit_header_offset: header.offset,
})),
}
}
}
#[derive(Debug, Clone)]
pub struct DebugAranges<R: Reader>(DebugLookup<R, ArangeParser<R>>);
impl<'input, Endian> DebugAranges<EndianBuf<'input, Endian>>
where
Endian: Endianity,
{
pub fn new(debug_aranges_section: &'input [u8], endian: Endian) -> Self {
Self::from(EndianBuf::new(debug_aranges_section, endian))
}
}
impl<R: Reader> DebugAranges<R> {
pub fn items(&self) -> ArangeEntryIter<R> {
ArangeEntryIter(self.0.items())
}
}
impl<R: Reader> Section<R> for DebugAranges<R> {
fn section_name() -> &'static str {
".debug_aranges"
}
}
impl<R: Reader> From<R> for DebugAranges<R> {
fn from(debug_aranges_section: R) -> Self {
DebugAranges(DebugLookup::from(debug_aranges_section))
}
}
#[derive(Debug, Clone)]
pub struct ArangeEntryIter<R: Reader>(LookupEntryIter<R, ArangeParser<R>>);
impl<R: Reader> ArangeEntryIter<R> {
pub fn next(&mut self) -> Result<Option<ArangeEntry<R::Offset>>> {
self.0.next()
}
}
impl<R: Reader> FallibleIterator for ArangeEntryIter<R> {
type Item = ArangeEntry<R::Offset>;
type Error = Error;
fn next(&mut self) -> ::std::result::Result<Option<Self::Item>, Self::Error> {
self.0.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
use lookup::LookupParser;
use endianity::{EndianBuf, LittleEndian};
use parser::Format;
use unit::DebugInfoOffset;
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_parse_header_ok() {
let buf = [
0x20, 0x00, 0x00, 0x00,
0x02, 0x00,
0x01, 0x02, 0x03, 0x04,
0x08,
0x04,
0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let rest = &mut EndianBuf::new(&buf, LittleEndian);
let (tuples, header) = ArangeParser::parse_header(rest)
.expect("should parse header ok");
assert_eq!(*rest, EndianBuf::new(&buf[buf.len() - 16..], LittleEndian));
assert_eq!(tuples, EndianBuf::new(&buf[buf.len() - 32..buf.len() - 16], LittleEndian));
assert_eq!(header,
ArangeHeader {
format: Format::Dwarf32,
length: 0x20,
version: 2,
offset: DebugInfoOffset(0x04030201),
address_size: 8,
segment_size: 4,
});
}
#[test]
fn test_parse_entry_ok() {
let header = ArangeHeader {
format: Format::Dwarf32,
length: 0,
version: 2,
offset: DebugInfoOffset(0),
address_size: 4,
segment_size: 0,
};
let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
let rest = &mut EndianBuf::new(&buf, LittleEndian);
let entry = ArangeParser::parse_entry(rest, &header).expect("should parse entry ok");
assert_eq!(*rest, EndianBuf::new(&buf[buf.len() - 1..], LittleEndian));
assert_eq!(
entry,
Some(ArangeEntry {
segment: None,
address: 0x04030201,
length: 0x08070605,
unit_header_offset: header.offset,
})
);
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_parse_entry_segment() {
let header = ArangeHeader {
format: Format::Dwarf32,
length: 0,
version: 2,
offset: DebugInfoOffset(0),
address_size: 4,
segment_size: 8,
};
let buf = [
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x09
];
let rest = &mut EndianBuf::new(&buf, LittleEndian);
let entry = ArangeParser::parse_entry(rest, &header)
.expect("should parse entry ok");
assert_eq!(*rest, EndianBuf::new(&buf[buf.len() - 1..], LittleEndian));
assert_eq!(entry,
Some(ArangeEntry {
segment: Some(0x1817161514131211),
address: 0x04030201,
length: 0x08070605,
unit_header_offset: header.offset,
}));
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_parse_entry_zero() {
let header = ArangeHeader {
format: Format::Dwarf32,
length: 0,
version: 2,
offset: DebugInfoOffset(0),
address_size: 4,
segment_size: 0,
};
let buf = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x09
];
let rest = &mut EndianBuf::new(&buf, LittleEndian);
let entry = ArangeParser::parse_entry(rest, &header)
.expect("should parse entry ok");
assert_eq!(*rest, EndianBuf::new(&buf[buf.len() - 1..], LittleEndian));
assert_eq!(entry,
Some(ArangeEntry {
segment: None,
address: 0x04030201,
length: 0x08070605,
unit_header_offset: header.offset,
}));
}
}