[dash-launcher] Load libraries from multiple packages
`library_loader` now supports multiple library directories.
`dash-launcher` now supplies libraries from two sources:
* `/pkg/lib` of the explored component
* `/pkg/lib` from the launcher namespace
This ensures that binaries packaged with the launcher
can run successfully.
Test: fx test library_loader
Change-Id: Ifcc9e8d785c417e275143b1314c9251e7196219a
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/693791
Reviewed-by: Shai Barack <shayba@google.com>
Reviewed-by: Gary Boone <gboone@google.com>
Commit-Queue: Xyan Bhatnagar <xbhatnag@google.com>
diff --git a/src/sys/lib/library_loader/src/lib.rs b/src/sys/lib/library_loader/src/lib.rs
index cd250c4..48ad1ea 100644
--- a/src/sys/lib/library_loader/src/lib.rs
+++ b/src/sys/lib/library_loader/src/lib.rs
@@ -39,9 +39,19 @@
/// `lib_proxy` must have been opened with at minimum OPEN_RIGHT_READABLE and OPEN_RIGHT_EXECUTABLE
/// rights.
pub fn start(lib_proxy: Arc<fio::DirectoryProxy>, chan: zx::Channel) {
+ start_with_multiple_dirs(vec![lib_proxy], chan);
+}
+
+/// start_with_multiple_dirs will expose the `fuchsia.ldsvc.Loader` service over the given channel,
+/// providing VMO buffers of requested library object names from any of the library directories in
+/// `lib_dirs`.
+///
+/// Each library directory must have been opened with at minimum OPEN_RIGHT_READABLE and
+/// OPEN_RIGHT_EXECUTABLE rights.
+pub fn start_with_multiple_dirs(lib_dirs: Vec<Arc<fio::DirectoryProxy>>, chan: zx::Channel) {
fasync::Task::spawn(
async move {
- let mut search_dirs = vec![lib_proxy.clone()];
+ let mut search_dirs = lib_dirs.clone();
// Wait for requests
let mut stream =
LoaderRequestStream::from_channel(fasync::Channel::from_channel(chan)?);
@@ -60,7 +70,7 @@
}
}
LoaderRequest::Config { config, responder } => {
- match parse_config_string(&lib_proxy, &config) {
+ match parse_config_string(&lib_dirs, &config) {
Ok(new_search_path) => {
search_dirs = new_search_path;
responder.send(zx::sys::ZX_OK)?;
@@ -72,7 +82,7 @@
}
}
LoaderRequest::Clone { loader, responder } => {
- start(lib_proxy.clone(), loader.into_channel());
+ start_with_multiple_dirs(lib_dirs.clone(), loader.into_channel());
responder.send(zx::sys::ZX_OK)?;
}
}
@@ -116,27 +126,37 @@
/// `//docs/concepts/booting/program_loading.md` for a description of the format. Returns the set
/// of directories which should be searched for objects.
pub fn parse_config_string(
- dir_proxy: &Arc<fio::DirectoryProxy>,
+ lib_dirs: &Vec<Arc<fio::DirectoryProxy>>,
config: &str,
) -> Result<Vec<Arc<fio::DirectoryProxy>>, Error> {
if config.contains("/") {
return Err(format_err!("'/' character found in loader service config string"));
}
+ let mut search_dirs = vec![];
if Some('!') == config.chars().last() {
- let sub_dir_proxy = fuchsia_fs::open_directory(
- dir_proxy,
- &Path::new(&config[..config.len() - 1]),
- fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
- )?;
- Ok(vec![sub_dir_proxy.into()])
+ // Only search the subdirs.
+ for dir_proxy in lib_dirs {
+ let sub_dir_proxy = fuchsia_fs::open_directory(
+ dir_proxy,
+ &Path::new(&config[..config.len() - 1]),
+ fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
+ )?;
+ search_dirs.push(Arc::new(sub_dir_proxy));
+ }
} else {
- let sub_dir_proxy = fuchsia_fs::open_directory(
- dir_proxy,
- &Path::new(config),
- fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
- )?;
- Ok(vec![sub_dir_proxy.into(), dir_proxy.clone()])
+ // Search the subdirs and the root dirs.
+ for dir_proxy in lib_dirs {
+ let sub_dir_proxy = fuchsia_fs::open_directory(
+ dir_proxy,
+ &Path::new(config),
+ fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
+ )?;
+ search_dirs.push(Arc::new(sub_dir_proxy));
+ }
+
+ search_dirs.append(&mut lib_dirs.clone());
}
+ Ok(search_dirs)
}
#[cfg(test)]
@@ -256,4 +276,47 @@
}
Ok(())
}
+
+ #[fasync::run_singlethreaded(test)]
+ async fn load_objects_multiple_dir_test() -> Result<(), Error> {
+ // This /pkg/lib/config_test/ directory is added by the build rules for this test package,
+ // since we need a directory that supports OPEN_RIGHT_EXECUTABLE. It contains a file 'foo'
+ // which contains 'hippos' and a file 'bar/baz' (that is, baz in a subdirectory bar) which
+ // contains 'rule'.
+ // TODO(fxbug.dev/37534): Use a synthetic /pkg/lib in this test so it doesn't depend on the
+ // package layout once Rust vfs supports OPEN_RIGHT_EXECUTABLE
+ let pkg_lib_1 = fuchsia_fs::open_directory_in_namespace(
+ "/pkg/lib/config_test/",
+ fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
+ )?;
+ let pkg_lib_2 = fuchsia_fs::open_directory_in_namespace(
+ "/pkg/lib/config_test/bar",
+ fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
+ )?;
+
+ let (loader_proxy, loader_service) = fidl::endpoints::create_proxy::<LoaderMarker>()?;
+ start_with_multiple_dirs(
+ vec![pkg_lib_1.into(), pkg_lib_2.into()],
+ loader_service.into_channel(),
+ );
+
+ for (obj_name, should_succeed) in vec![
+ // Should be able to access foo from dir #1
+ ("foo", true),
+ // Should be able to access baz from dir #2
+ ("baz", true),
+ // Should not be able to access bar (it's a directory)
+ ("bar", false),
+ ] {
+ let (res, o_vmo) = loader_proxy.load_object(obj_name).await?;
+ if should_succeed {
+ assert_eq!(zx::sys::ZX_OK, res, "loading {} did not succeed", obj_name);
+ assert!(o_vmo.is_some());
+ } else {
+ assert_ne!(zx::sys::ZX_OK, res, "loading {} did not fail", obj_name);
+ assert!(o_vmo.is_none());
+ }
+ }
+ Ok(())
+ }
}
diff --git a/src/sys/test_runners/lib_loader_cache/src/loader_cache.rs b/src/sys/test_runners/lib_loader_cache/src/loader_cache.rs
index 4a51168..983a7f5 100644
--- a/src/sys/test_runners/lib_loader_cache/src/loader_cache.rs
+++ b/src/sys/test_runners/lib_loader_cache/src/loader_cache.rs
@@ -119,7 +119,7 @@
}
LoaderRequest::Config { config, responder } => {
match library_loader::parse_config_string(
- &lib_loader_cache.lib_proxy,
+ &vec![lib_loader_cache.lib_proxy.clone()],
&config,
) {
Ok(new_search_path) => {
diff --git a/src/sys/tools/dash-launcher/src/launch.rs b/src/sys/tools/dash-launcher/src/launch.rs
index 8229475..a1fd588 100644
--- a/src/sys/tools/dash-launcher/src/launch.rs
+++ b/src/sys/tools/dash-launcher/src/launch.rs
@@ -190,6 +190,18 @@
Ok((stdin, stdout, stderr))
}
+fn get_lib_from_launcher_namespace() -> Result<fio::DirectoryProxy, LauncherError> {
+ let (lib_dir, server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>().unwrap();
+ fuchsia_fs::node::connect_in_namespace(
+ "/pkg/lib",
+ fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
+ server.into_channel(),
+ )
+ .map_err(|_| LauncherError::Internal)?;
+
+ Ok(lib_dir)
+}
+
fn create_dash_handles(
job: &zx::Job,
stdin: zx::Handle,
@@ -219,14 +231,20 @@
id: HandleId::new(HandleType::DefaultJob, 0).as_raw(),
};
- let ldsvc = if let Some(lib_dir) = lib_dir {
- let (ldsvc, server_end) = zx::Channel::create().map_err(|_| LauncherError::Internal)?;
- library_loader::start(Arc::new(lib_dir), server_end);
- ldsvc.into_handle()
- } else {
- // Use the default loader in the dash-launcher process
- fuchsia_runtime::loader_svc().map_err(|_| LauncherError::Internal)?
- };
+ // Create a library loader that uses the component's /pkg/lib dir first and
+ // falls back to the launcher's /pkg/lib dir if needed.
+ let mut lib_dirs = vec![];
+ if let Some(lib_dir) = lib_dir {
+ lib_dirs.push(Arc::new(lib_dir));
+ }
+
+ let launcher_lib_dir = get_lib_from_launcher_namespace()?;
+ lib_dirs.push(Arc::new(launcher_lib_dir));
+
+ let (ldsvc, server_end) = zx::Channel::create().map_err(|_| LauncherError::Internal)?;
+ let ldsvc = ldsvc.into_handle();
+ library_loader::start_with_multiple_dirs(lib_dirs, server_end);
+
let ldsvc_handle =
HandleInfo { handle: ldsvc, id: HandleId::new(HandleType::LdsvcLoader, 0).as_raw() };