1

I build a shared object with Rust and link against it in C. Compiling/Linking works fine but as soon as I call the function I get a SegFault.

Rust function declaration: It consumes two integers/usize and a reference to a function that returns an integer/usize.

#[no_mangle]
pub fn show_loading_animation(from: usize,
                              to: usize,
                              progress_in_percentage_fn: &dyn Fn() -> usize) { ... }

I call it from C like this:

typedef long long usize; // 64 bit
extern void show_loading_animation(usize, usize, usize (*prog_fn)());

usize progress_reporter() {
    return 80l;
}

int main(void) {
    show_loading_animation(0, 100, &progress_reporter);
    return 0;
}

I assume &dyn Fn() is not compatible with a c function reference? I debugged into show_loading_animation with gdb. This is the assembly and the last line is where it crashes.

0000000000004530 <show_loading_animation>:
4530:       55                      push   %rbp
4531:       41 57                   push   %r15
4533:       41 56                   push   %r14
4535:       41 55                   push   %r13
4537:       41 54                   push   %r12
4539:       53                      push   %rbx
453a:       48 81 ec f8 00 00 00    sub    $0xf8,%rsp
4541:       48 89 94 24 e0 00 00    mov    %rdx,0xe0(%rsp)
4548:       00 
4549:       48 89 34 24             mov    %rsi,(%rsp)
454d:       48 39 fe                cmp    %rdi,%rsi
4550:       0f 82 a9 03 00 00       jb     48ff <show_loading_animation+0x3cf>
4556:       48 89 fb                mov    %rdi,%rbx
4559:       48 8b 41 18             mov    0x18(%rcx),%rax
455d:       48 89 84 24 d8 00 00    mov    %rax,0xd8(%rsp)
4564:       00 
4565:       45 31 f6                xor    %r14d,%r14d
4568:       48 8d 6c 24 30          lea    0x30(%rsp),%rbp
456d:       0f 1f 00                nopl   (%rax)
4570:       48 8b bc 24 e0 00 00    mov    0xe0(%rsp),%rdi
4577:       00 
4578:       ff 94 24 d8 00 00 00    callq  *0xd8(%rsp) <-- crash
phip1611
  • 5,460
  • 4
  • 30
  • 57
  • try to box it so it trait object will be heap allocated? – Shirshak55 Jun 19 '20 at 12:29
  • Note: The `&` in show_loading_animation(0, 100, &progress_reporter);` is not necessary: using the function name can only mean its address (contrary to `progress_reporter()'` which would mean to call it). – Paul Ogilvie Jun 19 '20 at 12:37
  • 1
    Does this answer your question? [How do I create a Rust callback function to pass to a FFI function?](https://stackoverflow.com/questions/31463426/how-do-i-create-a-rust-callback-function-to-pass-to-a-ffi-function) – E_net4 Jun 19 '20 at 12:56

2 Answers2

3

&dyn T is a fat pointer (basically a pointer to data and a pointer to vtable), C knows nothing about it internal structure so you need to use type Fn = extern "C" fn() -> usize instead.

Kitsu
  • 3,166
  • 14
  • 28
  • Okay I try but I wanted to keep my Rust code free from C (like "extern C") so that I can use the lib from both, rust programs and c programs as smooth and easy as possible. – phip1611 Jun 19 '20 at 12:35
  • 1
    You probably should have another wrapper library for the C layer anyway. Very little idiomatic Rust functions have a C-compatible interface, most libraries should not use `#[no_mangle]` and libraries that do should have complicated names to avoid conflicts. – mcarton Jun 19 '20 at 12:41
  • 2
    Well, you must use `extern "C"` to be able to interop with C, there is no other way. But you still can wrap this functions in Rust for better experience. – Kitsu Jun 19 '20 at 12:45
  • I implemented another function that consumes `extern "C" fn() -> usize`. This way it works! I added it as an answer to the question. Thanks y'all! – phip1611 Jun 19 '20 at 13:13
0

I could solve it this way. I provide two functions now, one for rust, one for FFI.

// translates extern c function reference to closure function for rust
#[no_mangle]
pub fn show_loading_animation_ffi(from: usize,
                                  to: usize,
                                  progress_in_percentage_fn: extern "C" fn() -> usize) {
    let fn_closure = || {
        progress_in_percentage_fn()
    };
    show_loading_animation(from, to, &fn_closure);
}

I removed #[no_mangle] from the pure rust function (see code above).

phip1611
  • 5,460
  • 4
  • 30
  • 57