wasmer_wasix/state/linker/
locator.rs1use std::{
2 borrow::Cow,
3 ffi::OsStr,
4 path::{Path, PathBuf},
5};
6
7use shared_buffer::OwnedBuffer;
8use tracing::trace;
9use virtual_fs::{AsyncReadExt, FileSystem, FsError};
10
11use crate::{WasiFs, fs::WasiFsRoot};
12
13use super::{LinkError, LocateModuleError};
14
15const DEFAULT_RUNTIME_PATH: [&str; 3] = ["/lib", "/usr/lib", "/usr/local/lib"];
16
17pub(super) async fn locate_module(
18 module_path: &Path,
19 library_path: &[impl AsRef<Path>],
20 runtime_path: &[impl AsRef<str>],
21 calling_module_path: Option<impl AsRef<Path>>,
22 fs: &WasiFs,
23) -> Result<(PathBuf, OwnedBuffer), LinkError> {
24 async fn try_load(
25 fs: &WasiFsRoot,
26 path: impl AsRef<Path>,
27 ) -> Result<(PathBuf, OwnedBuffer), FsError> {
28 let mut file = match fs.new_open_options().read(true).open(path.as_ref()) {
29 Ok(f) => f,
30 Err(_) if path.as_ref().extension() == Some(OsStr::new("so")) => fs
33 .new_open_options()
34 .read(true)
35 .open(path.as_ref().with_extension("wasm"))?,
36 Err(e) => return Err(e),
37 };
38
39 let buf = if let Some(buf) = file.as_owned_buffer() {
40 buf
41 } else {
42 let mut buf = Vec::new();
43 file.read_to_end(&mut buf).await?;
44 OwnedBuffer::from(buf)
45 };
46
47 Ok((path.as_ref().to_owned(), buf))
48 }
49
50 if module_path.is_absolute() {
51 trace!(?module_path, "Locating module with absolute path");
52 try_load(&fs.root_fs, module_path).await.map_err(|e| {
53 LinkError::SharedLibraryMissing(
54 module_path.to_string_lossy().into_owned(),
55 LocateModuleError::Single(e),
56 )
57 })
58 } else if module_path.components().count() > 1 {
59 trace!(?module_path, "Locating module with relative path");
60 try_load(
61 &fs.root_fs,
62 fs.relative_path_to_absolute(module_path.to_string_lossy().into_owned()),
63 )
64 .await
65 .map_err(|e| {
66 LinkError::SharedLibraryMissing(
67 module_path.to_string_lossy().into_owned(),
68 LocateModuleError::Single(e),
69 )
70 })
71 } else {
72 trace!(
76 ?module_path,
77 "Locating module by name in default runtime path"
78 );
79
80 let calling_module_dir = calling_module_path
81 .as_ref()
82 .map(|p| p.as_ref().parent().unwrap_or_else(|| p.as_ref()));
83
84 let runtime_path = runtime_path.iter().map(|path| {
85 let path = path.as_ref();
86
87 let relative = path
88 .strip_prefix("$ORIGIN")
89 .or_else(|| path.strip_prefix("${ORIGIN}"));
90
91 match relative {
92 Some(relative) => {
93 let Some(calling_module_dir) = calling_module_dir else {
94 panic!(
100 "Internal error: $ORIGIN or ${{ORIGIN}} in RUNPATH, but \
101 no calling module path provided"
102 );
103 };
104 Cow::Owned(PathBuf::from(
105 fs.relative_path_to_absolute(
106 calling_module_dir
107 .join(relative)
108 .to_string_lossy()
109 .into_owned(),
110 ),
111 ))
112 }
113 None => Cow::Borrowed(Path::new(path)),
114 }
115 });
116
117 let search_paths = library_path
119 .iter()
120 .map(|path| Cow::Borrowed(path.as_ref()))
121 .chain(runtime_path)
122 .chain(
123 DEFAULT_RUNTIME_PATH
124 .iter()
125 .map(|path| Cow::Borrowed(Path::new(path))),
126 );
127
128 let mut errors: Vec<(PathBuf, FsError)> = Vec::new();
129 for path in search_paths {
130 let full_path = path.join(module_path);
131 trace!(search_path = ?path, full_path = ?full_path, "Searching module");
132 match try_load(&fs.root_fs, &full_path).await {
133 Ok(ret) => {
134 trace!(?module_path, full_path = ?ret.0, "Located module");
135 return Ok(ret);
136 }
137 Err(e) => errors.push((full_path, e)),
138 };
139 }
140
141 trace!(?module_path, "Failed to locate module");
142 Err(LinkError::SharedLibraryMissing(
143 module_path.to_string_lossy().into_owned(),
144 LocateModuleError::Multiple(errors),
145 ))
146 }
147}