use crate::Result;
use alloc::ffi::CString;
pub struct ElfFile {
name: CString,
fd: i32,
}
impl ElfFile {
pub unsafe fn from_owned_fd(path: &str, raw_fd: i32) -> Self {
ElfFile {
name: CString::new(path).unwrap(),
fd: raw_fd,
}
}
pub fn from_path(path: &str) -> Result<Self> {
from_path(path)
}
}
#[cfg(feature = "use-libc")]
mod imp {
use super::ElfFile;
use crate::{Result, io_error, object::ElfObject};
use alloc::ffi::CString;
use core::{ffi::CStr, str::FromStr};
use libc::{O_RDONLY, SEEK_SET};
impl Drop for ElfFile {
fn drop(&mut self) {
unsafe { libc::close(self.fd) };
}
}
pub(crate) fn from_path(path: &str) -> Result<ElfFile> {
let name = CString::from_str(path).unwrap();
let fd = unsafe { libc::open(name.as_ptr(), O_RDONLY) };
if fd == -1 {
return Err(io_error("open failed"));
}
Ok(ElfFile { name, fd })
}
fn lseek(fd: i32, offset: usize) -> Result<()> {
let off = unsafe { libc::lseek(fd, offset as _, SEEK_SET) };
if off == -1 || off as usize != offset {
return Err(io_error("lseek failed"));
}
Ok(())
}
fn read_exact(fd: i32, mut bytes: &mut [u8]) -> Result<()> {
loop {
if bytes.is_empty() {
return Ok(());
}
let bytes_to_read = bytes.len();
let ptr = bytes.as_mut_ptr() as *mut libc::c_void;
let result = unsafe { libc::read(fd, ptr, bytes_to_read) };
if result < 0 {
return Err(io_error("read error"));
} else if result == 0 {
return Err(io_error("failed to fill buffer"));
}
let n = result as usize;
bytes = &mut bytes[n..];
}
}
impl ElfObject for ElfFile {
fn read(&mut self, buf: &mut [u8], offset: usize) -> Result<()> {
lseek(self.fd, offset)?;
read_exact(self.fd, buf)?;
Ok(())
}
fn file_name(&self) -> &CStr {
&self.name
}
fn as_fd(&self) -> Option<i32> {
Some(self.fd)
}
}
}
#[cfg(feature = "use-syscall")]
mod imp {
use super::ElfFile;
use crate::{Result, io_error, object::ElfObject};
use alloc::{borrow::ToOwned, ffi::CString};
use core::{ffi::CStr, str::FromStr};
use syscalls::Sysno;
pub(crate) fn from_path(path: &str) -> Result<ElfFile> {
const RDONLY: u32 = 0;
let name = CString::from_str(path).unwrap().to_owned();
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
let fd = unsafe {
from_ret(
syscalls::raw_syscall!(Sysno::open, name.as_ptr(), RDONLY, 0),
"open failed",
)?
};
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
let fd = unsafe {
const AT_FDCWD: core::ffi::c_int = -100;
from_ret(
syscalls::raw_syscall!(Sysno::openat, AT_FDCWD, name.as_ptr(), RDONLY, 0),
"openat failed",
)?
};
Ok(ElfFile { fd: fd as _, name })
}
impl Drop for ElfFile {
fn drop(&mut self) {
unsafe {
from_ret(
syscalls::raw_syscall!(Sysno::close, self.fd),
"close failed",
)
.unwrap();
}
}
}
impl ElfObject for ElfFile {
fn read(&mut self, buf: &mut [u8], offset: usize) -> Result<()> {
const SEEK_START: u32 = 0;
unsafe {
from_ret(
syscalls::raw_syscall!(Sysno::lseek, self.fd, offset, SEEK_START),
"lseek failed",
)?;
let size = from_ret(
syscalls::raw_syscall!(Sysno::read, self.fd, buf.as_mut_ptr(), buf.len()),
"read failed",
)?;
assert!(size == buf.len());
}
Ok(())
}
fn file_name(&self) -> &CStr {
&self.name
}
fn as_fd(&self) -> Option<i32> {
Some(self.fd)
}
}
#[inline(always)]
fn from_ret(value: usize, msg: &str) -> Result<usize> {
if value > -4096isize as usize {
return Err(io_error(msg));
}
Ok(value)
}
}
use imp::from_path;