Function pointers and raw function pointers

Please read this issue: https://github.com/rust-lang/rust/issues/47615.

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.

1 Like

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.

1 Like

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() -> ()).

I'll show you what I mean.
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[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 get: Hello, World!

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();
}
4 Likes

This is not my actual code. It is just a reproduction of the compilation conditions.

Ok, well, without more information we can't really help you fix your problem.

All I can tell you is that there is no bug in the compiler and the 6 code examples you posted are behaving as expected.

(trying not to repeat what others say but will add;)
e.g. the compiler accepts foo as *const String
Most relevant doc.

which can not be dereferenced without using unsafe.
The compiler is designed with as unsafe that allows you to shoot yourself in the foot.

Till now, I have the following:

#[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).

use std::marker::PhantomData;

impl Assembler {
    pub unsafe fn prepare<T>(&self) -> Result<Compiled<T>, io::Error> {
        ...
            Ok(Compiled {
                func: ptr as usize,
                fty: PhantomData,
                size: self.bytes.len()
            })
        ...
    }
}

pub struct Compiled<T> {
    func: usize,
    fty: PhantomData<T>,
    size: usize,
}

impl<T> Compiled<T> {
    pub unsafe fn func(&self) -> T
    where
        T: Copy
    {
        *(&self.func as *const usize as *const T)
    }
}

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.

A macro could do it instead of the generic function. Not sure if it would be just shifting the ugliness or have a good effect.