Memory model when doing ffi with Rust?

71 points twitu

I've been doing ffi with Rust, compiling it as a cdylib so that other C-compatible languages can call the library (in my case it's Cython). I've understood quite a bit of how it works above the hood, but I'm perplexed by what goes on inside an ffi call.

This is the memory model I have in mind and my questions are based on it.

​

typical memory model

When cython calls an ffi function implemented in Rust (compiled into a cdylib) where is the rust function stack frame located? Is it in the same stack like this? Or is it located in a separate stack while being in the same memory stack?

    |-----------------------|
    | rust function frame   |
    | cython function frame |
    |   .........           |
    | heap memory           |
    | .........             |
    | static data           |
    | instructions cython and rust together |
    | ------------------------------------- |

Suppose I allocate some heap memory in the rust function as below.

#[no_mangle]
pub extern "C" fn foo() {
    let v = Box::new(1);
    println!("{}", v.as_ref() + 1);
}

Clearly this Box will allocate some memory in the heap memory. The heap memory is common for both cython and rust. However, an allocation like this also involves structures, for book keeping the memory, like the allocator itself. Where is the allocator allocated?

More generally, for a rust/C/C++ program being run as an executable a number of initialization happens before main is called, like register initialization, setting up the stack, initialization global variables. More details in this blog post.

Where do these these initialization steps happen in the context for an ffi call? Because there is no main entry point here, in fact there can be multiple entry points one for each exposed ffi function.

I'm super curious about how this works. And I'm happy to be corrected if my assumptions or memory model is incorrect.

Edit: update ascii diagram