dlopen_rs/
dlopen.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use crate::{loader::ElfLibrary, Result};
use elf_loader::RelocatedDylib;
use hashbrown::HashMap;
use std::{env, fs::File, path::PathBuf, sync::OnceLock};

static LD_LIBRARY_PATH: OnceLock<Vec<PathBuf>> = OnceLock::new();
#[cfg(feature = "ldso")]
const SYS_LIBS: [&'static str; 4] = ["libc", "libgcc_s", "libstdc++", "libm"];

impl ElfLibrary {
    /// Load a shared library from a specified path
    ///
    /// # Note
    /// Please set the `RUST_LD_LIBRARY_PATH` environment variable before calling this function.
    /// dlopen-rs will look for dependent dynamic libraries in the paths saved in `RUST_LD_LIBRARY_PATH`.
    /// The way to set `RUST_LD_LIBRARY_PATH` is the same as `LD_LIBRARY_PATH`.
    ///
    /// # Example
    /// ```no_run
    /// use std::path::Path;
    /// use dlopen_rs::ELFLibrary;
    ///
    /// let path = Path::new("/path/to/library.so");
    /// let lib = ELFLibrary::dlopen(path).expect("Failed to load library");
    /// ```
    pub fn dlopen(path: impl AsRef<std::ffi::OsStr>) -> Result<RelocatedDylib> {
        LD_LIBRARY_PATH.get_or_init(|| {
            let ld_library_path =
                env::var("RUST_LD_LIBRARY_PATH").expect("env RUST_LD_LIBRARY_PATH");

            ld_library_path
                .split(":")
                .map(|str| PathBuf::try_from(str).unwrap())
                .collect()
        });
        let lib = ElfLibrary::from_file(path)?;
        let mut relocated_libs = HashMap::new();
        // 不支持循环依赖,relocated_libs的作用是防止一个库被多次重复加载
        fn load_and_relocate(
            lib: ElfLibrary,
            relocated_libs: &mut HashMap<String, RelocatedDylib>,
        ) -> Result<RelocatedDylib> {
            let needed_libs_name = lib.needed_libs();
            let mut needed_libs = vec![];
            for needed_lib_name in needed_libs_name {
                // 不需要加载其他动态链接器
                if needed_lib_name.contains("ld-") {
                    continue;
                }

                let find_needed_lib = |relocated_libs: &mut HashMap<String, RelocatedDylib>| {
                    // 像libc这种系统库需要使用系统的动态链接器加载,前提是开启ldso feature
                    #[cfg(feature = "ldso")]
                    for sys_lib_name in SYS_LIBS {
                        if needed_lib_name.contains(sys_lib_name) {
                            let lib = ElfLibrary::sys_load(needed_lib_name)?;
                            relocated_libs
                                .insert_unique_unchecked(needed_lib_name.to_string(), lib.clone());
                            return Ok(lib);
                        }
                    }

                    // 按RUST_LD_LIBRARY_PATH指定的路径依次寻找
                    for sys_path in LD_LIBRARY_PATH.get().unwrap() {
                        let file_path = sys_path.join(needed_lib_name);
                        match File::open(&file_path) {
                            Ok(file) => {
                                let new_lib =
                                    ElfLibrary::from_open_file(file, file_path.to_str().unwrap())?;
                                let lib = load_and_relocate(new_lib, relocated_libs)?;
                                relocated_libs.insert_unique_unchecked(
                                    needed_lib_name.to_string(),
                                    lib.clone(),
                                );
                                return Ok(lib);
                            }
                            Err(_) => continue,
                        }
                    }
                    Err(crate::Error::IOError {
                        err: std::io::Error::new(
                            std::io::ErrorKind::NotFound,
                            format!("can not find file: {}", needed_lib_name),
                        ),
                    })
                };

                let needed_lib = relocated_libs
                    .get(*needed_lib_name)
                    .cloned()
                    .unwrap_or(find_needed_lib(relocated_libs)?);

                needed_libs.push(needed_lib);
            }
            lib.relocate(needed_libs).finish()
        }
        load_and_relocate(lib, &mut relocated_libs)
    }
}