getditto/safer_ffi

Exports from some public mods aren't included in headers

Closed this issue · 2 comments

Check the attachments for a complete reproduction Cargo project.

I have a lib.rs file that re-exports some crate modules. Like this:

use ::safer_ffi::prelude::*;

pub mod my_mod;

#[ffi_export]
pub fn init() {  
}

Within the exported mod (my_mod.rs), I have an item called tokenizer, which does not end up in the generated header, in spite of it being #[ffi_export]. This is my primary complaint.

However, the plot thickens because, if I then uncomment useless_enum from the same file, both items are now included in the generated header.

Here is a complete repro case, just as described. It follows the same structure as the QuickStart example, so you can generate the header by running:

cargo build
cargo run --features headers --bin generate-headers

safer_ffi_bug_regression.zip

Thank you

This seems to be dtolnay/inventory#52

  • (safer-ffi uses inventory under the hood to handle the list/collection of #[ffi_export]ed items for which to generate headers; the following static is probably being removed by the linker):

    #[used]
    #[doc(hidden)]
    #[link_section = "__DATA,__mod_init_func"]
    static __init6873130405561783045___rust_ctor___ctor
    Full inventory output
    #[allow(non_upper_case_globals)]
    extern fn __init6873130405561783045() {
        ::safer_ffi::inventory::submit({
            ::safer_ffi::FfiExport({
                #[allow(unused_parens, clippy::all)]
                fn typedef(
                    definer: &'_ mut dyn ::safer_ffi::headers::Definer,
                ) -> ::safer_ffi::std::io::Result<()> {
                    <<tokenizer as ::safer_ffi::layout::ReprC>::CLayout as ::safer_ffi::layout::CType>::c_define_self(
                        definer,
                    )
                }
                typedef
            })
        });
    }
    #[used]
    #[allow(non_upper_case_globals)]
    #[doc(hidden)]
    #[link_section = "__DATA,__mod_init_func"]
    static __init6873130405561783045___rust_ctor___ctor: unsafe extern "C" fn() = {
        unsafe extern "C" fn __init6873130405561783045___rust_ctor___ctor() {
            __init6873130405561783045()
        }
        __init6873130405561783045___rust_ctor___ctor
    };

From that issue, and https://github.com/dtolnay/inventory/pull/57/files, it is mentioned that using codegen-units = 1, at least when on macOS, may work around the issue. I've tested just that, and it seems to work:

# On macOS
CARGO_PROFILE_DEV_CODEGEN_UNITS=1 cargo r --features=headers
  • or:

    # Cargo.toml
    [profile.dev]
    codegen-units = 1

Alas, I don't know of a reliable way to handle this; in order for the header generation of #[ffi_export] to work, a way to globally collect header generation snippets is necessary, and at the moment, there are only two crates that offer this: ::inventory and ::linkme, and both rely on linker shenanigans, which can be brittle to certain linkers agressively removing stuff. It's thus kind of beyond my capacity to properly fix this 😔

So I'll have to close this issue, but feel free to post any further questions you may have, or ideas related to this 🙂