From a271fbe1958d0de71b477f67681c453cd5e71fdf Mon Sep 17 00:00:00 2001 From: Alexandra Iordache Date: Fri, 6 Mar 2020 21:01:15 +0200 Subject: [PATCH] cosmetic changes & doctests Signed-off-by: Alexandra Iordache --- src/cmdline/mod.rs | 103 +++++++++++++---- src/lib.rs | 16 ++- src/loader/mod.rs | 231 +++++++++++++++++++++++--------------- src/loader/struct_util.rs | 67 +++++++++-- 4 files changed, 289 insertions(+), 128 deletions(-) diff --git a/src/cmdline/mod.rs b/src/cmdline/mod.rs index abeb095f..012ee4d4 100644 --- a/src/cmdline/mod.rs +++ b/src/cmdline/mod.rs @@ -12,7 +12,7 @@ use std::fmt; use std::result; /// The error type for command line building operations. -#[derive(PartialEq, Debug)] +#[derive(Debug, PartialEq)] pub enum Error { /// Operation would have resulted in a non-printable ASCII character. InvalidAscii, @@ -30,16 +30,17 @@ impl fmt::Display for Error { f, "{}", match *self { - Error::InvalidAscii => "string contains non-printable ASCII character", - Error::HasSpace => "string contains a space", - Error::HasEquals => "string contains an equals sign", - Error::TooLarge => "inserting string would make command line too long", + Error::InvalidAscii => "String contains a non-printable ASCII character.", + Error::HasSpace => "String contains a space.", + Error::HasEquals => "String contains an equals sign.", + Error::TooLarge => "Inserting string would make command line too long.", } ) } } -/// Specialized Result type for command line operations. +/// Specialized [`Result`] type for command line operations. +/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html pub type Result = result::Result; fn valid_char(c: char) -> bool { @@ -69,17 +70,37 @@ fn valid_element(s: &str) -> Result<()> { } } -/// A builder for a kernel command line string that validates the string as its being built. A -/// `CString` can be constructed from this directly using `CString::new`. -#[derive(Clone)] +/// A builder for a kernel command line string that validates the string as it's being built. +/// A `CString` can be constructed from this directly using `CString::new`. +/// +/// # Examples +/// +/// ```rust +/// # use linux_loader::cmdline::*; +/// # use std::ffi::CString; +/// let cl = Cmdline::new(100); +/// let cl_cstring = CString::new(cl).unwrap(); +/// assert_eq!(cl_cstring.to_str().unwrap(), ""); +/// ``` pub struct Cmdline { line: String, capacity: usize, } impl Cmdline { - /// Constructs an empty Cmdline with the given capacity, which includes the nul terminator. - /// Capacity must be greater than 0. + /// Constructs an empty [`Cmdline`] with the given capacity, including the nul terminator. + /// + /// # Arguments + /// + /// * `capacity` - Command line capacity. Must be greater than 0. + /// + /// # Examples + /// + /// ```rust + /// # use linux_loader::cmdline::*; + /// let cl = Cmdline::new(100); + /// ``` + /// [`Cmdline`]: struct.Cmdline.html pub fn new(capacity: usize) -> Cmdline { assert_ne!(capacity, 0); Cmdline { @@ -109,7 +130,23 @@ impl Cmdline { assert!(self.line.len() < self.capacity); } - /// Validates and inserts a key value pair into this command line + /// Validates and inserts a key-value pair into this command line. + /// + /// # Arguments + /// + /// * `key` - Key to be inserted in the command line string. + /// * `val` - Value corresponding to `key`. + /// + /// # Examples + /// + /// ```rust + /// # use linux_loader::cmdline::*; + /// # use std::ffi::CString; + /// let mut cl = Cmdline::new(100); + /// cl.insert("foo", "bar"); + /// let cl_cstring = CString::new(cl).unwrap(); + /// assert_eq!(cl_cstring.to_str().unwrap(), "foo=bar"); + /// ``` pub fn insert>(&mut self, key: T, val: T) -> Result<()> { let k = key.as_ref(); let v = val.as_ref(); @@ -127,7 +164,22 @@ impl Cmdline { Ok(()) } - /// Validates and inserts a string to the end of the current command line + /// Validates and inserts a string to the end of the current command line. + /// + /// # Arguments + /// + /// * `slug` - String to be appended to the command line. + /// + /// # Examples + /// + /// ```rust + /// # use linux_loader::cmdline::*; + /// # use std::ffi::CString; + /// let mut cl = Cmdline::new(100); + /// cl.insert_str("foobar"); + /// let cl_cstring = CString::new(cl).unwrap(); + /// assert_eq!(cl_cstring.to_str().unwrap(), "foobar"); + /// ``` pub fn insert_str>(&mut self, slug: T) -> Result<()> { let s = slug.as_ref(); valid_str(s)?; @@ -141,7 +193,16 @@ impl Cmdline { Ok(()) } - /// Returns the cmdline in progress without nul termination + /// Returns the string representation of the command line without the nul terminator. + /// + /// # Examples + /// + /// ```rust + /// # use linux_loader::cmdline::*; + /// let mut cl = Cmdline::new(10); + /// cl.insert_str("foobar"); + /// assert_eq!(cl.as_str(), "foobar"); + /// ``` pub fn as_str(&self) -> &str { self.line.as_str() } @@ -159,7 +220,7 @@ mod tests { use std::ffi::CString; #[test] - fn insert_hello_world() { + fn test_insert_hello_world() { let mut cl = Cmdline::new(100); assert_eq!(cl.as_str(), ""); assert!(cl.insert("hello", "world").is_ok()); @@ -170,7 +231,7 @@ mod tests { } #[test] - fn insert_multi() { + fn test_insert_multi() { let mut cl = Cmdline::new(100); assert!(cl.insert("hello", "world").is_ok()); assert!(cl.insert("foo", "bar").is_ok()); @@ -178,7 +239,7 @@ mod tests { } #[test] - fn insert_space() { + fn test_insert_space() { let mut cl = Cmdline::new(100); assert_eq!(cl.insert("a ", "b"), Err(Error::HasSpace)); assert_eq!(cl.insert("a", "b "), Err(Error::HasSpace)); @@ -188,7 +249,7 @@ mod tests { } #[test] - fn insert_equals() { + fn test_insert_equals() { let mut cl = Cmdline::new(100); assert_eq!(cl.insert("a=", "b"), Err(Error::HasEquals)); assert_eq!(cl.insert("a", "b="), Err(Error::HasEquals)); @@ -199,7 +260,7 @@ mod tests { } #[test] - fn insert_emoji() { + fn test_insert_emoji() { let mut cl = Cmdline::new(100); assert_eq!(cl.insert("heart", "💖"), Err(Error::InvalidAscii)); assert_eq!(cl.insert("💖", "love"), Err(Error::InvalidAscii)); @@ -207,7 +268,7 @@ mod tests { } #[test] - fn insert_string() { + fn test_insert_string() { let mut cl = Cmdline::new(13); assert_eq!(cl.as_str(), ""); assert!(cl.insert_str("noapic").is_ok()); @@ -217,7 +278,7 @@ mod tests { } #[test] - fn insert_too_large() { + fn test_insert_too_large() { let mut cl = Cmdline::new(4); assert_eq!(cl.insert("hello", "world"), Err(Error::TooLarge)); assert_eq!(cl.insert("a", "world"), Err(Error::TooLarge)); diff --git a/src/lib.rs b/src/lib.rs index fadbd490..d1147a73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,20 +11,18 @@ //! A Linux kernel image loading crate. //! -//! This crate offers support for loading raw ELF (vmlinux) and compressed -//! big zImage (bzImage) kernel images. -//! Support for any other kernel image format can be added by implementing -//! the KernelLoader. +//! This crate offers support for loading raw ELF (vmlinux) and compressed big zImage (bzImage) +//! kernel images. +//! Support for any other kernel image format can be added by implementing the [`KernelLoader`]. //! //! # Platform support //! -//! - x86_64 +//! - `x86_64` //! -//! This crates only supports x86_64 platforms because it implements support -//! for kernel image formats (vmlinux and bzImage) that are x86 specific. +//! Extending this crate to support other kernel image formats (e.g. ARM's Image) will make it +//! consumable by other platforms. //! -//! Extending it to support other kernel image formats (e.g. ARM's Image) -//! will make it consumable by other platforms. +//! [`KernelLoader`]: trait.KernelLoader.html pub mod cmdline; pub mod loader; diff --git a/src/loader/mod.rs b/src/loader/mod.rs index b5804449..1dba8ea9 100644 --- a/src/loader/mod.rs +++ b/src/loader/mod.rs @@ -7,12 +7,12 @@ // // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause -//! Traits and Structs -//! - [KernelLoader](trait.KernelLoader.html): load kernel image into guest memory -//! - [KernelLoaderResult](struct.KernelLoaderResult.html): the structure which loader -//! returns to VMM to assist zero page construction and boot environment setup -//! - [Elf](struct.Elf.html): elf image loader -//! - [BzImage](struct.BzImage.html): bzImage loader +//! Traits and structs for loading kernels into guest memory. +//! - [KernelLoader](trait.KernelLoader.html): load kernel image into guest memory. +//! - [KernelLoaderResult](struct.KernelLoaderResult.html): structure passed to the VMM to assist +//! zero page construction and boot environment setup. +//! - [Elf](struct.Elf.html): elf image loader. +//! - [BzImage](struct.BzImage.html): bzImage loader. extern crate vm_memory; @@ -36,15 +36,17 @@ use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize}; #[allow(missing_docs)] #[cfg_attr(feature = "cargo-clippy", allow(clippy::all))] pub mod bootparam; + #[allow(dead_code)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(non_upper_case_globals)] #[cfg_attr(feature = "cargo-clippy", allow(clippy::all))] mod elf; + #[cfg(any(feature = "elf", feature = "bzimage"))] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -mod struct_util; +pub mod struct_util; #[derive(Debug, PartialEq)] /// Kernel loader errors. @@ -95,7 +97,8 @@ pub enum Error { SeekBzImageCompressedKernel, } -/// A specialized `Result` type for the kernel loader. +/// A specialized [`Result`] type for the kernel loader. +/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html pub type Result = std::result::Result; impl error::Error for Error { @@ -135,27 +138,34 @@ impl Display for Error { } } -#[derive(Debug, Default, Copy, Clone, PartialEq)] -/// Result of the KernelLoader load() call. +#[derive(Debug, Default, PartialEq)] +/// Result of [`KernelLoader.load()`](trait.KernelLoader.html#tymethod.load). /// -/// This specifies where the kernel is loading and passes additional -/// information for the rest of the boot process to be completed by -/// the VMM. +/// This specifies where the kernel is loading and passes additional information for the rest of +/// the boot process to be completed by the VMM. pub struct KernelLoaderResult { - /// Address in the guest memory where the kernel image starts to be loaded + /// Address in the guest memory where the kernel image starts to be loaded. pub kernel_load: GuestAddress, - /// Offset in guest memory corresponding to the end of kernel image, in case that - /// device tree blob and initrd will be loaded adjacent to kernel image. + /// Offset in guest memory corresponding to the end of kernel image, in case the device tree + /// blob and initrd will be loaded adjacent to kernel image. pub kernel_end: GuestUsize, - /// This field is only for bzImage following https://www.kernel.org/doc/Documentation/x86/boot.txt - /// VMM should make use of it to fill zero page for bzImage direct boot. + /// Configuration for the VMM to use to fill zero page for bzImage direct boot. + /// See https://www.kernel.org/doc/Documentation/x86/boot.txt. pub setup_header: Option, } -/// A kernel image loading support must implement the KernelLoader trait. -/// The only method to be implemented is the load one, returning a KernelLoaderResult structure. +/// Trait that specifies kernel image loading support. pub trait KernelLoader { /// How to load a specific kernel image format into the guest memory. + /// + /// # Arguments + /// + /// * `guest_mem`: [`GuestMemory`] to load the kernel in. + /// * `kernel_start`: Address in guest memory where the kernel is loaded. + /// * `kernel_image`: Kernel image to be loaded. + /// * `highmem_start_address`: Address where high memory starts. + /// + /// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html fn load( guest_mem: &M, kernel_start: Option, @@ -174,19 +184,40 @@ pub struct Elf; #[cfg(feature = "elf")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl KernelLoader for Elf { - /// Loads a kernel from a vmlinux elf image to a slice + /// Loads a kernel from a vmlinux elf image into guest memory. /// - /// kernel is loaded into guest memory at offset phdr.p_paddr specified by elf image. + /// The kernel is loaded into guest memory at offset `phdr.p_paddr` specified by the elf image. /// /// # Arguments /// - /// * `guest_mem` - The guest memory region the kernel is written to. - /// * `kernel_start` - The offset into 'guest_mem' at which to load the kernel. + /// * `guest_mem`: [`GuestMemory`] to load the kernel in. + /// * `kernel_start`: Address in guest memory where the kernel is loaded. /// * `kernel_image` - Input vmlinux image. - /// * `highmem_start_address` - This is the start of the high memory, kernel should above it. + /// * `highmem_start_address`: Address where high memory starts. + /// + /// # Examples /// - /// # Returns - /// * KernelLoaderResult + /// ```rust + /// # extern crate vm_memory; + /// # use linux_loader::loader::*; + /// # use vm_memory::{Address, GuestAddress, GuestMemoryMmap}; + /// # use std::io::Cursor; + /// let mem_size: usize = 0x1000000; + /// let himem_start = GuestAddress(0x0); + /// let kernel_addr = GuestAddress(0x200000); + /// let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), mem_size)]).unwrap(); + /// let mut kernel_image = vec![]; + /// kernel_image.extend_from_slice(include_bytes!("test_elf.bin")); + /// let loader_result = Elf::load( + /// &gm, + /// Some(kernel_addr), + /// &mut Cursor::new(&kernel_image), + /// Some(himem_start), + /// ).unwrap(); + /// assert_eq!(loader_result.kernel_load.raw_value(), 0x300000); + /// ``` + /// + /// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html fn load( guest_mem: &M, kernel_start: Option, @@ -288,20 +319,45 @@ pub struct BzImage; #[cfg(feature = "bzimage")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl KernelLoader for BzImage { - /// Loads a bzImage + /// Loads a kernel from a bzImage to guest memory. /// - /// kernel is loaded into guest memory at code32_start the default load address - /// stored in bzImage setup header. + /// The kernel is loaded at `code32_start`, the default load address stored in the bzImage + /// setup header. /// /// # Arguments /// - /// * `guest_mem` - The guest memory where the kernel image is loaded. - /// * `kernel_start` - The offset into 'guest_mem' at which to load the kernel. + /// * `guest_mem`: [`GuestMemory`] to load the kernel in. + /// * `kernel_start`: Address in guest memory where the kernel is loaded. /// * `kernel_image` - Input bzImage image. - /// * `highmem_start_address` - This is the start of the high memory, kernel should above it. + /// * `highmem_start_address`: Address where high memory starts. + /// + /// # Examples /// - /// # Returns - /// * KernelLoaderResult + /// ```rust + /// # extern crate vm_memory; + /// # use linux_loader::loader::*; + /// # use vm_memory::{Address, GuestAddress, GuestMemoryMmap}; + /// # use std::io::Cursor; + /// let mem_size: usize = 0x1000000; + /// let himem_start = GuestAddress(0x0); + /// let kernel_addr = GuestAddress(0x200000); + /// let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), mem_size)]).unwrap(); + /// let mut kernel_image = vec![]; + /// kernel_image.extend_from_slice(include_bytes!("bzimage")); + /// let loader_result = Elf::load( + /// &gm, + /// Some(kernel_addr), + /// &mut Cursor::new(&kernel_image), + /// Some(himem_start), + /// ).unwrap(); + /// assert_eq!(loader_result.kernel_load.raw_value(), 0x200000); + /// assert_eq!(loader_result.setup_header.unwrap().header, 0x53726448); + /// assert_eq!(loader_result.setup_header.unwrap().version. 0x20d); + /// assert_eq!(loader_result.setup_header.unwrap().loadflags, 1); + /// assert_eq!(loader_result.kernel_end, 0x60c320); + /// ``` + /// + /// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html fn load( guest_mem: &M, kernel_start: Option, @@ -378,13 +434,31 @@ impl KernelLoader for BzImage { } } -/// Writes the command line string to the given memory slice. +/// Writes the command line string to the given guest memory slice. /// /// # Arguments /// -/// * `guest_mem` - A u8 slice that will be partially overwritten by the command line. +/// * `guest_mem` - [`GuestMemory`] that will be partially overwritten by the command line. /// * `guest_addr` - The address in `guest_mem` at which to load the command line. /// * `cmdline` - The kernel command line. +/// +/// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html +/// +/// # Examples +/// +/// ```rust +/// # extern crate vm_memory; +/// # use linux_loader::loader::*; +/// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; +/// # use std::ffi::CStr; +/// let mem_size: usize = 0x1000000; +/// let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), mem_size)]).unwrap(); +/// let cl = CStr::from_bytes_with_nul(b"foo=bar\0").unwrap(); +/// let mut buf = vec![0u8;8]; +/// let result = load_cmdline(&gm, GuestAddress(0x1000), &cl).unwrap(); +/// gm.read_slice(buf.as_mut_slice(), GuestAddress(0x1000)).unwrap(); +/// assert_eq!(buf.as_slice(), "foo=bar\0".as_bytes()); +/// pub fn load_cmdline( guest_mem: &M, guest_addr: GuestAddress, @@ -445,7 +519,7 @@ mod test { #[test] #[cfg(feature = "bzimage")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn load_bzImage() { + fn test_load_bzImage() { let gm = create_guest_mem(); let image = make_bzimage(); let mut kernel_start = GuestAddress(0x200000); @@ -459,23 +533,12 @@ mod test { Some(highmem_start_address), ) .unwrap(); - assert_eq!(0x53726448, loader_result.setup_header.unwrap().header); - println!( - "bzImage is loaded at {:8x} \n", - loader_result.kernel_load.raw_value() - ); - println!( - "bzImage version is {:2x} \n", - loader_result.setup_header.unwrap().version - ); - println!( - "bzImage loadflags is {:x} \n", - loader_result.setup_header.unwrap().loadflags - ); - println!( - "bzImage kernel size is {:4x} \n", - (loader_result.kernel_end as u32) - ); + + assert_eq!(loader_result.kernel_load.raw_value(), 0x200000); + assert_eq!(loader_result.setup_header.unwrap().header, 0x53726448); + assert_eq!(loader_result.setup_header.unwrap().version, 0x20d); + assert_eq!(loader_result.setup_header.unwrap().loadflags, 1); + assert_eq!(loader_result.kernel_end, 0x60c320); // load bzImage without kernel_start loader_result = BzImage::load( @@ -485,37 +548,32 @@ mod test { Some(highmem_start_address), ) .unwrap(); - assert_eq!(0x53726448, loader_result.setup_header.unwrap().header); - println!( - "bzImage is loaded at {:8x} \n", - loader_result.kernel_load.raw_value() - ); + assert_eq!(loader_result.kernel_load.raw_value(), 0x100000); // load bzImage withouth himem_start loader_result = BzImage::load(&gm, None, &mut Cursor::new(&image), None).unwrap(); assert_eq!(0x53726448, loader_result.setup_header.unwrap().header); - println!( - "bzImage is loaded at {:8x} \n", - loader_result.kernel_load.raw_value() - ); + assert_eq!(loader_result.kernel_load.raw_value(), 0x100000); // load bzImage with a bad himem setting kernel_start = GuestAddress(0x1000); highmem_start_address = GuestAddress(0x200000); - let x = BzImage::load( - &gm, - Some(kernel_start), - &mut Cursor::new(&image), - Some(highmem_start_address), + + assert_eq!( + BzImage::load( + &gm, + Some(kernel_start), + &mut Cursor::new(&image), + Some(highmem_start_address), + ), + Err(Error::InvalidKernelStartAddress) ); - assert_eq!(x.is_ok(), false); - println!("load bzImage with bad himem setting \n"); } #[test] #[cfg(feature = "elf")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn load_elf() { + fn test_load_elf() { let gm = create_guest_mem(); let image = make_elf_bin(); let kernel_addr = GuestAddress(0x200000); @@ -527,16 +585,10 @@ mod test { Some(highmem_start_address), ) .unwrap(); - println!( - "load elf at address {:8x} \n", - loader_result.kernel_load.raw_value() - ); + assert_eq!(loader_result.kernel_load.raw_value(), 0x300000); loader_result = Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap(); - println!( - "load elf at address {:8x} \n", - loader_result.kernel_load.raw_value() - ); + assert_eq!(loader_result.kernel_load.raw_value(), 0x300000); loader_result = Elf::load( &gm, @@ -545,10 +597,7 @@ mod test { Some(highmem_start_address), ) .unwrap(); - println!( - "load elf at address {:8x} \n", - loader_result.kernel_load.raw_value() - ); + assert_eq!(loader_result.kernel_load.raw_value(), 0x100000); highmem_start_address = GuestAddress(0xa00000); assert_eq!( @@ -563,7 +612,7 @@ mod test { } #[test] - fn cmdline_overflow() { + fn test_cmdline_overflow() { let gm = create_guest_mem(); let cmdline_address = GuestAddress(MEM_SIZE - 5); assert_eq!( @@ -577,7 +626,7 @@ mod test { } #[test] - fn cmdline_write_end() { + fn test_cmdline_write_end() { let gm = create_guest_mem(); let mut cmdline_address = GuestAddress(45); assert_eq!( @@ -607,7 +656,7 @@ mod test { #[cfg(feature = "elf")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[test] - fn bad_magic() { + fn test_bad_magic_number() { let gm = create_guest_mem(); let kernel_addr = GuestAddress(0x0); let mut bad_image = make_elf_bin(); @@ -621,8 +670,8 @@ mod test { #[cfg(feature = "elf")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[test] - fn bad_endian() { - // Only little endian is supported + fn test_bad_endian() { + // Only little endian is supported. let gm = create_guest_mem(); let kernel_addr = GuestAddress(0x0); let mut bad_image = make_elf_bin(); @@ -636,8 +685,8 @@ mod test { #[cfg(feature = "elf")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[test] - fn bad_phoff() { - // program header has to be past the end of the elf header + fn test_bad_phoff() { + // Program header has to be past the end of the elf header. let gm = create_guest_mem(); let kernel_addr = GuestAddress(0x0); let mut bad_image = make_elf_bin(); diff --git a/src/loader/struct_util.rs b/src/loader/struct_util.rs index dc0714b9..4e0c1cfd 100644 --- a/src/loader/struct_util.rs +++ b/src/loader/struct_util.rs @@ -7,14 +7,21 @@ // // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +//! Utility functions for struct manipulation. + use std; use std::io::Read; use std::mem; #[derive(Debug)] +/// Errors related to struct manipulation. pub enum Error { + /// Failed to read struct. ReadStruct, } + +/// A specialized [`Result`] type(trait.KernelLoader.html). for struct manipulation. +/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html pub type Result = std::result::Result; /// Reads a struct from an input buffer. @@ -24,6 +31,30 @@ pub type Result = std::result::Result; /// * `f` - The input to read from. Often this is a file. /// * `out` - The struct to fill with data read from `f`. /// +/// # Examples +/// +/// ```rust +/// # use std::io::Cursor; +/// # use std::slice; +/// # use std::mem::size_of; +/// # use linux_loader::loader::struct_util::*; +/// #[derive(Clone, Copy, Debug, Default, PartialEq)] +/// struct Foo { +/// bar: u32, +/// baz: u8, +/// } +/// +/// let foo = Foo { bar: 0xdead_beef, baz: 42 }; +/// let foo_bytes = unsafe { +/// slice::from_raw_parts(&foo as *const _ as *const u8, size_of::()) +/// }; +/// let mut other_foo = Foo::default(); +/// unsafe { +/// read_struct(&mut Cursor::new(foo_bytes), &mut other_foo).unwrap(); +/// } +/// assert_eq!(foo, other_foo); +/// ``` +/// /// # Safety /// /// This is unsafe because the struct is initialized to unverified data read from the input. @@ -42,6 +73,32 @@ pub unsafe fn read_struct(f: &mut F, out: &mut T) -> Result<() /// * `f` - The input to read from. Often this is a file. /// * `len` - The number of structs to fill with data read from `f`. /// +/// # Examples +/// +/// ```rust +/// # use std::io::Cursor; +/// # use std::slice; +/// # use std::mem::size_of; +/// # use linux_loader::loader::struct_util::*; +/// #[derive(Clone, Copy, Debug, Default, PartialEq)] +/// struct Foo { +/// bar: u32, +/// baz: u8, +/// } +/// +/// let foo_v = vec![ +/// Foo { bar: 0xdead_beef, baz: 42 }, +/// Foo { bar: 0xcafe_babe, baz: 24 }, +/// ]; +/// let foo_bytes = unsafe { +/// slice::from_raw_parts(foo_v.as_ptr() as *const u8, 2 * size_of::()) +/// }; +/// let other_foo_v = unsafe { +/// read_struct_slice(&mut Cursor::new(foo_bytes), 2).unwrap() +/// }; +/// assert_eq!(foo_v, other_foo_v); +/// ``` +/// /// # Safety /// /// This is unsafe because the struct is initialized to unverified data read from the input. @@ -74,7 +131,7 @@ mod tests { } #[test] - fn struct_basic_read() { + fn test_struct_basic_read() { let orig = TestRead { a: 0x7766554433221100, b: 0x88, @@ -83,7 +140,6 @@ mod tests { e: 0xbb, }; let source = unsafe { - // Don't worry it's a test std::slice::from_raw_parts( &orig as *const _ as *const u8, std::mem::size_of::(), @@ -98,7 +154,7 @@ mod tests { } #[test] - fn struct_read_past_end() { + fn test_struct_read_past_end() { let orig = TestRead { a: 0x7766554433221100, b: 0x88, @@ -107,7 +163,6 @@ mod tests { e: 0xbb, }; let source = unsafe { - // Don't worry it's a test std::slice::from_raw_parts( &orig as *const _ as *const u8, std::mem::size_of::() - 1, @@ -116,13 +171,12 @@ mod tests { let mut tr: TestRead = Default::default(); unsafe { assert!(read_struct(&mut Cursor::new(source), &mut tr).is_err()); - format!("{:?}", read_struct(&mut Cursor::new(source), &mut tr)); } } #[test] #[cfg(feature = "elf")] - fn struct_slice_read() { + fn test_struct_slice_read() { let orig = vec![ TestRead { a: 0x7766554433221100, @@ -147,7 +201,6 @@ mod tests { }, ]; let source = unsafe { - // Don't worry it's a test std::slice::from_raw_parts( orig.as_ptr() as *const u8, std::mem::size_of::() * 3,