// this exists primarily to test various API usages of scroll; e.g., must compile // guard against potential undefined behaviour when borrowing from // packed structs. See https://github.com/rust-lang/rust/issues/46043 #![deny(safe_packed_borrows)] // #[macro_use] extern crate scroll_derive; use std::ops::{Deref, DerefMut}; use scroll::{ctx, Result, Cread, Pread}; use scroll::ctx::SizeWith; #[derive(Default)] pub struct Section<'a> { pub sectname: [u8; 16], pub segname: [u8; 16], pub addr: u64, pub size: u64, pub offset: u32, pub align: u32, pub reloff: u32, pub nreloc: u32, pub flags: u32, pub data: &'a [u8], } impl<'a> Section<'a> { pub fn name(&self) -> Result<&str> { self.sectname.pread::<&str>(0) } pub fn segname(&self) -> Result<&str> { self.segname.pread::<&str>(0) } } impl<'a> ctx::SizeWith for Section<'a> { fn size_with(_ctx: &()) -> usize { 4 } } #[repr(C)] // renable when scroll_derive Pread/Pwrite matches //#[derive(Debug, Clone, Copy, Pread, Pwrite)] #[derive(Debug, Clone, Copy)] pub struct Section32 { pub sectname: [u8; 16], pub segname: [u8; 16], pub addr: u32, pub size: u32, pub offset: u32, pub align: u32, pub reloff: u32, pub nreloc: u32, pub flags: u32, pub reserved1: u32, pub reserved2: u32, } impl<'a> ctx::TryFromCtx<'a, ()> for Section<'a> { type Error = scroll::Error; fn try_from_ctx(_bytes: &'a [u8], _ctx: ()) -> ::std::result::Result<(Self, usize), Self::Error> { let section = Section::default(); Ok((section, ::std::mem::size_of::
())) } } pub struct Segment<'a> { pub cmd: u32, pub cmdsize: u32, pub segname: [u8; 16], pub vmaddr: u64, pub vmsize: u64, pub fileoff: u64, pub filesize: u64, pub maxprot: u32, pub initprot: u32, pub nsects: u32, pub flags: u32, pub data: &'a [u8], offset: usize, raw_data: &'a [u8], } impl<'a> Segment<'a> { pub fn name(&self) -> Result<&str> { Ok(self.segname.pread::<&str>(0)?) } pub fn sections(&self) -> Result>> { let nsects = self.nsects as usize; let mut sections = Vec::with_capacity(nsects); let offset = &mut (self.offset + Self::size_with(&())); let _size = Section::size_with(&()); let raw_data: &'a [u8] = self.raw_data; for _ in 0..nsects { let section = raw_data.gread_with::>(offset, ())?; sections.push(section); //offset += size; } Ok(sections) } } impl<'a> ctx::SizeWith for Segment<'a> { fn size_with(_ctx: &()) -> usize { 4 } } pub struct Segments<'a> { pub segments: Vec>, } impl<'a> Deref for Segments<'a> { type Target = Vec>; fn deref(&self) -> &Self::Target { &self.segments } } impl<'a> DerefMut for Segments<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.segments } } impl<'a> Segments<'a> { pub fn new() -> Self { Segments { segments: Vec::new(), } } pub fn sections(&self) -> Result>>> { let mut sections = Vec::new(); for segment in &self.segments { sections.push(segment.sections()?); } Ok(sections) } } fn lifetime_passthrough_<'a>(segments: &Segments<'a>, section_name: &str) -> Option<&'a [u8]> { let segment_name = "__TEXT"; for segment in &segments.segments { if let Ok(name) = segment.name() { println!("segment.name: {}", name); if name == segment_name { if let Ok(sections) = segment.sections() { for section in sections { let sname = section.name().unwrap(); println!("section.name: {}", sname); if section_name == sname { return Some(section.data); } } } } } } None } #[test] fn lifetime_passthrough() { let segments = Segments::new(); let _res = lifetime_passthrough_(&segments, "__text"); assert!(true) } #[derive(Default)] #[repr(packed)] struct Foo { foo: i64, bar: u32, } impl scroll::ctx::FromCtx for Foo { fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { Foo { foo: bytes.cread_with::(0, ctx), bar: bytes.cread_with::(8, ctx) } } } impl scroll::ctx::SizeWith for Foo { fn size_with(_: &scroll::Endian) -> usize { ::std::mem::size_of::() } } #[test] fn ioread_api() { use std::io::Cursor; use scroll::{LE, IOread}; let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; let mut bytes = Cursor::new(bytes_); let foo = bytes.ioread_with::(LE).unwrap(); let bar = bytes.ioread_with::(LE).unwrap(); assert_eq!(foo, 1); assert_eq!(bar, 0xbeef); let error = bytes.ioread_with::(LE); assert!(error.is_err()); let mut bytes = Cursor::new(bytes_); let foo_ = bytes.ioread_with::(LE).unwrap(); assert_eq!({foo_.foo}, foo); assert_eq!({foo_.bar}, bar); } #[repr(packed)] struct Bar { foo: i32, bar: u32, } impl scroll::ctx::FromCtx for Bar { fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { Bar { foo: bytes.cread_with(0, ctx), bar: bytes.cread_with(4, ctx) } } } #[test] fn cread_api() { use scroll::{LE, Cread}; let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; let foo = bytes.cread_with::(0, LE); let bar = bytes.cread_with::(8, LE); assert_eq!(foo, 1); assert_eq!(bar, 0xbeef); } #[test] fn cread_api_customtype() { use scroll::{LE, Cread}; let bytes = [0xff, 0xff, 0xff, 0xff, 0xef,0xbe,0xad,0xde,]; let bar = &bytes[..].cread_with::(0, LE); assert_eq!({bar.foo}, -1); assert_eq!({bar.bar}, 0xdeadbeef); } #[test] #[should_panic] fn cread_api_badindex() { use scroll::Cread; let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0xad,0xde,]; let _foo = bytes.cread::(1_000_000); } #[test] fn cwrite_api() { use scroll::Cwrite; use scroll::Cread; let mut bytes = [0x0; 16]; bytes.cwrite::(42, 0); bytes.cwrite::(0xdeadbeef, 8); assert_eq!(bytes.cread::(0), 42); assert_eq!(bytes.cread::(8), 0xdeadbeef); } impl scroll::ctx::IntoCtx for Bar { fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) { use scroll::Cwrite; bytes.cwrite_with(self.foo, 0, ctx); bytes.cwrite_with(self.bar, 4, ctx); } } #[test] fn cwrite_api_customtype() { use scroll::{Cwrite, Cread}; let bar = Bar { foo: -1, bar: 0xdeadbeef }; let mut bytes = [0x0; 16]; &bytes[..].cwrite::(bar, 0); let bar = bytes.cread::(0); assert_eq!({bar.foo}, -1); assert_eq!({bar.bar}, 0xdeadbeef); }