Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests ELF + ACPI tables #112

Merged
merged 3 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 203 additions & 9 deletions src/elf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use core::mem;
///
/// assert_eq!(error_message, "invalid ELF address range");
/// ```
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum ElfError {
FileTooShort,

Expand Down Expand Up @@ -330,7 +330,7 @@ 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)]
#[derive(Default, Debug, PartialEq)]
pub struct Elf64FileRange {
pub offset_begin: usize,
pub offset_end: usize,
Expand Down Expand Up @@ -362,7 +362,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],
Expand Down Expand Up @@ -1005,7 +1005,7 @@ impl<'a> Elf64File<'a> {

/// Header of the ELF64 file, including fields describing properties such
/// as type, machine architecture, entry point, etc.
#[derive(Debug, Default)]
#[derive(Debug, Default, PartialEq)]
pub struct Elf64Hdr {
#[allow(unused)]
/// An array of 16 bytes representing the ELF identification, including the ELF magic number
Expand Down Expand Up @@ -1442,7 +1442,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)>,
}
Expand Down Expand Up @@ -1551,7 +1551,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,
Expand Down Expand Up @@ -1583,7 +1583,7 @@ impl Elf64DynamicRelocTable {
}
}

#[derive(Debug)]
#[derive(Debug, PartialEq)]
struct Elf64DynamicSymtab {
/// Base virtual address of the symbol table (DT_SYMTAB)
base_vaddr: Elf64Addr,
Expand All @@ -1609,7 +1609,7 @@ impl Elf64DynamicSymtab {
}
}

#[derive(Debug)]
#[derive(Debug, PartialEq)]
struct Elf64Dynamic {
// No DT_REL representation: "The AMD64 ABI architectures uses only
// Elf64_Rela relocation entries [...]".
Expand Down Expand Up @@ -1853,7 +1853,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],
}
Expand Down Expand Up @@ -2402,3 +2402,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<Elf64FileRange, ElfError> = 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<Elf64FileRange, ElfError> = invalid_range.try_into();
assert!(result.is_err());
}
Zildj1an marked this conversation as resolved.
Show resolved Hide resolved

#[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);
}
}
47 changes: 47 additions & 0 deletions src/locking/rwlock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,50 @@ impl<T: Debug> RWLock<T> {
}
}
}

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);
}
}
20 changes: 20 additions & 0 deletions src/locking/spinlock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,23 @@ impl<T: Debug> SpinLock<T> {
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());
}
}