From 5f4955a6aac0c20f774b907b1e5df356a31d08db Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Fri, 15 Oct 2021 14:25:32 +0200 Subject: [PATCH 1/4] ffi: Allow loading libraries without full path --- src/builtins.rs | 6 ++-- src/error/mod.rs | 6 ++++ src/ffi.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 92 insertions(+), 9 deletions(-) diff --git a/src/builtins.rs b/src/builtins.rs index 61918d0b..c37f123d 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::io::{self, Write}; +use crate::ffi; use crate::instance::{FromObjectInstance, ToObjectInstance}; use crate::{Context, Instruction, JkInt, JkString, ObjectInstance}; @@ -51,9 +52,8 @@ fn string_display_err(ctx: &mut Context, args: Args) -> Option { fn ffi_link_with(ctx: &mut Context, args: Args) -> Option { let lib_path = JkString::from_instance(&args[0].execute(ctx).unwrap()).0; - match unsafe { libloading::Library::new(lib_path) } { - Ok(lib) => ctx.add_lib(lib), - Err(e) => ctx.error(e.into()), + if let Err(e) = ffi::link_with(ctx, lib_path.into()) { + ctx.error(e); } None diff --git a/src/error/mod.rs b/src/error/mod.rs index c905683b..33d9caf6 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -194,4 +194,10 @@ impl std::convert::From for Error { } } +impl std::convert::From for Error { + fn from(e: std::env::VarError) -> Self { + Error::new(ErrKind::ExternFunc).with_msg(e.to_string()) + } +} + impl std::error::Error for Error {} diff --git a/src/ffi.rs b/src/ffi.rs index 1c9fd0a3..ce25aea9 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -2,9 +2,62 @@ //! Primitive types are converted to their C counterparts. //! FIXME +use std::path::{Path, PathBuf}; + use crate::instruction::{FunctionCall, FunctionDec}; use crate::{Context, ErrKind, Error, JkInt, ObjectInstance, ToObjectInstance}; +/// Fetch libraries from the path according to dlopen's (simplified) rules. +/// If the LD_LIBRARY_PATH variable is set and contains a colon, then assume it +/// contains a list of colon-separated directories and search through them. +/// Then, search through /lib/, and then finally through /usr/lib/ +fn lib_from_path(lib_path: PathBuf) -> Result { + if let Ok(lib) = lib_from_ld_library_path(&lib_path) { + return Ok(lib); + } + + let root_lib_path = PathBuf::from("/lib").join(&lib_path); + if root_lib_path.exists() { + if let Ok(lib) = unsafe { libloading::Library::new(root_lib_path) } { + return Ok(lib); + } + } + + let usr_root_lib_path = PathBuf::from("/usr/lib").join(&lib_path); + if usr_root_lib_path.exists() { + return unsafe { Ok(libloading::Library::new(usr_root_lib_path)?) }; + } + + Err(Error::new(ErrKind::ExternFunc)) +} + +/// Look through the paths specified in the LD_LIBRARY_PATH environment variable (if any) +fn lib_from_ld_library_path(lib_path: &Path) -> Result { + let paths = std::env::var("LD_LIBRARY_PATH")?; + + for dir in paths.split(':') { + let path = PathBuf::from(dir).join(&lib_path); + dbg!(&path); + if path.exists() { + return unsafe { Ok(libloading::Library::new(path)?) }; + } + } + + Err(Error::new(ErrKind::ExternFunc)) +} + +pub fn link_with(ctx: &mut Context, lib_path: PathBuf) -> Result<(), Error> { + let lib = if std::path::Path::new(&lib_path).exists() { + unsafe { libloading::Library::new(lib_path)? } + } else { + lib_from_path(lib_path)? + }; + + ctx.add_lib(lib); + + Ok(()) +} + pub fn execute( dec: &FunctionDec, call: &FunctionCall, @@ -58,15 +111,13 @@ mod tests { use crate::{JkInt, ToObjectInstance}; fn init_ctx() -> Context { - let mut i = jinko! { + jinko! { + link_with("./tests/fixtures/clib/lib.so"); + ext func no_arg() -> int; ext func square(v: int) -> int; ext func add(lhs: int, rhs: int) -> int; - }; - - i.add_lib(unsafe { libloading::Library::new("./tests/fixtures/clib/lib.so").unwrap() }); - - i + } } #[test] @@ -104,4 +155,30 @@ mod tests { assert_eq!(execute(&dec, &call, &mut i), None); } + + #[test] + fn load_libs_stress() { + let ld_library_path = std::env::var("LD_LIBRARY_PATH").unwrap(); + let pwd = std::env::var("PWD").unwrap(); + std::env::set_var( + "LD_LIBRARY_PATH", + format!("{}/{}:{}", pwd, "tests/fixtures/clib/", ld_library_path), + ); + + jinko! { + link_with("lib.so"); + }; + + // Load libc, probably from LD_LIBRARY_PATH + jinko! { + link_with("libc.so.6"); + }; + + std::env::remove_var("LD_LIBRARY_PATH"); + + // Load libc without LD_LIBRARY_PATH + jinko! { + link_with("libc.so.6"); + }; + } } From 3ab7c3aeba66e08f0aef3851a5382d189006cec4 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Fri, 15 Oct 2021 14:59:38 +0200 Subject: [PATCH 2/4] ffi: Do not unwrap in test when fetching LD_LIBRARY_PATH --- src/ffi.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index ce25aea9..4bb2bf21 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -17,6 +17,7 @@ fn lib_from_path(lib_path: PathBuf) -> Result { } let root_lib_path = PathBuf::from("/lib").join(&lib_path); + dbg!(&root_lib_path); if root_lib_path.exists() { if let Ok(lib) = unsafe { libloading::Library::new(root_lib_path) } { return Ok(lib); @@ -24,6 +25,7 @@ fn lib_from_path(lib_path: PathBuf) -> Result { } let usr_root_lib_path = PathBuf::from("/usr/lib").join(&lib_path); + dbg!(&usr_root_lib_path); if usr_root_lib_path.exists() { return unsafe { Ok(libloading::Library::new(usr_root_lib_path)?) }; } @@ -37,7 +39,6 @@ fn lib_from_ld_library_path(lib_path: &Path) -> Result Date: Fri, 15 Oct 2021 16:00:08 +0200 Subject: [PATCH 3/4] ffi: Search through /lib/*/ and /usr/lib/*/ for libraries --- src/builtins.rs | 5 +++-- src/ffi.rs | 42 ++++++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/builtins.rs b/src/builtins.rs index c37f123d..08f9ba5e 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::io::{self, Write}; +use std::path::PathBuf; use crate::ffi; use crate::instance::{FromObjectInstance, ToObjectInstance}; @@ -52,8 +53,8 @@ fn string_display_err(ctx: &mut Context, args: Args) -> Option { fn ffi_link_with(ctx: &mut Context, args: Args) -> Option { let lib_path = JkString::from_instance(&args[0].execute(ctx).unwrap()).0; - if let Err(e) = ffi::link_with(ctx, lib_path.into()) { - ctx.error(e); + if let Err(e) = ffi::link_with(ctx, PathBuf::from(&lib_path)) { + ctx.error(e.with_msg(format!("couldn't link with library `{}`", &lib_path))); } None diff --git a/src/ffi.rs b/src/ffi.rs index 4bb2bf21..11243a53 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -7,6 +7,34 @@ use std::path::{Path, PathBuf}; use crate::instruction::{FunctionCall, FunctionDec}; use crate::{Context, ErrKind, Error, JkInt, ObjectInstance, ToObjectInstance}; +fn find_lib(paths: &Vec, lib_path: &Path) -> Result { + for dir_base in paths.iter() { + let lib_path = PathBuf::from(dir_base).join(lib_path); + dbg!(&lib_path); + if lib_path.exists() { + if let Ok(lib) = unsafe { libloading::Library::new(lib_path) } { + return Ok(lib); + } + } + } + + Err(Error::new(ErrKind::ExternFunc)) +} + +fn lib_from_root(base: &Path, lib_path: &Path) -> Result { + // We search through all entries in the `base` directory as well as all the entries + // in the directories contained in `base`. + let mut dirs = vec![PathBuf::from(base)]; + for entry in base.read_dir()? { + let entry = entry?; + if entry.file_type()?.is_dir() { + dirs.push(entry.path()) + } + } + + find_lib(&dirs, lib_path) +} + /// Fetch libraries from the path according to dlopen's (simplified) rules. /// If the LD_LIBRARY_PATH variable is set and contains a colon, then assume it /// contains a list of colon-separated directories and search through them. @@ -16,18 +44,12 @@ fn lib_from_path(lib_path: PathBuf) -> Result { return Ok(lib); } - let root_lib_path = PathBuf::from("/lib").join(&lib_path); - dbg!(&root_lib_path); - if root_lib_path.exists() { - if let Ok(lib) = unsafe { libloading::Library::new(root_lib_path) } { - return Ok(lib); - } + if let Ok(lib) = lib_from_root(&PathBuf::from("/lib"), &lib_path) { + return Ok(lib); } - let usr_root_lib_path = PathBuf::from("/usr/lib").join(&lib_path); - dbg!(&usr_root_lib_path); - if usr_root_lib_path.exists() { - return unsafe { Ok(libloading::Library::new(usr_root_lib_path)?) }; + if let Ok(lib) = lib_from_root(&PathBuf::from("/usr/lib"), &lib_path) { + return Ok(lib); } Err(Error::new(ErrKind::ExternFunc)) From 7dd41e7314cf5523747e2c17ad4d648c6a69800d Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Fri, 15 Oct 2021 16:04:51 +0200 Subject: [PATCH 4/4] ffi: Fix clippy warnings --- src/ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ffi.rs b/src/ffi.rs index 11243a53..79b72bd6 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use crate::instruction::{FunctionCall, FunctionDec}; use crate::{Context, ErrKind, Error, JkInt, ObjectInstance, ToObjectInstance}; -fn find_lib(paths: &Vec, lib_path: &Path) -> Result { +fn find_lib(paths: &[PathBuf], lib_path: &Path) -> Result { for dir_base in paths.iter() { let lib_path = PathBuf::from(dir_base).join(lib_path); dbg!(&lib_path);