dlopen-rs 0.8.0

A dynamic linker fully implemented in Rust.
Documentation
use crate::utils::debug::GDBDebug;
use crate::{Error, Result};
use alloc::boxed::Box;
use alloc::string::ToString;
use alloc::vec::Vec;

impl From<syscalls::Errno> for Error {
    fn from(value: syscalls::Errno) -> Self {
        Error::IO(value.to_string())
    }
}

const AT_PHDR: usize = 3;
const AT_PHNUM: usize = 5;
const AT_BASE: usize = 7;

pub(crate) unsafe fn get_r_debug() -> *mut GDBDebug {
    let phdr_addr = get_auxv(AT_PHDR);
    let phnum = get_auxv(AT_PHNUM);
    let base = get_auxv(AT_BASE);

    unsafe { crate::os::find_r_debug(phdr_addr, phnum, base) }
}

fn get_auxv(target_type: usize) -> usize {
    let Ok(data) = read_file("/proc/self/auxv") else {
        return 0;
    };
    let size = core::mem::size_of::<usize>();
    for chunk in data.chunks_exact(size * 2) {
        let type_ = usize::from_ne_bytes(chunk[..size].try_into().unwrap());
        let val = usize::from_ne_bytes(chunk[size..].try_into().unwrap());
        if type_ == target_type {
            return val;
        }
        if type_ == 0 {
            break;
        }
    }
    0
}

pub(crate) fn read_file(path: &str) -> Result<Box<[u8]>> {
    read_file_limit(path, usize::MAX)
}

pub(crate) fn read_file_limit(path: &str, limit: usize) -> Result<Box<[u8]>> {
    let mut path_c = Vec::from(path.as_bytes());
    path_c.push(0);

    const O_RDONLY: usize = 0;
    const SEEK_END: usize = 2;
    const SEEK_SET: usize = 0;

    let fd = unsafe {
        #[cfg(any(
            target_arch = "aarch64",
            target_arch = "riscv64",
            target_arch = "riscv32"
        ))]
        {
            syscalls::syscall4(
                syscalls::Sysno::openat,
                -100isize as usize,
                path_c.as_ptr() as usize,
                O_RDONLY,
                0,
            )?
        }
        #[cfg(target_arch = "x86_64")]
        {
            syscalls::syscall2(syscalls::Sysno::open, path_c.as_ptr() as usize, O_RDONLY)?
        }
    };

    let read_result = (|| -> Result<Box<[u8]>> {
        let mut buffer = Vec::new();
        let file_size = unsafe {
            syscalls::syscall3(syscalls::Sysno::lseek, fd as usize, 0, SEEK_END).unwrap_or(0)
        };

        if file_size > 0
            && unsafe { syscalls::syscall3(syscalls::Sysno::lseek, fd as usize, 0, SEEK_SET) }
                .is_ok()
        {
            let read_size = core::cmp::min(file_size, limit);
            buffer.reserve_exact(read_size);
            unsafe {
                buffer.set_len(read_size);
            }
            let bytes_read = unsafe {
                syscalls::syscall3(
                    syscalls::Sysno::read,
                    fd as usize,
                    buffer.as_mut_ptr() as usize,
                    read_size,
                )?
            };
            if bytes_read != read_size {
                return Err(Error::IO(alloc::string::String::from(
                    "Failed to read complete file",
                )));
            }
        } else {
            if file_size == 0 {
                let _ =
                    unsafe { syscalls::syscall3(syscalls::Sysno::lseek, fd as usize, 0, SEEK_SET) };
            }
            let mut temp = [0u8; 1024];
            loop {
                let to_read = core::cmp::min(temp.len(), limit - buffer.len());
                if to_read == 0 {
                    break;
                }
                let bytes_read = unsafe {
                    syscalls::syscall3(
                        syscalls::Sysno::read,
                        fd as usize,
                        temp.as_mut_ptr() as usize,
                        to_read,
                    )?
                };
                if bytes_read == 0 {
                    break;
                }
                buffer.extend_from_slice(&temp[..bytes_read]);
            }
        }
        Ok(buffer.into_boxed_slice())
    })();

    unsafe {
        let _ = syscalls::syscall1(syscalls::Sysno::close, fd as usize);
    }
    read_result
}