std::dynamic_lib::DynamicLibrary
を使って、実行時に共有ライブラリを読み込む方法
共有ライブラリ(C 側)
共有ライブラリがわはシンプルに 1 を返す関数を実装
int one() {
return 1;
}
./libtest.so
としてコンパイルしておく。
Rust側
先にソースコードを示すと以下の通り。
#![feature(std_misc)]
#![feature(convert)]
use std::env;
use std::path::Path;
use std::dynamic_lib::DynamicLibrary;
fn main() {
let dylib = match DynamicLibrary::open(
Some(Path::new(env::args().nth(1).expect("no args").as_str()))
) {
Ok(x) => {x}
Err(x) => {panic!(x)}
};
unsafe {
let one = std::mem::transmute::<*mut u8, extern "C" fn() -> u32>(dylib.symbol("one").ok().expect("error"));
println!("{}", one());
}
}
ポイントは、DynamicLibrary.symbol の戻り値を std::mem::transmuteでキャストする点。これがわからなくて、コアダンプしまくった。(正直、ドキュメントに書いておいてほしいところ)
symbolの戻り値をキャストする
let one = std::mem::transmute::<*mut u8, extern "C" fn() -> u32>(dylib.symbol("one").ok().expect("error"));
println!("{}", one());
実行結果
$ cargo run $PWD/libtest.so one
1
$
番外編:rustで記述された関数を呼び出してみる(実験)
rustでは関数名はマングリングされるため、ただのお遊びです。
(rustでpluginを実現するための仕組みはまだ確立していないだろうし)
ほんとは、trait継承させた関数をロードしたかったのだけど、試す時間がないため、通常の関数をロードしてみる。
呼び出される関数側
なにはともあれ、呼び出される側のパッケージを作成しておく。
$ cargo new plugin
Cargo.toml で crate-type を指定しておく
plugin/Cargo.toml
[package]
name = "plugin"
version = "0.1.0"
authors = ["Osamu NAKAMURA <osamu0329nakamura@gmail.com>"]
[lib]
name = "plugin"
crate-type = ["dylib"]
plugin/src/lib.rs
pub fn hello(s: &str) {
println!("{}", s)
}
ビルドすると target/debug/libplugin.so が生成されるので、nm
でシンボル名を確認しておく。
$ nm target/debug/libplugin.so | grep hello
00000000003cf120 d _ZN5hello15__STATIC_FMTSTR20he86eaad7e5625859saaE
00000000000b3580 T _ZN5hello20h9886c09a1f174ff1eaaE
呼び出す側
こちらはバイナリテンプレートで作成する。(省略)
先程確認したシンボルをハードコードしています。
main.rs
#![feature(std_misc)]
use std::dynamic_lib::DynamicLibrary;
fn main() {
let path = Some(std::path::Path::new("../plugin/target/debug/libplugin.so"));
let dylib = match DynamicLibrary::open(path) {
Ok(lib) => {lib}
Err(x) => {panic!("{}", x)}
};
let f = unsafe {
//let sym = "_ZN13create_struct20hd017fab034f9fd02NbaE";
let sym = "_ZN5hello20h9886c09a1f174ff1eaaE";
match dylib.symbol(sym) {
Ok(f) => {std::mem::transmute::<*mut u8, fn(&str) -> ()>(f)}
Err(s) => panic!(s)
}
};
f("Hello world!");
}
んで、実行する。
$ cargo run
Hello world!
関数名のマングリングについては、librustc_trans/back/link.rs のコメントに記述があります。