The problem here is that vlog_startup_routines is not a pointer. If you declare it as a pointer; it is an array. The symbol resolves to the address of the first item of the array. In C, if you have:
int i = 7;
int a[1] = { 8 };
int *p = &i;
then at the linker level, the symbol i is the address of the location containing the value 7, a is also the address of a location containing an integer value (8), and p is the address of a location containing a pointer to an integer. Another way of saying it is that the linker symbol is always the address of the variable.
If you declare it as:
// Or whatever argument types
type VlogStartupRoutine = extern "C" fn();
extern "C" {
static vlog_startup_routines: VlogStartupRoutine;
}
you're saying that vlog_startup_routines is a variable containing a function pointer, more like the C void *vlog_startup_routines.
unsafe {
println!("{:p}", vlog_startup_routines);
println!("{:p}", hello_register as *const ());
}
it's dereferencing taking the value stored at the address vlog_startup_routines, which is indeed the first pointer.
The correct (nearly) code is:
type VlogStartupRoutine = Option<extern "C" fn()>;
#[link(name = "funcref")]
extern "C" {
static vlog_startup_routines: [VlogStartupRoutine;10];
fn hello_register();
}
fn main() {
unsafe {
println!("{:p}", vlog_startup_routines.as_ptr());
println!("{:p}", hello_register as *const ());
}
unsafe {
let routine = vlog_startup_routines[0].unwrap();
println!("Calling startup");
routine();
assert!(vlog_startup_routines[1].is_none());
}
}
Note that I use Option<extern "C" fn()> for the nullable function pointer as described here.
This outputs, for me:
0x7efc27d37030
0x7efc27b366f0
Calling startup
hello_register called
The reason I say "nearly" is that I'm not sure how to say it's an unknown-sized array. :-)