mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-03-26 22:23:14 +00:00
Nim's code generator transforms function signatures involving large structs
in two ways that conflict with the standard C ABI:
- Return values of large structs (> register size): Nim emits a void
function with an explicit out-pointer appended as the *last* argument.
The standard x86-64 SysV ABI passes the hidden return pointer in RDI
(before the real arguments); ARM64 aapcs64 uses X8. Calling Rust
directly from Nim therefore puts the pointer in the wrong register /
stack slot on both architectures, causing crashes.
- Large struct parameters (> ~24 bytes): Nim passes a pointer rather
than copying bytes on the stack / into registers as the C ABI expects.
This commit introduces a thin C shim (nim_shims.c) that acts as a
translation layer:
- Each nim_* wrapper is declared with a Nim-compatible signature, so
Nim calls it correctly by its own rules.
- Inside the wrapper the C compiler calls the real Rust-exported
function using the standard C ABI, inserting the correct hidden-
pointer placement and stack-copy behaviour for the current platform.
As a result:
- The Rust API stays standard C ABI (return T by value; destroy takes
*mut T, which is pointer-sized and matches Nim's large-param transform).
- Other language bindings (C, Swift, Go, …) call Rust directly without
any shim — the standard ABI is preserved for them.
- The fix is correct on both x86-64 and ARM64 without any
architecture-specific code in Nim or Rust.
Changes:
- nim-bindings/src/nim_shims.c: C bridge with nim_* wrappers for all
create/handle/installation_name and destroy functions
- nim-bindings/src/bindings.nim: {.compile: "nim_shims.c"}, proc
signatures use natural return-by-value form, importc names point to
the nim_* shims
- nim-bindings/src/libchat.nim: call sites use natural let binding form;
destroy calls pass addr res (ptr T)
- conversations/src/api.rs: destroy functions take *mut T so Nim's
large-param-to-pointer transform is satisfied without a stack copy