Linking to mangled Rust symbols


#1

How can I link to a function with Rust style mangled name? My framework/library requires a custom entry point for Rust, the actual entry point is somewhere inside linked C code. One way to do this is to declare:

extern "C" { 
    fn my_entry_point();
}

in the library and then specifiy:

#[no_mangle] 
pub extern "C" fn my_entry_point() { 
    /* ... */
}

in the user code.

However, there is absolutely no guarantee that the user uses the right signature for the entry point, which can lead to some extremely unsafe code. They could reference parameters that don’t exist or use the wrong calling convention and the linker will be unable to help out here.

A solution to this is to leave the entry point’s name and signature unspecified and supply a macro to the user that takes care of generating an entry point with the right name, signature and calling convention, like so:

my_entry_point! {
    /* ... */
}

But in my opinion this is less than ideal. I would like the user to be able to define the entry point like this (possibly even without pub similarly to how main is usually defined):

pub fn my_entry_point() {
    /* ... */
}

But unfortunately, from the library side there seems to be no way to go about to reference this function without explicitly stating the fully mangled name:

extern "Rust" {
    #[link_name = "<mangled_implementation_dependent_nonsense>"]
    fn my_entry_point();
}

It would be nice if there was an attribute like #[mangled] that could be used within extern "Rust" blocks to link to a function with Rust ABI using its mangled name and thus providing some level of type-safety for the linker, like so:

extern "Rust" {
    #[mangled]
    fn my_entry_point();
}

Is something like this possible? Or should I stick with the macro solution?


#2

I think the macro solution doesn’t look too bad.


#3

With #[proc_macro_attribute] you could define an attribute that would transform a function into something you can use.

#[lib_entry_point]
fn entry_point() {
  // [...]
}

That could be transformed however you want.
Perhaps to this:

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

This is a pattern I used for https://crates.io/crates/init


#4

Thanks for the responses. I chose to go with the macro approach for now, in the same way quick_main! is implemented in the error-chain crate. Custom attributes seem a little bit too much for this relatively simple problem.

It looks something like this:

rduino_main!(main);

fn main() -> ! {
    panic::set_hook(panic_hook);

    let serial_port = serial::monitor().unwrap_or_default();
    let mut serial = serial_port.begin().unwrap();
    serial.wait();
    loop {
        if serial.available() > 0 {
            serial.drain();
            writeln!(serial, "Hello world!").unwrap();
        }        
    }
}

Or alternatively with a closure:

rduino_main!(|| {
    panic::set_hook(panic_hook);
    
    /* ... */
});