Calling c functions with larger structs by value fails

Here’s a minimized example of an issue I ran into, while working on the DuckDB Mojo bindings.

I have a c function that expects to be called by value with a struct containing more than 2 elements. Using DLHandle.call, it seems to be working only up to 2 struct elements (haven’t tried other element sizes than Int64 yet). With a struct size of more than 2 elements, I get a crash on macOS/garbage on Linux.

Any idea what I might be doing wrong here?

I already tried to add the @register_passable decorator to the struct, but it didn’t help with the issue.

simple.mojo:

from sys.ffi import DLHandle, os_is_macos

@value
struct simple2:
    var a: Int64
    var b: Int64

@value
struct simple3:
    var a: Int64
    var b: Int64
    var c: Int64

def main():
    if os_is_macos():
        lib = DLHandle("simple.dylib")
    else:
        lib = DLHandle("simple.so")

    lib.call["f2", NoneType, simple2](simple2(1,2))    # ok
    lib.call["f3", NoneType, simple3](simple3(1,2,3))  # boom

simple.c:

#include <stdio.h>

typedef struct {
    int64_t a;
    int64_t b;
} simple2;

typedef struct {
    int64_t a;
    int64_t b;
    int64_t c;
} simple3;

void f2(simple2 s) {
    printf("%lld, %lld\n", s.a, s.b);
    return;
}

void f3(simple3 s) {
    printf("%lld, %lld, %lld\n", s.a, s.b, s.c);
    return;
}
clang -shared -o simple.dylib -fPIC -g -O0 simple.c
mojo simple.mojo

1, 2
Please submit a bug report to https://github.com/modular/mojo/issues and include the crash backtrace along with all the relevant source codes.
Stack dump:
0.      Program arguments: mojo simple.mojo
mojo crashed!
Please file a bug report.
[86130:17607179:20250228,105129.504880:WARNING crash_report_exception_handler.cc:257] UniversalExceptionRaise: (os/kern) failure (5)
[1]    86128 segmentation fault  mojo simple.mojo

This issue looks very similar to what I’ve described above: FFI BUG when passing structs from mojo to c and sometimes from c to mojo.

It’s very possible that Mojo is padding simple3 out to 32 bytes for SIMD-related reasons.

1 Like

Thanks @owenhilyard. I gave it a try with a struct of 4 (and 8) ints but the result is still the same.

Interestingly, if I add @register_passable, and debug into the C function, I can see the values inside of some registers. The struct seems to point to an invalid address though: simple4 @ 0x1

Could someone from the @Modular team help out here? I’d be great to know if there is a way to pass larger structs by value to C/C++ with current Mojo somehow.