use std::ffi::{CStr, c_void};
use std::os::fd::{AsRawFd, IntoRawFd};
use std::path::Path;
use std::ptr;
use crate::PatchError;
#[repr(C)]
struct ExtInfo {
flags: u64,
reserved_addr: *const c_void,
reserved_size: libc::size_t,
relro_fd: libc::c_int,
library_fd: libc::c_int,
library_fd_offset: libc::off64_t,
library_namespace: *const c_void,
}
const ANDROID_DLEXT_USE_LIBRARY_FD: u64 = 0x10;
unsafe extern "C" {
fn android_dlopen_ext(
filename: *const libc::c_char,
flags: libc::c_int,
ext_info: *const ExtInfo,
) -> *const c_void;
}
pub(crate) unsafe fn memfd_dlopen(file: &Path) -> Result<libloading::Library, PatchError> {
let memfd_err =
|what: &str, e: &dyn std::fmt::Display| PatchError::AndroidMemfd(format!("{what}: {e}"));
let contents = std::fs::read(file).map_err(|e| memfd_err("Failed to read file", &e))?;
let mfd = memfd::MemfdOptions::default()
.create("frust-hotpatch")
.map_err(|e| memfd_err("Failed to create memfd", &e))?;
mfd.as_file()
.set_len(contents.len() as u64)
.map_err(|e| memfd_err("Failed to set memfd length", &e))?;
let mut map = unsafe { memmap2::MmapMut::map_mut(mfd.as_file()) }
.map_err(|e| memfd_err("Failed to map memfd", &e))?;
map.copy_from_slice(&contents);
let _exec_map = map
.make_exec()
.map_err(|e| memfd_err("Failed to make memfd executable", &e))?;
let info = ExtInfo {
flags: ANDROID_DLEXT_USE_LIBRARY_FD,
reserved_addr: ptr::null(),
reserved_size: 0,
relro_fd: 0,
library_fd: mfd.as_raw_fd(),
library_fd_offset: 0,
library_namespace: ptr::null(),
};
let flags = libloading::os::unix::RTLD_LAZY | libloading::os::unix::RTLD_LOCAL;
let handle = libloading::os::unix::with_dlerror(
|| {
let handle = unsafe { android_dlopen_ext(c"/frust-hotpatch".as_ptr(), flags, &info) };
(!handle.is_null()).then_some(handle)
},
|err: &CStr| err.to_string_lossy().into_owned(),
)
.map_err(|e| {
PatchError::AndroidMemfd(format!(
"android_dlopen_ext failed: {}",
e.unwrap_or_default()
))
})?;
let _ = mfd.into_raw_fd();
let lib = unsafe { libloading::os::unix::Library::from_raw(handle.cast_mut()) };
Ok(lib.into())
}