Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suitability for opaque-types(-only) FFI types #492

Open
chrysn opened this issue Mar 16, 2020 · 4 comments
Open

Suitability for opaque-types(-only) FFI types #492

chrysn opened this issue Mar 16, 2020 · 4 comments

Comments

@chrysn
Copy link

chrysn commented Mar 16, 2020

A library I'm currently binding to Rust (liboscore) has something of a compile-time module system (think back-end, not plug-in; there's only one back-end at a compile time) that works like this:

  • The library has a header file of functions the module needs to provide.
  • Some of those types used in that header are not defined by the library.
  • It is up to the module to define those in an extra header; they need to be complete types as the library (and, by transitivity through struct memberships, its user) can allocate instances with a fixed size on the stack.
  • Those types are opaque to the library, so obviously there are initialization functions in the to-be-provided functions list.

Is this a use case cbindgen aims to support?

If so, there's a few things I'd need and could attempt to work on (but first is the question of whether this is desirable in the first place):

  • Declaring a struct as opaque to cbindgen could help work around FFI issues. (Eg. I'm running into a version of cbindgen panics with weird Result error #206, but didn't really try to dig down there yet as it's not like I actually need the heapless vec that's probably the culprit to be visible to C). For those types, any type with the right size and alignment will do – currently, as a workaround, I have things like typedef struct {uint64_t dontuse[16];} oscore_crypto_aead_encryptstate_t; in there.
  • The function signatures are exported in parallel to the types. That doesn't hurt and even helps the C compiler verify that everyone agrees on signatures, but they need to be exact; a size_t_is_usize workaround like in size_t vs usize rust-lang/rust-bindgen#1671 could help there (but probably just the code that mixes them should be fixed).
  • Something similar to #[link(name=...)] (edit: or #[export_name=...]?) for these types would be convenient, as it would allow the use of rustish type names throughout the code, which only get named mylibrary_mymodule_typename (from TypeName in Rust which has modules/namespaces) at export time.

edit: added reference to export_name item

@emilio
Copy link
Collaborator

emilio commented Mar 20, 2020

This seems generally reasonable though I'm not sure I've grasped all the details of what you want.

Declaring types as opaque should "just work" if you don't make them repr(C) or such.

The function signatures are exported in parallel to the types. That doesn't hurt and even helps the C compiler verify that everyone agrees on signatures, but they need to be exact; a size_t_is_usize workaround like in rust-lang/rust-bindgen#1671 could help there (but probably just the code that mixes them should be fixed).

Can you clarify? What functions are not getting exported right now (but should)?

Something similar to #[link(name=...)] (edit: or #[export_name=...]?) for these types would be convenient, as it would allow the use of rustish type names throughout the code, which only get named mylibrary_mymodule_typename (from TypeName in Rust which has modules/namespaces) at export time.

We have export.rename which I think would do what you want, right? Something like this.

We could somehow move it to the type declaration instead of the config maybe.

@chrysn
Copy link
Author

chrysn commented Mar 20, 2020

Declaring types as opaque should "just work" if you don't make them repr(C) or such.

The issue I'm having with them is that while I don't need access from C to their internals, I need them to be complete. They're used by the C library like this:

backendtype_t state;
backend_initialize(&state);
backend_work_on(&state, ...);

Making that backendtype_t repr(C) is not really feasible because it may contain all kinds of ZSTs and what so not you'd use when you don't expect to have to use a C API.

Can you clarify? What functions are not getting exported right now (but should)?

The other way 'round: the function signatures are exported, that's something I originally disliked (after all, the canonical signature is in the specified interface that comes from the C library) but now came to appreciate as the duplicate signatures don't hurt but provide additional verification.

But then, those types need to match precisely, this means

  • generating signatures with a size_t (in alignment with bindgen, cbindgen is mapping Rust's usize to uintptr_t)
  • Implementations like pub extern "C" fn initialize(state: &mut MaybeUninit<State>) { ... } need something like Support MaybeUninit and ManuallyDrop #406 if that even suffices, or workarounds (which would probably mean just making the function initialize(state: *mut State)).

We have export.rename which I think would do what you want, right?

That helps indeed, thanks, I missed it, that works for me now.

As you hinted, I'd prefer to have some declaration inline instead of cbindgen.toml lists, but anyway.

There's already the type my_c_style_type = RustType; way of specifying things, but that requires me to use a prefix to get all the Rust definitions out of the main C namespace, but then the my_c_style_type gets prefixed as well, and unlike function names I can't #[no_mangle] them to avoid the prefixing.
may even type oscore_x = X as long as X can be prefixed but well

@emilio
Copy link
Collaborator

emilio commented Mar 20, 2020

The issue I'm having with them is that while I don't need access from C to their internals, I need them to be complete. [...] Making that backendtype_t repr(C) is not really feasible because it may contain all kinds of ZSTs and what so not you'd use when you don't expect to have to use a C API.

Ah, that seems pretty hard to do. Cbindgen doesn't have access to rustc's internals so it can't know the type layout to generate something useful. Plus even if we had it, whatever it generates would be dependent on the rust version that was used at the time, because Rustc could change the layout to its will.

The other way 'round: the function signatures are exported, that's something I originally disliked (after all, the canonical signature is in the specified interface that comes from the C library) but now came to appreciate as the duplicate signatures don't hurt but provide additional verification.

Ah I see... Yeah I find it useful to catch mistakes, fwiw.

Implementations like pub extern "C" fn initialize(state: &mut MaybeUninit) { ... } need something like #406 if that even suffices, or workarounds (which would probably mean just making the function initialize(state: *mut State)).

Yeah, I think #406 is the right way to address this.

@aidanhs
Copy link
Contributor

aidanhs commented Nov 10, 2023

Just for cross referencing purposes since I've been skimming through related issues, #24 relates to the desire to have a way to automatically generate things like:

currently, as a workaround, I have things like typedef struct {uint64_t dontuse[16];} oscore_crypto_aead_encryptstate_t; in there

chrysn added a commit to coap-security/liboscore that referenced this issue Apr 17, 2024
(working, but not particularly good; for some issues see [492])

[492]: mozilla/cbindgen#492
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants