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)
}
}