use crate::error::{Result, WraithError};
use crate::navigation::ModuleHandle;
use crate::structures::list_entry::{unlink_entry, ListEntry};
pub struct SavedLinks {
in_load_order_flink: *mut ListEntry,
in_load_order_blink: *mut ListEntry,
in_memory_order_flink: *mut ListEntry,
in_memory_order_blink: *mut ListEntry,
in_init_order_flink: *mut ListEntry,
in_init_order_blink: *mut ListEntry,
}
unsafe impl Send for SavedLinks {}
unsafe impl Sync for SavedLinks {}
pub struct UnlinkGuard {
handle: ModuleHandle,
saved: SavedLinks,
auto_relink: bool,
}
impl UnlinkGuard {
pub fn leak(mut self) {
self.auto_relink = false;
core::mem::forget(self);
}
pub fn forget(mut self) {
self.auto_relink = false;
}
pub fn relink(self) -> Result<()> {
if !self.auto_relink {
relink_module_internal(&self.handle, &self.saved)?;
}
Ok(())
}
pub fn handle(&self) -> &ModuleHandle {
&self.handle
}
pub fn will_auto_relink(&self) -> bool {
self.auto_relink
}
pub fn set_auto_relink(&mut self, auto: bool) {
self.auto_relink = auto;
}
}
impl Drop for UnlinkGuard {
fn drop(&mut self) {
if self.auto_relink {
let _ = relink_module_internal(&self.handle, &self.saved);
}
}
}
pub fn unlink_module(handle: ModuleHandle) -> Result<UnlinkGuard> {
let links = handle.get_link_pointers();
if links.in_load_order.is_null()
|| links.in_memory_order.is_null()
|| links.in_initialization_order.is_null()
{
return Err(WraithError::NullPointer {
context: "module list links",
});
}
let saved = unsafe {
SavedLinks {
in_load_order_flink: (*links.in_load_order).flink,
in_load_order_blink: (*links.in_load_order).blink,
in_memory_order_flink: (*links.in_memory_order).flink,
in_memory_order_blink: (*links.in_memory_order).blink,
in_init_order_flink: (*links.in_initialization_order).flink,
in_init_order_blink: (*links.in_initialization_order).blink,
}
};
unsafe {
unlink_entry(links.in_load_order);
unlink_entry(links.in_memory_order);
unlink_entry(links.in_initialization_order);
}
Ok(UnlinkGuard {
handle,
saved,
auto_relink: true,
})
}
pub fn relink_module(guard: UnlinkGuard) -> Result<()> {
guard.relink()
}
fn relink_module_internal(handle: &ModuleHandle, saved: &SavedLinks) -> Result<()> {
let links = handle.get_link_pointers();
unsafe {
(*links.in_load_order).flink = saved.in_load_order_flink;
(*links.in_load_order).blink = saved.in_load_order_blink;
(*saved.in_load_order_blink).flink = links.in_load_order;
(*saved.in_load_order_flink).blink = links.in_load_order;
(*links.in_memory_order).flink = saved.in_memory_order_flink;
(*links.in_memory_order).blink = saved.in_memory_order_blink;
(*saved.in_memory_order_blink).flink = links.in_memory_order;
(*saved.in_memory_order_flink).blink = links.in_memory_order;
(*links.in_initialization_order).flink = saved.in_init_order_flink;
(*links.in_initialization_order).blink = saved.in_init_order_blink;
(*saved.in_init_order_blink).flink = links.in_initialization_order;
(*saved.in_init_order_flink).blink = links.in_initialization_order;
}
Ok(())
}
pub fn unlink_permanent(handle: ModuleHandle) -> Result<()> {
let guard = unlink_module(handle)?;
guard.leak();
Ok(())
}
#[cfg(test)]
mod tests {
#[test]
fn test_saved_links_size() {
use super::SavedLinks;
let expected = 6 * core::mem::size_of::<*mut ()>();
assert_eq!(core::mem::size_of::<SavedLinks>(), expected);
}
}