Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
27 changes: 18 additions & 9 deletions kernel/src/arch_impl/aarch64/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,31 +477,40 @@ fn load_segment_into_page_table(
}
}

// Calculate offset within the page where data should go.
// For the first page of a non-page-aligned segment (p_vaddr not aligned),
// data starts partway into the page.
let page_offset = if vaddr.as_u64() > page_vaddr.as_u64() {
(vaddr.as_u64() - page_vaddr.as_u64()) as usize
} else {
0usize
};

// Calculate which part of the file data maps to this page
let page_file_offset = if page_vaddr.as_u64() >= vaddr.as_u64() {
page_vaddr.as_u64() - vaddr.as_u64()
} else {
0
};

// Limit copy to the remaining space in this page to prevent buffer overflow.
// Without this, non-page-aligned segments (e.g., BusyBox data at 0x4005ff78)
// would write past the end of the physical frame, corrupting adjacent memory.
let bytes_available_in_page = (4096 - page_offset) as u64;
let copy_start_in_file = page_file_offset;
let copy_end_in_file = core::cmp::min(page_file_offset + 4096, file_size as u64);
let copy_end_in_file = core::cmp::min(
page_file_offset + bytes_available_in_page,
file_size as u64,
);

if copy_start_in_file < file_size as u64 && copy_end_in_file > copy_start_in_file {
let file_data_start = (file_start as u64 + copy_start_in_file) as usize;
let copy_size = (copy_end_in_file - copy_start_in_file) as usize;

// Calculate offset within the page where data should go
let page_offset = if vaddr.as_u64() > page_vaddr.as_u64() {
vaddr.as_u64() - page_vaddr.as_u64()
} else {
0
};

// Copy data using physical memory access (Linux-style approach)
unsafe {
let src = data.as_ptr().add(file_data_start);
let dst = phys_ptr.add(page_offset as usize);
let dst = phys_ptr.add(page_offset);
core::ptr::copy_nonoverlapping(src, dst, copy_size);
}

Expand Down
27 changes: 18 additions & 9 deletions kernel/src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,31 +523,40 @@ fn load_segment_into_page_table(
// Copy data if this page overlaps with file data
let page_start_vaddr = page.start_address();

// Calculate offset within the page where data should go.
// For the first page of a non-page-aligned segment (p_vaddr not aligned),
// data starts partway into the page.
let page_offset = if vaddr > page_start_vaddr {
(vaddr.as_u64() - page_start_vaddr.as_u64()) as usize
} else {
0usize
};

// Calculate which part of the file data maps to this page
let page_file_offset = if page_start_vaddr >= vaddr {
page_start_vaddr.as_u64() - vaddr.as_u64()
} else {
0
};

// Limit copy to the remaining space in this page to prevent buffer overflow.
// Without this, non-page-aligned segments (e.g., BusyBox data at 0x4005ff78)
// would write past the end of the physical frame, corrupting adjacent memory.
let bytes_available_in_page = (4096 - page_offset) as u64;
let copy_start_in_file = page_file_offset;
let copy_end_in_file = core::cmp::min(page_file_offset + 4096, file_size as u64);
let copy_end_in_file = core::cmp::min(
page_file_offset + bytes_available_in_page,
file_size as u64,
);

if copy_start_in_file < file_size as u64 && copy_end_in_file > copy_start_in_file {
let file_data_start = (file_start as u64 + copy_start_in_file) as usize;
let copy_size = (copy_end_in_file - copy_start_in_file) as usize;

// Calculate offset within the page where data should go
let page_offset = if vaddr > page_start_vaddr {
vaddr.as_u64() - page_start_vaddr.as_u64()
} else {
0
};

// Copy using physical memory access (Linux-style approach)
unsafe {
let src = data.as_ptr().add(file_data_start);
let dst = phys_ptr.add(page_offset as usize);
let dst = phys_ptr.add(page_offset);
core::ptr::copy_nonoverlapping(src, dst, copy_size);
}

Expand Down
78 changes: 73 additions & 5 deletions kernel/src/syscall/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,14 @@ pub const S_IFDIR: u32 = 0o040000; // Directory
pub const S_IFCHR: u32 = 0o020000; // Character device
pub const S_IFIFO: u32 = 0o010000; // FIFO (pipe)

/// stat structure (Linux compatible)
/// Note: The layout is the same for x86_64 and aarch64 Linux.
/// stat structure (Linux x86_64 compatible - 144 bytes)
///
/// x86_64 and aarch64 Linux have different struct stat layouts.
/// x86_64 uses the layout from arch/x86/include/uapi/asm/stat.h (144 bytes).
/// aarch64 uses the generic layout from include/uapi/asm-generic/stat.h (128 bytes).
/// Key differences: field order (nlink/mode swapped), nlink width (u64 vs u32),
/// blksize width (i64 vs i32), and trailing reserved space (24 vs 8 bytes).
#[cfg(target_arch = "x86_64")]
#[repr(C)]
pub struct Stat {
pub st_dev: u64,
Expand All @@ -117,6 +123,10 @@ pub struct Stat {
_reserved: [i64; 3],
}

#[cfg(target_arch = "x86_64")]
const _: () = assert!(core::mem::size_of::<Stat>() == 144);

#[cfg(target_arch = "x86_64")]
impl Stat {
/// Create a zeroed Stat structure
pub fn zeroed() -> Self {
Expand All @@ -143,6 +153,64 @@ impl Stat {
}
}

/// stat structure (Linux aarch64 compatible - 128 bytes)
///
/// Layout from include/uapi/asm-generic/stat.h used by aarch64 Linux.
#[cfg(target_arch = "aarch64")]
#[repr(C)]
pub struct Stat {
pub st_dev: u64,
pub st_ino: u64,
pub st_mode: u32,
pub st_nlink: u32,
pub st_uid: u32,
pub st_gid: u32,
pub st_rdev: u64,
_pad1: u64,
pub st_size: i64,
pub st_blksize: i32,
_pad2: i32,
pub st_blocks: i64,
pub st_atime: i64,
pub st_atime_nsec: i64,
pub st_mtime: i64,
pub st_mtime_nsec: i64,
pub st_ctime: i64,
pub st_ctime_nsec: i64,
_reserved: [u32; 2],
}

#[cfg(target_arch = "aarch64")]
const _: () = assert!(core::mem::size_of::<Stat>() == 128);

#[cfg(target_arch = "aarch64")]
impl Stat {
/// Create a zeroed Stat structure
pub fn zeroed() -> Self {
Self {
st_dev: 0,
st_ino: 0,
st_mode: 0,
st_nlink: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
_pad1: 0,
st_size: 0,
st_blksize: 0,
_pad2: 0,
st_blocks: 0,
st_atime: 0,
st_atime_nsec: 0,
st_mtime: 0,
st_mtime_nsec: 0,
st_ctime: 0,
st_ctime_nsec: 0,
_reserved: [0; 2],
}
}
}

/// sys_open - Open a file or directory
///
/// Helper: sys_open write path (O_CREAT/O_TRUNC) — works on any Ext2Fs instance.
Expand Down Expand Up @@ -696,7 +764,7 @@ pub fn sys_fstat(fd: i32, statbuf: u64) -> SyscallResult {
stat.st_uid = inode_stat.uid;
stat.st_gid = inode_stat.gid;
stat.st_size = inode_stat.size;
stat.st_nlink = inode_stat.nlink;
stat.st_nlink = inode_stat.nlink as _;
stat.st_atime = inode_stat.atime;
stat.st_mtime = inode_stat.mtime;
stat.st_ctime = inode_stat.ctime;
Expand All @@ -716,7 +784,7 @@ pub fn sys_fstat(fd: i32, statbuf: u64) -> SyscallResult {
stat.st_uid = inode_stat.uid;
stat.st_gid = inode_stat.gid;
stat.st_size = inode_stat.size;
stat.st_nlink = inode_stat.nlink;
stat.st_nlink = inode_stat.nlink as _;
stat.st_atime = inode_stat.atime;
stat.st_mtime = inode_stat.mtime;
stat.st_ctime = inode_stat.ctime;
Expand Down Expand Up @@ -3362,7 +3430,7 @@ pub fn sys_newfstatat(dirfd: i32, pathname: u64, statbuf: u64, _flags: u32) -> S
stat.st_uid = inode_stat.uid;
stat.st_gid = inode_stat.gid;
stat.st_size = inode_stat.size;
stat.st_nlink = inode_stat.nlink;
stat.st_nlink = inode_stat.nlink as _;
stat.st_atime = inode_stat.atime;
stat.st_mtime = inode_stat.mtime;
stat.st_ctime = inode_stat.ctime;
Expand Down
48 changes: 24 additions & 24 deletions kernel/src/test_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3000,11 +3000,11 @@ pub fn test_shell_pipe() {
}
}

/// Test the `btrue` coreutil
/// Test the `true` coreutil
///
/// Verifies that /bin/btrue correctly exits with code 0.
/// Verifies that /sbin/true correctly exits with code 0.
pub fn test_true_coreutil() {
log::info!("Testing btrue coreutil (exit code 0)");
log::info!("Testing true coreutil (exit code 0)");

#[cfg(feature = "testing")]
let true_test_elf_buf = crate::userspace_test::get_test_binary("true_test");
Expand Down Expand Up @@ -3033,11 +3033,11 @@ pub fn test_true_coreutil() {
}
}

/// Test the `bfalse` coreutil
/// Test the `false` coreutil
///
/// Verifies that /bin/bfalse correctly exits with code 1.
/// Verifies that /bin/false correctly exits with code 1.
pub fn test_false_coreutil() {
log::info!("Testing bfalse coreutil (exit code 1)");
log::info!("Testing false coreutil (exit code 1)");

#[cfg(feature = "testing")]
let false_test_elf_buf = crate::userspace_test::get_test_binary("false_test");
Expand Down Expand Up @@ -3066,11 +3066,11 @@ pub fn test_false_coreutil() {
}
}

/// Test the `bhead` coreutil
/// Test the `head` coreutil
///
/// Verifies that /bin/bhead correctly outputs the first N lines of files.
/// Verifies that /bin/head correctly outputs the first N lines of files.
pub fn test_head_coreutil() {
log::info!("Testing bhead coreutil (first N lines)");
log::info!("Testing head coreutil (first N lines)");

#[cfg(feature = "testing")]
let head_test_elf_buf = crate::userspace_test::get_test_binary("head_test");
Expand Down Expand Up @@ -3099,11 +3099,11 @@ pub fn test_head_coreutil() {
}
}

/// Test the `btail` coreutil
/// Test the `tail` coreutil
///
/// Verifies that /bin/btail correctly outputs the last N lines of files.
/// Verifies that /bin/tail correctly outputs the last N lines of files.
pub fn test_tail_coreutil() {
log::info!("Testing btail coreutil (last N lines)");
log::info!("Testing tail coreutil (last N lines)");

#[cfg(feature = "testing")]
let tail_test_elf_buf = crate::userspace_test::get_test_binary("tail_test");
Expand Down Expand Up @@ -3132,11 +3132,11 @@ pub fn test_tail_coreutil() {
}
}

/// Test the `bwc` coreutil
/// Test the `wc` coreutil
///
/// Verifies that /bin/bwc correctly counts lines, words, and bytes.
/// Verifies that /bin/wc correctly counts lines, words, and bytes.
pub fn test_wc_coreutil() {
log::info!("Testing bwc coreutil (line/word/byte counts)");
log::info!("Testing wc coreutil (line/word/byte counts)");

#[cfg(feature = "testing")]
let wc_test_elf_buf = crate::userspace_test::get_test_binary("wc_test");
Expand Down Expand Up @@ -3165,11 +3165,11 @@ pub fn test_wc_coreutil() {
}
}

/// Test the `bwhich` coreutil
/// Test the `which` coreutil
///
/// Verifies that /bin/bwhich correctly locates commands in PATH.
/// Verifies that /bin/which correctly locates commands in PATH.
pub fn test_which_coreutil() {
log::info!("Testing bwhich coreutil (command location)");
log::info!("Testing which coreutil (command location)");

#[cfg(feature = "testing")]
let which_test_elf_buf = crate::userspace_test::get_test_binary("which_test");
Expand Down Expand Up @@ -3198,11 +3198,11 @@ pub fn test_which_coreutil() {
}
}

/// Test the `bcat` coreutil
/// Test the `cat` coreutil
///
/// Verifies that /bin/bcat correctly outputs file contents.
/// Verifies that /bin/cat correctly outputs file contents.
pub fn test_cat_coreutil() {
log::info!("Testing bcat coreutil (file concatenation)");
log::info!("Testing cat coreutil (file concatenation)");

#[cfg(feature = "testing")]
let cat_test_elf_buf = crate::userspace_test::get_test_binary("cat_test");
Expand Down Expand Up @@ -3231,11 +3231,11 @@ pub fn test_cat_coreutil() {
}
}

/// Test the `bls` coreutil
/// Test the `ls` coreutil
///
/// Verifies that /bin/bls correctly lists directory contents.
/// Verifies that /bin/ls correctly lists directory contents.
pub fn test_ls_coreutil() {
log::info!("Testing bls coreutil (directory listing)");
log::info!("Testing ls coreutil (directory listing)");

#[cfg(feature = "testing")]
let ls_test_elf_buf = crate::userspace_test::get_test_binary("ls_test");
Expand Down
Loading
Loading