LoginSignup
12
11

More than 5 years have passed since last update.

DynamicLibrary で実行時に共有ライブラリをロードする

Last updated at Posted at 2015-05-25

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 のコメントに記述があります。

12
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11