Home > Blockchain >  mem::transmute to generic function type
mem::transmute to generic function type

Time:03-07

TL; DR;
Is it possible in rust to have a generic function that will convert a raw function pointer to a function.

pub fn <F>convert_raw_ptr_to_func(raw_func_ptr: *const Addr) -> F {
    unsafe {
        std::mem::transmute::<_, F>(raw_func_ptr)
    }

}

Use case:
In my program, I often have to retrieve a function from virtual function pointer table by an index. This is how I do this currently:

pub fn get_fn_from_vtbl_by_idx(vtbl_ptr: *const Addr, idx: u8) -> *mut Addr {
    let vtbl_ptr = vtbl_ptr as *const Addr;
    unsafe {
        let vtbl = *vtbl_ptr as *const Addr;
        vtbl.offset(idx as isize).read() as *mut Addr
    }
}

// Some place in code
type FnFoo = unsafe extern "thiscall" fn(this: *const Addr, a: i32) -> *mut Addr;
let fn_foo_addr = get_fn_from_vtbl_by_idx(self.base, 3);
let fn_foo = unsafe { std::mem::transmute::<_, FnFoo >(fn_foo_addr) };

It would be really nice if I could reduce the boilerplate and move the transmute to get_fn_from_vtbl_by_idx function, to get something like this:

pub fn get_fn_from_vtbl_by_idx<F>(vtbl_ptr: *const Addr, idx: u8) -> F {
    let vtbl_ptr = vtbl_ptr as *const Addr;
    unsafe {
        let vtbl = *vtbl_ptr as *const Addr;
        let func_addr = vtbl.offset(idx as isize).read() as *mut Addr;
        std::mem::transmute::<_, F>(func_addr)
    }
}

CodePudding user response:

std::mem::transmute requires that the source and destination types have the same size, but it can't be determined that your generic F is pointer-sized.

Instead, you can utilise a conversion trait:

trait FromFuncAddr: Sized {
    unsafe fn from_func_addr(func_addr: *mut Addr) -> Self;
}

impl FromFuncAddr for FnFoo {
    unsafe fn from_func_addr(func_addr: *mut Addr) -> Self {
        std::mem::transmute(func_addr)
    }
}

pub fn get_fn_from_vtbl_by_idx<F: FromFuncAddr>(vtbl_ptr: *const Addr, idx: u8) -> F {
    let vtbl_ptr = vtbl_ptr as *const Addr;
    unsafe {
        let vtbl = *vtbl_ptr as *const Addr;
        let func_addr = vtbl.offset(idx as isize).read() as *mut Addr;
        F::from_func_addr(func_addr)
    }
}

CodePudding user response:

The usual way is to use transmute_copy(), which is the same as (p as *const Dest).read(). You should be careful to not use both values if they're not Copy, but if you don't the compiler should be able to optimize it away.

pub fn get_fn_from_vtbl_by_idx<F>(vtbl_ptr: *const Addr, idx: u8) -> F {
    let vtbl_ptr = vtbl_ptr as *const Addr;
    unsafe {
        let vtbl = *vtbl_ptr as *const Addr;
        let func_addr = vtbl.offset(idx as isize).read() as *mut Addr;
        std::mem::transmute_copy(&func_addr)
    }
}
  • Related