ABI - Use Rust code from C
I just followed “The Rust Reference: 17. Application Binary Interface (ABI)” and “The Embedded Rust Book: 10.2 A little Rust with your C”
After then, I tried to expose Rust program to C as ABI.
Test the code in the first reference
foo.rs
:
// This is kept because of `#[used]`:
#[used]
static FOO: u32 = 123;
// This is removable because it is unused:
#[allow(dead_code)]
static BAR: u32 = 0;
// This is kept because it is publicly reachable:
pub static BAZ: u32 = 0;
// This is kept because it is referenced by a public, reachable function:
static QUUX: u32 = 0;
pub fn quux() -> &'static u32 {
&QUUX
}
// This is removable because it is referenced by a private, unused (dead) function:
static CORGE: u32 = 0;
#[allow(dead_code)]
fn corge() -> &'static u32 {
&CORGE
}
used
attribute
The attribute can only be applied to static
item.
This attribute tells compiler to keep this variable, even the variable isn’t used and is room for optimization in case of shared object output.
no_mangle
attribute
Name mangling is also calledd name decoration (by Wikipedia).
Name mangling append the variable/function name in the output.
The Rust compiler mangles symbol names differently than native code linkers expect, and this is why we need no_mangle
.
link_section
attribute
The attribute specifies the section of the object file that a function or static’s content will be placed into.
#[no_mangle]
#[link_section = ".example_section"]
pub static VAR1: u32 = 1;
export_name
attribute
This attribute specifies the name of the symbol that will be exported on a function or static.
#[export_name = "exported_symbol_name"]
pub fn name_in_rust() { }
First, just follow Official document
$ rustc -O --emit=obj --crate-type=rlib foo.rs
$ nm -C foo.o
0000000000000000 R foo::BAZ
0000000000000000 r foo::FOO
0000000000000000 R foo::QUUX
0000000000000000 T foo::quux
-O
: Optimize the code. A synonym for -C opt-level=2.--emit-obj
: Generates a native object file. The default output filename isCRATE_NAME.o
..--crate-type=rlib
: A “Rust library” file will be produced. This is used as an intermediate artifact and can be thought of as a “static Rust library”..
Another file type: staticlib
Tried with another --crate-type
:
rustc -O --emit=obj --crate-type=staticlib foo.rs
--crate-type=staticlib
: A static system library will be produced. … This format is recommended for use in situations such as linking Rust code into an existing non-Rust application because it will not have dynamic dependencies on other Rust code..
The result:
➜ nm -C foo.o
0000000000000000 r foo::FOO
➜ objdump -D -C --demangle foo.o
foo.o: file format elf64-x86-64
Disassembly of section .rodata._ZN3foo3FOO17hbcfcd8524a307ff1E:
0000000000000000 <foo::FOO>:
0: 7b 00 jnp 2 <foo::FOO+0x2>
...
Hex 7b
is 123
.
Export Rust function to C
Now, I tried the second reference.
https://docs.rust-embedded.org/book/interoperability/rust-with-c.html
AS of Jan. 2022, C++ has no stable ABI for the Rust compiler to target, so we try C.
lib.rs
:
static FOO: usize = 123;
#[no_mangle]
#[export_name = "mylib"]
pub extern "C" fn rust_adder(a: &usize, b: &usize) -> usize {
*a + *b + FOO
}
test.c
:
#include <stdio.h>
int mylib(int *a, int *b);
int main() {
int i = 1000;
int j = 10000;
int result = mylib(&i, &j);
printf("Result: %d", result);
}
Compile, link, and execute!!:
rustc -O --emit=obj --crate-type=staticlib lib.rs
gcc -o rust_with_c test.c lib.o
./rust_with_c
Result: 11123