Unable to export not mangled symbol in an executable as libraries can do

cb60702
Opened by Maxxie at 2021-08-03 23:49:59

trying something like this:

#[no_mangle]
pub extern "C" fn foo()
{
    //...stuff...
}

foo() seems to get exported if compiled inside a library, but if compiled in a binary crate it doesn't. I found this out trying to use gtk builder's "gtk_builder_connect_signals", which requires for not mangled symbols to be exported. Is this behaviour a bug? or is it a design choice? if so, why?

  1. I'm told by @Aatch that this works on nightly. Which version did you try with?

    Steve Klabnik at 2015-05-07 08:59:15

  2. This is actually possible in nightly, though I'm not sure if it's intentional. You can use the linkage attribute #[linkage = "<linkage>"], replacing "<linkage>" with one of the options on this list: http://llvm.org/docs/LangRef.html#linkage-types

    To export a symbol, you'll want to use "external".

    James Miller at 2015-05-07 08:59:22

  3. I'm also surprised that this doesn't work, I would expect this to work:

    $ cat foo.rs
    #[no_mangle]
    pub extern fn foo() {}
    fn main() {}
    $ rustc foo.rs
    $ nm -g foo | foo
    # should see the symbol `foo` defined here
    

    Alex Crichton at 2015-05-07 16:30:24

  4. @steveklabnik I tried this on one of the latest nightly (can't remember exactly which, surely not older than a week though)

    Maxxie at 2015-05-09 15:53:10

  5. @Aatch I just tried that, no luck (latest nightly).

    Maxxie at 2015-05-09 15:55:19

  6. Triaging:

    The following does not produce the foo symbol.

    cat > foo.rs <<EOF
    #[no_mangle]
    pub extern fn foo() {}
    fn main() {}
    EOF
    rustc foo.rs
    nm -g foo | grep foo
    # should see the symbol `foo` defined here
    

    Tested on rustc 1.7.0-nightly (d0bac3f14 2016-01-18)

    Bruno Tavares at 2016-01-18 21:17:27

  7. Taking another look at this after some time I think it's not the exporting symbol part that's not working, but it's got something to do with #[allow(dead_code)], maybe it just suppresses the warning but will still refuse (silently) to compile in the dead code function. I'm thinking this because if I link with a C library requiring the function it's compiled and exported. To test this I used two identical functions, both calling println!() but with different messages. The one function that's needed by the C library works when called from the C library and will have its message in the strings of the program, but the other message from the function not needed by the C library won't be found. Perhaps extern functions shouldn't be considered dead code in any case

    Maxxie at 2016-01-21 18:18:42

  8. To support (I hope) the above:

    cat > foo.rs <<EOF
    #[no_mangle]
    pub extern fn foo() {}
    fn main() {foo();}
    EOF
    rustc foo.rs
    nm -g foo | grep foo
    

    The code produces the symbol line, it seems that referencing the function makes it being exported properly, if I understand correctly. If I remove no_mangle and pub extern from the code above nm no longer gives the symbol in the output

    rustc 1.23.0-nightly (8b22e70b2 2017-10-31)

    Cyryl Płotnicki at 2017-11-01 13:54:13

  9. It looks like exporting top-level functions is supported now, but re-exports are still DCE'd.

    #[no_mangle]
    pub use my_helper_lib::exported_fn; // not exported
    
    #[no_mangle]
    extern "C" fn another_fn() {} // this is exported fine
    
    fn main() {
       let lib = libloading::Library::new("my_lib.so").unwrap();
    }
    

    Not even wrapping the exported_fn in test::black_box prevents DCE.

    Any update on this?

    Nick Hynes at 2019-01-27 21:13:49

  10. @nhynes that looks similar to rust-lang/rfcs#2771

    Daniel Sommermann at 2021-08-03 23:49:59