-
Notifications
You must be signed in to change notification settings - Fork 419
native-lib: allow multiple libraries and/or dirs #4372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,47 +87,52 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | |
| } | ||
|
|
||
| /// Get the pointer to the function of the specified name in the shared object file, | ||
| /// if it exists. The function must be in the shared object file specified: we do *not* | ||
| /// return pointers to functions in dependencies of the library. | ||
| /// if it exists. The function must be in one of the shared object files specified: | ||
| /// we do *not* return pointers to functions in dependencies of libraries. | ||
| fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option<CodePtr> { | ||
| let this = self.eval_context_mut(); | ||
| // Try getting the function from the shared library. | ||
| let (lib, lib_path) = this.machine.native_lib.as_ref().unwrap(); | ||
| let func: libloading::Symbol<'_, unsafe extern "C" fn()> = | ||
| unsafe { lib.get(link_name.as_str().as_bytes()).ok()? }; | ||
| #[expect(clippy::as_conversions)] // fn-ptr to raw-ptr cast needs `as`. | ||
| let fn_ptr = *func.deref() as *mut std::ffi::c_void; | ||
| // Try getting the function from one of the shared libraries. | ||
| for (lib, lib_path) in &this.machine.native_lib { | ||
| let Ok(func): Result<libloading::Symbol<'_, unsafe extern "C" fn()>, _> = | ||
| (unsafe { lib.get(link_name.as_str().as_bytes()) }) | ||
| else { | ||
| continue; | ||
| }; | ||
| #[expect(clippy::as_conversions)] // fn-ptr to raw-ptr cast needs `as`. | ||
| let fn_ptr = *func.deref() as *mut std::ffi::c_void; | ||
|
|
||
| // FIXME: this is a hack! | ||
| // The `libloading` crate will automatically load system libraries like `libc`. | ||
| // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 | ||
| // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the | ||
| // library if it can't find the symbol in the library itself. | ||
| // So, in order to check if the function was actually found in the specified | ||
| // `machine.external_so_lib` we need to check its `dli_fname` and compare it to | ||
| // the specified SO file path. | ||
| // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, | ||
| // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 | ||
| // using the `libc` crate where this interface is public. | ||
| let mut info = std::mem::MaybeUninit::<libc::Dl_info>::zeroed(); | ||
| unsafe { | ||
| if libc::dladdr(fn_ptr, info.as_mut_ptr()) != 0 { | ||
| let info = info.assume_init(); | ||
| #[cfg(target_os = "cygwin")] | ||
| let fname_ptr = info.dli_fname.as_ptr(); | ||
| #[cfg(not(target_os = "cygwin"))] | ||
| let fname_ptr = info.dli_fname; | ||
| assert!(!fname_ptr.is_null()); | ||
| if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap() | ||
| != lib_path.to_str().unwrap() | ||
| { | ||
| return None; | ||
| // FIXME: this is a hack! | ||
| // The `libloading` crate will automatically load system libraries like `libc`. | ||
| // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 | ||
| // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the | ||
| // library if it can't find the symbol in the library itself. | ||
| // So, in order to check if the function was actually found in the specified | ||
| // `machine.external_so_lib` we need to check its `dli_fname` and compare it to | ||
| // the specified SO file path. | ||
| // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, | ||
| // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 | ||
| // using the `libc` crate where this interface is public. | ||
| let mut info = std::mem::MaybeUninit::<libc::Dl_info>::zeroed(); | ||
| unsafe { | ||
| if libc::dladdr(fn_ptr, info.as_mut_ptr()) != 0 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The more concerning point is that we just ignore
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to panic on fail? With the current dir implementation it would allow e.g. passing a directory which also contains things that aren't libraries and it will just ignore those. For instance it could be a directory of random build artifacts from some library, where only some are actually .so files, and this will transparently ignore all other files
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I already implemented this in #4389. At least for single-file arguments I think it should definitely be an error if that file cannot be loaded. |
||
| let info = info.assume_init(); | ||
| #[cfg(target_os = "cygwin")] | ||
| let fname_ptr = info.dli_fname.as_ptr(); | ||
| #[cfg(not(target_os = "cygwin"))] | ||
| let fname_ptr = info.dli_fname; | ||
| assert!(!fname_ptr.is_null()); | ||
| if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap() | ||
| != lib_path.to_str().unwrap() | ||
| { | ||
| return None; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Return a pointer to the function. | ||
| Some(CodePtr(fn_ptr)) | ||
| // Return a pointer to the function. | ||
| return Some(CodePtr(fn_ptr)); | ||
| } | ||
| None | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does passing a directory here even do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-Zmiri-native-lib=/path/to/dir/->-Zmiri-native-lib=/path/to/dir/file1 -Zmiri-native-lib=/path/to/dir/file2 -Zmiri-native-lib=/path/to/dir/file3 ...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay; the docs should say that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wait, they do, I just missed it. 🤦