I am writing a windows program that uses only ntdll.dll in its import, that is why I need custom memory manager. I know that there is a GlobalAlloc
trait for implementing custom allocator, but as I said, my program must have imports from ntdll.dll only, that is why I am writing all memory operations myself.
In particular, I am implementing reference counter, that frees memory when counter = 0. Here is code:
use core::ops::Drop;
use super::{alloc_mem, free_mem, ALLOC_MEM_ERROR};
pub struct TheBox<T: Clone>
{
data: *mut T,
rc: *mut u64,
}
impl<T: Clone> TheBox<T>
{
pub fn new(v: T) -> Result<TheBox<T>, u32>
{
unsafe
{
let mem: usize = alloc_mem(core::mem::size_of::<T>() + core::mem::size_of::<u64>());
if mem == 0
{
return Err(ALLOC_MEM_ERROR);
}
let d = mem + core::mem::size_of::<u64>();
let rc_mem = mem;
let rc: *mut u64 = rc_mem as *mut u64;
*rc = 1;
let data: *mut T = d as *mut T;
*data = v;
Ok(TheBox
{
data: data,
rc: rc,
})
}
}
pub fn get_val(&self) -> &T
{
unsafe
{
&(*self.data)
}
}
pub fn get_val_mut(&mut self) -> &mut T
{
unsafe
{
&mut (*self.data)
}
}
pub fn unbox(&self) -> T
{
unsafe
{
(*(self.data)).clone()
}
}
}
impl<T: Clone> Clone for TheBox<T>
{
fn clone(&self) -> TheBox<T>
{
unsafe
{
asm!("lock inc qword ptr [rax]" : : "{rax}"(self.rc) : : "intel");
}
TheBox
{
data: self.data,
rc: self.rc,
}
}
}
impl<T: Clone> Drop for TheBox<T>
{
fn drop(&mut self)
{
unsafe
{
let mut do_free: u8;
asm!("lock dec qword ptr [rax]" : : "{rax}"(self.rc) : : "intel");
asm!("sete cl" : "={cl}"(do_free) : : : "intel");
if do_free == 1
{
free_mem(self.rc as usize);
}
}
}
}
Here alloc_mem
and free_mem
calls RtlAllocateHeap
and RtlFreeHeap
.
When I initialize memory with zeros in alloc_mem
everything is like normal, but when I do not initialize memory with zeros, it crashes in drop method on instruction with lock
prefix because self.rc
is invalid.
What I am doing wrong?