Function pointers and raw function pointers


#1

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


#2

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.


#3

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.


#4

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.


#5

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!


#6

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


#7

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!


#8

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.


#9
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();
}

#10

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


#11

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.


#12

(trying not to repeat what others say but will add;)
e.g. the compiler accepts foo as *const String
Most relevant doc.
https://doc.rust-lang.org/nomicon/casts.html
which can not be dereferenced without using unsafe.
The compiler is designed with as unsafe that allows you to shoot yourself in the foot.


#13

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.


#14

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

#15

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.


#16

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.