TL;DR
C使用dlopen
系统调用,Rust用libloading这个板条箱。dlopen的用法可以看手册(RTFM)
man dlopen
Example of Rust
示例:
use std::env;
use libloading::{Library, Symbol};
use std::ffi::CString;
use std::os::raw::c_char;
type AddFunc = unsafe fn(i32, i32) -> i32;
type MsgFunc = unsafe fn(*const c_char, *mut *mut c_char);
fn main() {
let library_path = env::args().nth(1).expect("USAGE: loading <LIB>");
unsafe {
let lib = Library::new(library_path).unwrap();
let func: Symbol<AddFunc> = lib.get(b"add").unwrap();
let answer = func(1, 2);
println!("1 + 2 = {}", answer);
let func2: Symbol<MsgFunc> = lib.get(b"echo2").unwrap();
let message = CString::new("helloworld").unwrap();
let mut message2: *mut c_char = 0x0 as *mut c_char;
func2(message.as_ptr(), &mut message2);
print!("{} => {}", message.to_str().unwrap(), CString::from_raw(message2).to_str().unwrap());
}
}
C代码
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int add(int a, int b) {
return a + b;
}
void echo(const char* input, char** output) {
int len = strlen(input);
*output = (char*)malloc(len + 1);
for (int i = 0; i < len; i++) {
(*output)[i] = input[i] + 1;
}
(*output)[len] = '\0';
}
void echo2(const char* input, char** output) {
int len = strlen(input);
*output = (char*)malloc(len + 1);
for (int i = 0; i < len; i++) {
(*output)[i] = toupper(input[i]);
}
(*output)[len] = '\0';
}
把C代码编译成动态链接库,其中-fPIC
表示位置无关,-shared
表示动态链接库
gcc -c -fPIC adder.c -o libadder.o
gcc -shared libadder.o -o libadder2.so
最后运行程序,得到结果如下
1 + 2 = 3
helloworld => HELLOWORLD
实际上rust代码之间也可以这样动态互调
#[no_mangle]
pub extern "C" fn add(a: isize, b: isize) -> isize {
a + b
}
编译,cdynlib
表示使用C ABI的动态链接库
rustc --crate-type cdylib adder.rs
Ref
- https://www.michaelfbryan.com/rust-ffi-guide/dynamic_loading.html
- https://github.com/nagisa/rust_libloading
- https://man7.org/linux/man-pages/man3/dlopen.3.html
- https://eli.thegreenplace.net/2012/08/24/plugins-in-c