Why is `#[no_mangle]` required to see Rust functions in WASM library?

61 views Asked by At

I created a new empty Rust library. I set the crate-type to cdylib so that .wasm files are generated. This is my lib.rs:

#[no_mangle]
pub extern fn fibonacci(n: usize) -> usize {
  if n < 2 {
     return n;
  }

  return fibonacci(n - 1) + fibonacci(n - 2);
}

If I remove #[no_mangle], then Cargo produces a WASM binary that doesn't even contain any code:

$ wasm-objdump -h -x target/wasm32-unknown-unknown/release/fibonacci.wasm

fibonacci.wasm: file format wasm 0x1

Sections:

    Table start=0x0000000a end=0x0000000f (size=0x00000005) count: 1
   Memory start=0x00000011 end=0x00000014 (size=0x00000003) count: 1
   Global start=0x00000016 end=0x0000002f (size=0x00000019) count: 3
   Export start=0x00000031 end=0x00000056 (size=0x00000025) count: 3
   Custom start=0x00000059 end=0x00000f5a (size=0x00000f01) ".debug_abbrev"
   Custom start=0x00000f5e end=0x0005c6df (size=0x0005b781) ".debug_info"
   Custom start=0x0005c6e3 end=0x00084461 (size=0x00027d7e) ".debug_ranges"
   Custom start=0x00084465 end=0x00118f6a (size=0x00094b05) ".debug_str"
   Custom start=0x00118f6e end=0x00152a48 (size=0x00039ada) ".debug_line"
   Custom start=0x00152a4a end=0x00152a63 (size=0x00000019) "name"
   Custom start=0x00152a65 end=0x00152ab2 (size=0x0000004d) "producers"
   Custom start=0x00152ab4 end=0x00152ae0 (size=0x0000002c) "target_features"

Section Details:

Table[1]:
 - table[0] type=funcref initial=1 max=1
Memory[1]:
 - memory[0] pages: initial=16
Global[3]:
 - global[0] i32 mutable=1 <__stack_pointer> - init i32=1048576
 - global[1] i32 mutable=0 <__data_end> - init i32=1048576
 - global[2] i32 mutable=0 <__heap_base> - init i32=1048576
Export[3]:
 - memory[0] -> "memory"
 - global[1] -> "__data_end"
 - global[2] -> "__heap_base"
Custom:
 - name: ".debug_abbrev"
Custom:
 - name: ".debug_info"
Custom:
 - name: ".debug_ranges"
Custom:
 - name: ".debug_str"
Custom:
 - name: ".debug_line"
Custom:
 - name: "name"
 - global[0] <__stack_pointer>
Custom:
 - name: "producers"
Custom:
 - name: "target_features"
  - [+] mutable-globals
  - [+] sign-ext

Is there any way to force the function to be included in the binary without using #[no_mangle]? Using extern by itself doesn't work.

2

There are 2 answers

0
prog-fh On BEST ANSWER

The reference says about no_mangle that it obviously disables name mangling, but moreover it is similar to the used attribute.
However, used only applies to static items, not functions, as visible in the example below.

Then, the only way to export your function implies disabling name mangling (which is reasonable in my opinion).

fn this_is_invisible(n: i32) -> i32 {
    n * 2 + 1
}

pub fn this_is_also_invisible(n: i32) -> i32 {
    n * 2 + 1
}

#[no_mangle]
fn this_is_accepted(n: i32) -> i32 {
    n * 2 + 1
}

/*
// ERROR: attribute must be applied to a `static` variable
#[used]
fn this_is_rejected(n: i32) -> i32 {
    n * 2 + 1
}
*/
$ nm target/debug/libmy_project.so | grep this_is
0000000000006e00 T this_is_accepted
1
Stargateur On

extern is equivalent to extern "C" meaning the ABI of this function will use the common ABI of the platform (yes I refuse to call it C ABI).

no_mangle have two purposes, first disable the mangle, aka transforming the name of the function. And it will mark this function as "to export". Yes, it's maybe confusing. But no_mangle can be read as export.

See more: