I need to pass the pointer to unix's mmap, and so, I need a raw function pointer. If fn() -> () is a type for a function pointer, why does the compiler accept foo as *const fn() -> () (which causes segmentation fault on dereference) rather than &foo as *const fn() -> () since it would be a pointer to a function pointer (and a similar workaround does not causes segmentation fault)?
why does the compiler accept foo as *const fn() -> ()
Because the compiler doesn't (can't) check raw pointers. This is unsafe code territory, and you're responsible for checking your own types.
As @bluss already mentioned in the issue you linked, *const fn() -> () is a pointer to a function pointer, so that might not be what you want. I'm not sure why you're passing function pointers to mmap, so I can't really help you figure out what's going wrong without more details.
I am passing to mmap to make it executable code. It is a just in time compiled function. The problem is: *const fn() -> () when dereferenced, causes segmentation fault. But if I cast a *const *const fn() -> () to *const fn() and then dereferences it, the function is called fine. It seems a bug to me.
It sounds like you're using one too many indirections, but I can't really tell without more information about the values and types you're casting to and from.
Do you have a Rust or C function that you are calling? Can you copy its signature here? That would help us being able to tell you exactly how to call it correctly in Rust. I don't think you need a pointer to a function pointer, it is much more likely you just need a function pointer!
I think a *const u8 is what should be passed to mmap - it just wants a "data" pointer. Once the memory region is marked executable by mmap, you can then proceed to call the function using a Rust fn pointer (i.e. just fn() -> ()).
fn foo() {
println!("Hello, World!");
}
fn main() {
unsafe {
let f = &foo as *const fn();
(*f)();
}
}
I get:
error[E0606]: casting `&fn() {foo}` as `*const fn()` is invalid
--> test.rs:7:17
|
7 | let f = &foo as *const fn();
| ^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
With
fn foo() {
println!("Hello, World!");
}
fn main() {
unsafe {
let f = &&foo as *const fn();
(*f)();
}
}
I get:
error[E0606]: casting `&&fn() {foo}` as `*const fn()` is invalid
--> test.rs:7:17
|
7 | let f = &&foo as *const fn();
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
WIth
fn foo() {
println!("Hello, World!");
}
fn main() {
unsafe {
let f = foo as *const fn();
f();
}
}
I get:
error[E0618]: expected function, found `*const fn()`
--> test.rs:8:9
|
8 | f();
| ^^^
|
note: defined here
--> test.rs:7:13
|
7 | let f = foo as *const fn();
| ^
error: aborting due to previous error
With:
fn foo() {
println!("Hello, World!");
}
fn main() {
unsafe {
let f = foo as *const fn();
(*f)();
}
}
I get SIGSEGV.
With:
fn foo() {
println!("Hello, World!");
}
fn main() {
unsafe {
let f = (foo as *const fn()).as_ref().unwrap();
f();
}
}
I get SIGSEGV too.
BUT with:
fn foo() {
println!("Hello, World!");
}
fn main() {
unsafe {
let f = (&(foo as *const fn()) as *const *const fn()) as *const fn();
(*f)();
}
}
I am making a generic assembler. The problem is that Rust does not accept as primitive casting with a generic type. Also, there is no fn item constraint in the trait bounds (I said fn, not Fn). I could make a generic function signature extern "C" unsafe fn(A) -> B. The problem is that the calling convention may be chosen by the implementor of the assembly.
error[E0606]: casting `&fn() {foo}` as `*const fn()` is invalid
The type of foo is the specific function foo. You first need to cast it to a function pointer. Then you can take a reference to it and then you can cast that to a pointer to a function pointer. This works:
fn foo() {
println!("Hello, World!");
}
fn main() {
unsafe {
let f = &(foo as fn()) as *const fn();
(*f)();
}
}
But as many people have already mentioned in this thread, you probably don't need a pointer to a function pointer, and a regular function pointer would suffice:
fn foo() {
println!("Hello, World!");
}
fn main() {
let f: fn() = foo; // automatically coerced into a function pointer
f();
}
#[cfg(any(unix))]
use libc::{
mmap,
munmap,
PROT_EXEC,
PROT_READ,
PROT_WRITE,
MAP_ANONYMOUS,
MAP_PRIVATE,
};
use std::{ptr, io, slice};
/// The assembler of the x86 and x86-64 JIT assembly.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Assembler {
bytes: Vec<u8>,
}
impl Assembler {
/// Creates a new JIT assembler.
pub fn new() -> Self {
Self {
bytes: Vec::with_capacity(64)
}
}
/// Puts some bytecode into the assembler.
pub fn put(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes);
}
/// Returns the bytecode inside.
pub fn inside(&self) -> &[u8] {&self.bytes}
/// Prepares a new compiled executable code.
#[cfg(any(unix))]
pub unsafe fn prepare<T>(&self) -> Result<Compiled<T>, io::Error> {
let ptr = mmap(
ptr::null_mut(),
self.bytes.len(),
PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1,
0
);
if ptr == ptr::null_mut() {
Err(io::Error::last_os_error())
} else {
ptr::copy(self.bytes.as_ptr(), ptr as *mut u8, self.bytes.len());
Ok(Compiled {
func: ptr as *mut T,
size: self.bytes.len()
})
}
}
}
impl From<Vec<u8>> for Assembler {
fn from(bytes: Vec<u8>) -> Self {
Self {
bytes
}
}
}
/// A compiled executable code.
/// The function lifetime depends on this struct.
/// Should be only used with `fn`s (not `Fn`s).
#[derive(Debug, PartialEq, Eq)]
pub struct Compiled<T> {
func: *mut T,
size: usize,
}
impl<T> Compiled<T> {
/// Returns a pointer to the compiled function.
/// Do not call the function when the struct
/// is dropped.
pub unsafe fn func(&self) -> T
where
T: Copy
{
*(&self.func as *const *mut T as *const T)
}
/// Returns the length of the function.
pub fn len(&self) -> usize {self.size}
/// Returns the function bytecode as a vector.
pub fn bytecode(&self) -> Vec<u8> {
Vec::from(unsafe {
slice::from_raw_parts(self.func as *const u8, self.size)
})
}
}
#[cfg(any(unix))]
impl<T> Drop for Compiled<T> {
fn drop(&mut self) {
unsafe {
let result = munmap(self.func as *mut _, self.size);
debug_assert!(result >= 0);
}
}
}
Note that Compiled.func should actually be T, but I can't cast *mut c_void as T. I also cannot transmute it, because transmute needs types with same size, but I can't constraint T's size.
Ok so the main ergonimcs issue you're running into (and I think you've mentioned this) is that it's not possible to be generic over function pointers or even "things that you can cast into a pointer". You can get around this by storing the function pointer type in the type and just storing the function pointer as a generic value (I'm using usize here but you could also use *const c_void or so).
Well, I think that your solution is similar to what I did. Thank all of you by helping me and taking my questions. It is now clearer to me what *const fn() -> () means.