diff --git a/src/elf/mod.rs b/src/elf/mod.rs index 46d567299..d7cf7ae79 100644 --- a/src/elf/mod.rs +++ b/src/elf/mod.rs @@ -35,7 +35,8 @@ use core::mem; /// /// assert_eq!(error_message, "invalid ELF address range"); /// ``` -#[derive(Debug, Clone, Copy)] + +#[derive(Debug, Clone, Copy, PartialEq)] pub enum ElfError { FileTooShort, @@ -330,7 +331,8 @@ impl cmp::PartialOrd for Elf64AddrRange { /// This struct represents a parsed 64-bit ELF file. It contains information /// about the ELF file's header, load segments, dynamic section, and more. -#[derive(Default, Debug, Clone, Copy)] + +#[derive(Default, Debug, Clone, Copy, PartialEq)] pub struct Elf64FileRange { pub offset_begin: usize, pub offset_end: usize, @@ -362,7 +364,7 @@ impl convert::TryFrom<(Elf64Off, Elf64Xword)> for Elf64FileRange { /// This struct represents a parsed 64-bit ELF file. It contains information /// about the ELF file's header, load segments, dynamic section, and more. -#[derive(Default, Debug)] +#[derive(Default, Debug, PartialEq)] pub struct Elf64File<'a> { /// Buffer containing the ELF file data elf_file_buf: &'a [u8], @@ -1005,7 +1007,8 @@ impl<'a> Elf64File<'a> { /// Header of the ELF64 file, including fields describing properties such /// as type, machine architecture, entry point, etc. -#[derive(Debug, Default, Clone, Copy)] + +#[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Elf64Hdr { #[allow(unused)] /// An array of 16 bytes representing the ELF identification, including the ELF magic number @@ -1442,7 +1445,7 @@ impl Elf64Shdr { /// Represents a collection of ELF64 load segments, each associated with an /// address range and a program header index. -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] struct Elf64LoadSegments { segments: Vec<(Elf64AddrRange, Elf64Half)>, } @@ -1551,7 +1554,7 @@ impl Elf64LoadSegments { } /// Represents an ELF64 dynamic relocation table -#[derive(Debug)] +#[derive(Debug, PartialEq)] struct Elf64DynamicRelocTable { /// Virtual address of the relocation table (DT_RELA / DR_REL) base_vaddr: Elf64Addr, @@ -1583,7 +1586,7 @@ impl Elf64DynamicRelocTable { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] struct Elf64DynamicSymtab { /// Base virtual address of the symbol table (DT_SYMTAB) base_vaddr: Elf64Addr, @@ -1609,7 +1612,7 @@ impl Elf64DynamicSymtab { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] struct Elf64Dynamic { // No DT_REL representation: "The AMD64 ABI architectures uses only // Elf64_Rela relocation entries [...]". @@ -1856,7 +1859,7 @@ impl<'a> Iterator for Elf64ImageLoadSegmentIterator<'a> { /// Represents an ELF64 string table ([`Elf64Strtab`]) containing strings /// used within the ELF file -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] struct Elf64Strtab<'a> { strtab_buf: &'a [u8], } @@ -2410,3 +2413,197 @@ impl<'a, RP: Elf64RelocProcessor> Iterator for Elf64AppliedRelaIterator<'a, RP> Some(Ok(Some(reloc_op))) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_elf64_shdr_verify_methods() { + // Create a valid Elf64Shdr instance for testing. + let valid_shdr = Elf64Shdr { + sh_name: 1, + sh_type: 2, + sh_flags: Elf64ShdrFlags::WRITE | Elf64ShdrFlags::ALLOC, + sh_addr: 0x1000, + sh_offset: 0x2000, + sh_size: 0x3000, + sh_link: 3, + sh_info: 4, + sh_addralign: 8, + sh_entsize: 0, + }; + + // Verify that the valid Elf64Shdr instance passes verification. + assert!(valid_shdr.verify().is_ok()); + + // Create an invalid Elf64Shdr instance for testing. + let invalid_shdr = Elf64Shdr { + sh_name: 0, + sh_type: 2, + sh_flags: Elf64ShdrFlags::from_bits(0).unwrap(), + sh_addr: 0x1000, + sh_offset: 0x2000, + sh_size: 0x3000, + sh_link: 3, + sh_info: 4, + sh_addralign: 7, // Invalid alignment + sh_entsize: 0, + }; + + // Verify that the invalid Elf64Shdr instance fails verification. + assert!(invalid_shdr.verify().is_err()); + } + + #[test] + fn test_elf64_dynamic_reloc_table_verify_valid() { + // Create a valid Elf64DynamicRelocTable instance for testing. + let reloc_table = Elf64DynamicRelocTable { + base_vaddr: 0x1000, + size: 0x2000, + entsize: 0x30, + }; + + // Verify that the valid Elf64DynamicRelocTable instance passes verification. + assert!(reloc_table.verify().is_ok()); + } + + #[test] + fn test_elf64_addr_range_methods() { + // Test Elf64AddrRange::len() and Elf64AddrRange::is_empty(). + + // Create an Elf64AddrRange instance for testing. + let addr_range = Elf64AddrRange { + vaddr_begin: 0x1000, + vaddr_end: 0x2000, + }; + + // Check that the length calculation is correct. + assert_eq!(addr_range.len(), 0x1000); + + // Check if the address range is empty. + assert!(!addr_range.is_empty()); + + // Test Elf64AddrRange::try_from(). + + // Create a valid input tuple for try_from. + let valid_input: (Elf64Addr, Elf64Xword) = (0x1000, 0x2000); + + // Attempt to create an Elf64AddrRange from the valid input. + let result = Elf64AddrRange::try_from(valid_input); + + // Verify that the result is Ok and contains the expected Elf64AddrRange. + assert!(result.is_ok()); + let valid_addr_range = result.unwrap(); + assert_eq!(valid_addr_range.vaddr_begin, 0x1000); + assert_eq!(valid_addr_range.vaddr_end, 0x3000); + } + + #[test] + fn test_elf64_file_range_try_from() { + // Valid range + let valid_range: (Elf64Off, Elf64Xword) = (0, 100); + let result: Result = valid_range.try_into(); + assert!(result.is_ok()); + let file_range = result.unwrap(); + assert_eq!(file_range.offset_begin, 0); + assert_eq!(file_range.offset_end, 100); + + // Invalid range (overflow) + let invalid_range: (Elf64Off, Elf64Xword) = (usize::MAX as Elf64Off, 100); + let result: Result = invalid_range.try_into(); + assert!(result.is_err()); + } + + #[test] + fn test_elf64_file_read() { + // In the future, you can play around with this skeleton ELF + // file to test other cases + let byte_data: [u8; 184] = [ + // ELF Header + 0x7F, 0x45, 0x4C, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Program Header (with PT_LOAD) + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Section Header (simplified) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Raw Machine Code Instructions + 0xf3, 0x0f, 0x1e, 0xfa, 0x31, 0xed, 0x49, 0x89, 0xd1, 0x5e, 0x48, 0x89, 0xe2, 0x48, + 0x83, 0xe4, 0xf0, 0x50, 0x54, 0x45, 0x31, 0xc0, 0x31, 0xc9, 0x48, 0x8d, 0x3d, 0xca, + 0x00, 0x00, 0x00, 0xff, 0x15, 0x53, 0x2f, 0x00, 0x00, 0xf4, 0x66, 0x2e, 0x0f, 0x1f, + 0x84, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x3d, 0x79, 0x2f, 0x00, 0x00, 0x48, 0x8d, + 0x05, 0x72, 0x2f, 0x00, 0x00, 0x48, 0x39, 0xf8, 0x74, 0x15, 0x48, 0x8b, 0x05, 0x36, + 0x2f, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x09, 0xff, 0xe0, 0x0f, 0x1f, 0x80, 0x00, + 0x00, 0x00, 0x00, 0xc3, + ]; + + // Use the Elf64File::read method to create an Elf64File instance + let res = Elf64File::read(&byte_data); + assert_eq!(res, Err(crate::elf::ElfError::InvalidPhdrSize)); + + // Construct an Elf64Hdr instance from the byte data + let elf_hdr = Elf64Hdr::read(&byte_data); + + // Did we fail to read the ELF header? + assert!(elf_hdr.is_ok()); + let elf_hdr = elf_hdr.unwrap(); + + let expected_type = 2; + let expected_machine = 0x3E; + let expected_version = 1; + + // Assert that the fields of the header match the expected values + assert_eq!(elf_hdr.e_type, expected_type); + assert_eq!(elf_hdr.e_machine, expected_machine); + assert_eq!(elf_hdr.e_version, expected_version); + } + + #[test] + fn test_elf64_load_segments() { + let mut load_segments = Elf64LoadSegments::new(); + let vaddr_range1 = Elf64AddrRange { + vaddr_begin: 0x1000, + vaddr_end: 0x2000, + }; + let vaddr_range2 = Elf64AddrRange { + vaddr_begin: 0x3000, + vaddr_end: 0x4000, + }; + let segment_index1 = 0; + let segment_index2 = 1; + + // Insert load segments + assert!(load_segments + .try_insert(vaddr_range1, segment_index1) + .is_ok()); + assert!(load_segments + .try_insert(vaddr_range2, segment_index2) + .is_ok()); + + // Lookup load segments by virtual address + let (index1, offset1) = load_segments + .lookup_vaddr_range(&Elf64AddrRange { + vaddr_begin: 0x1500, + vaddr_end: 0x1700, + }) + .unwrap(); + let (index2, offset2) = load_segments + .lookup_vaddr_range(&Elf64AddrRange { + vaddr_begin: 0x3500, + vaddr_end: 0x3700, + }) + .unwrap(); + + assert_eq!(index1, segment_index1); + assert_eq!(offset1, 0x500); // Offset within the segment + assert_eq!(index2, segment_index2); + assert_eq!(offset2, 0x500); // Offset within the segment + + // Total virtual address range + let total_range = load_segments.total_vaddr_range(); + assert_eq!(total_range.vaddr_begin, 0x1000); + assert_eq!(total_range.vaddr_end, 0x4000); + } +} diff --git a/src/locking/rwlock.rs b/src/locking/rwlock.rs index f1b30a8bb..f148765da 100644 --- a/src/locking/rwlock.rs +++ b/src/locking/rwlock.rs @@ -262,3 +262,50 @@ impl RWLock { } } } + +mod tests { + + #[test] + fn test_lock_rw() { + use crate::locking::*; + let rwlock = RWLock::new(42); + + // Acquire a read lock and check the initial value + let read_guard = rwlock.lock_read(); + assert_eq!(*read_guard, 42); + + drop(read_guard); + + let read_guard2 = rwlock.lock_read(); + assert_eq!(*read_guard2, 42); + + // Create another RWLock instance for modification + let rwlock_modify = RWLock::new(0); + + let mut write_guard = rwlock_modify.lock_write(); + *write_guard = 99; + assert_eq!(*write_guard, 99); + + drop(write_guard); + + let read_guard = rwlock.lock_read(); + assert_eq!(*read_guard, 42); + } + + #[test] + fn test_concurrent_readers() { + use crate::locking::*; + // Let's test two concurrent readers on a new RWLock instance + let rwlock_concurrent = RWLock::new(123); + + let read_guard1 = rwlock_concurrent.lock_read(); + let read_guard2 = rwlock_concurrent.lock_read(); + + // Assert that both readers can access the same value (123) + assert_eq!(*read_guard1, 123); + assert_eq!(*read_guard2, 123); + + drop(read_guard1); + drop(read_guard2); + } +} diff --git a/src/locking/spinlock.rs b/src/locking/spinlock.rs index 97e012422..67c91c44c 100644 --- a/src/locking/spinlock.rs +++ b/src/locking/spinlock.rs @@ -174,3 +174,23 @@ impl SpinLock { None } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_spin_lock() { + let spin_lock = SpinLock::new(0); + + let mut guard = spin_lock.lock(); + *guard += 1; + + // Ensure the locked data is updated. + assert_eq!(*guard, 1); + + // Try to lock again; it should fail and return None. + let try_lock_result = spin_lock.try_lock(); + assert!(try_lock_result.is_none()); + } +}