szymonwieloch / rust-dlopen Goto Github PK
View Code? Open in Web Editor NEWRust library for opening and working with dynamic link libraries.
License: MIT License
Rust library for opening and working with dynamic link libraries.
License: MIT License
I would like to load a library with RTLD_GLOBAL
as I want to use the symbols for a subsequent library load. Can you add this option?
Providing badges in cargo should improve outlook of the library and increase trust tothe library by users.
I'm using the raw API, and would like to get the DLL base address.
It's currently possible to use AddressInfoObtainer
to get the base address, but that requires the user to supply an arbitrary symbol name to look up. It would be very convenient for my application if this wasn't necessary, and I could get it directly from the Library without needing a symbol name.
To attract users it is required to show value of this library. It is best to show differences between existing alternatives and this library.
I think open
should probably be marked unsafe
, because it could call a unsafe function, although not directly.
For example, below code demonstrates a constructor function is called on library load (no unsafe
code needed):
cargo new dlopen-test
cd dlopen-test/
cat > hello.c << EOF
#include <stdio.h>
void __attribute__ ((constructor)) hello(void)
{
printf("hello from C\n");
}
EOF
gcc -Wall -shared -fPIC -o hello.so hello.c
cat > src/main.rs << EOF
extern crate dlopen;
use dlopen::raw::Library;
fn main() {
let lib = Library::open("./hello.so").unwrap();
}
EOF
cargo add dlopen
cargo run
# some cargo outputs, ends with this print from C code
hello from C
A dastardly user could:
Does the crate have anything to prevent this? I see the Symbol types that are trying to ensure you only call a function if it's still loaded as long as you are going through them, but it seems like it's possible for users to circumvent by having functions that return function pointers, but I'm still new to Rust and I haven't actually tried coding this up so I could be wrong.
On Windows it is doable using the SetDllDirectory() function, on POSIX probably modifying the LD_LIBRARY_PATH with setenv could do the trick.
RUST_BACKTRACE=1 cargo test
fails on my laptop:
Running target/debug/deps/raw-5bfffb8910e2b187
running 1 test
test open_play_close_raw ... FAILED
failures:
---- open_play_close_raw stdout ----
Library path: /Users/maoe/src/github.com/szymonwieloch/rust-dlopen/target/debug/deps/libexample.dylib.dSYM
thread 'open_play_close_raw' panicked at 'Could not open library: OpeningLibraryError(Custom { kind: Other, error: StringError("dlopen(/Users/maoe/src/github.com/szymonwieloch/rust-dlopen/target/debug/deps/libexample.dylib.dSYM, 5): no suitable image found. Did find:\n\t/Users/maoe/src/github.com/szymonwieloch/rust-dlopen/target/debug/deps/libexample.dylib.dSYM: not a file\n\t/Users/maoe/src/github.com/szymonwieloch/rust-dlopen/target/debug/deps/libexample.dylib.dSYM: not a file\n\t/Users/maoe/src/github.com/szymonwieloch/rust-dlopen/target/debug/deps/libexample.dylib.dSYM: not a file\n\t/Users/maoe/src/github.com/szymonwieloch/rust-dlopen/target/debug/deps/libexample.dylib.dSYM: not a file") })', libcore/result.rs:945:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
1: std::sys_common::backtrace::print
at libstd/sys_common/backtrace.rs:71
at libstd/sys_common/backtrace.rs:59
2: std::panicking::default_hook::{{closure}}
at libstd/panicking.rs:211
3: std::panicking::default_hook
at libstd/panicking.rs:221
4: <std::panicking::begin_panic::PanicPayload<A> as core::panic::BoxMeUp>::get
at libstd/panicking.rs:511
5: std::panicking::continue_panic_fmt
at libstd/panicking.rs:426
6: std::panicking::try::do_call
at libstd/panicking.rs:337
7: <T as core::any::Any>::get_type_id
at libcore/panicking.rs:92
8: core::result::unwrap_failed
at /private/tmp/rust-20180803-66047-v6t750/rustc-1.28.0-src/src/libcore/macros.rs:26
9: <core::result::Result<T, E>>::expect
at /private/tmp/rust-20180803-66047-v6t750/rustc-1.28.0-src/src/libcore/result.rs:809
10: raw::open_play_close_raw
at tests/raw.rs:16
11: raw::__test::TESTS::{{closure}}
at tests/raw.rs:14
12: core::ops::function::FnOnce::call_once
at /private/tmp/rust-20180803-66047-v6t750/rustc-1.28.0-src/src/libcore/ops/function.rs:223
13: <F as alloc::boxed::FnBox<A>>::call_box
at libtest/lib.rs:1451
at /private/tmp/rust-20180803-66047-v6t750/rustc-1.28.0-src/src/libcore/ops/function.rs:223
at /private/tmp/rust-20180803-66047-v6t750/rustc-1.28.0-src/src/liballoc/boxed.rs:640
14: panic_unwind::dwarf::eh::read_encoded_pointer
at libpanic_unwind/lib.rs:105
failures:
open_play_close_raw
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
error: test failed, to rerun pass '--test raw'
As the error message says libexample.dylib.dSYM
is indeed not a file. It's a bundle which contains a shared lib.
% tree target/debug/deps/libexample.dylib.dSYM/
target/debug/deps/libexample.dylib.dSYM/
└── Contents
├── Info.plist
└── Resources
└── DWARF
└── libexample.dylib
3 directories, 2 files
% file target/debug/deps/libexample.dylib.dSYM/Contents/Resources/DWARF/libexample.dylib
target/debug/deps/libexample.dylib.dSYM/Contents/Resources/DWARF/libexample.dylib: Mach-O 64-bit dSYM companion file x86_64
I'm testing this with the current master with rustc 1.28.0.
Trying to open a DLL by full path wrapped in OsStr on windows 10. But error code 126 / The specified module could not be found, raised.
When passing path directly to open function, it works!?
Tks
Why doesn't the macro allow the fields to be public? Or is there a way I don't know of?
I'm a noob at this stuff, so I don't know if there is a reason or if it just was never implemented. It's super frustrating that this is the only thing holding me back on my project..
This library looks promising for my needs, but looks like not much has happened for a while?
What is the plan with this library, is it going to be maintained?
I can see in the source code that there's a global lock for dlerror
, since this crate assumes that it's not thread-safe. However, the manuals for Linux and macOS do indicate that their implementation is thread-safe:
Perhaps that lock could only be used on platforms other than these (or any more that might come up).
Also, the table about libloading not being thread-safe is slightly wrong, AFAIK (https://docs.rs/dlopen/0.1.8/dlopen/#compare-with-other-libraries). Internally, libloading uses SetThreadErrorMode
(maybe it didn't in the past), which means that it's thread safe on Windows. The issue it has is that on FreeBSD, for example, dlerror
isn't thread-safe.
I had to add this to Cargo.toml in addition for dlopen = "0.1"
dlopen_derive = "0.1"
Please add it to the docs
I am using the library to verify that a dynamic linked library exports a specific set of symbols.
Currently, the library returns an error in cases of missing symbols that do not list the specific symbol missing:
SymbolGettingError(Os { code: 127, kind: Other, message: "The specified procedure could not be found." })
For troubleshooting bigger libraries it would be very useful if the missing symbol would be included in the message, like:
SymbolGettingError(Os { code: 127, kind: Other, message: "The specified procedure "foo" could not be found." })
Currently, to use dlopen, the user needs 2 dependencies, one for the derive macro and one for the library itself
dlopen = "0.1"
dlopen_derive = "0.1"
as well as this extra dependency, crates also need to #[macro_use]
the derive crate in order to use the macro
#[macro_use]
extern crate dlopen_derive;
This could be remedied by putting the above statement in the dlopen crate itself, much like how serde re-exports the Serialize and Deserialize dervive macros (https://github.com/serde-rs/serde/blob/master/serde/src/lib.rs#L278-L289). So the user would only need to use the trait and it would also import the macro under the same name.
It might also be possible to do this in the wrapper
module rather than the crate root so that the user importing the trait use dlopen::wrapper::WrapperApi
they would also import the derive macro of that name.
This is reported when turning on the #![no-std]
.
dlopen-test on main [?] via 🦀 v1.60.0-nightly ✗✗✗ RUSTFLAGS="-Z macro-backtrace" cargo build --release
error[E0433]: failed to resolve: could not find `std` in the list of imported crates
--> src/main.rs:24:10
|
24 | #[derive(WrapperApi)]
| ^^^^^^^^^^
| |
| could not find `std` in the list of imported crates
| in this derive macro expansion
|
::: /home/animesh/.cargo/registry/src/github.com-1ecc6299db9ec823/dlopen_derive-0.1.4/src/lib.rs:23:1
|
23 | pub fn wrapper_api(input: TokenStream) -> TokenStream {
| ----------------------------------------------------- in this expansion of `#[derive(WrapperApi)]`
Refactor wrapper API and and Symbor API to provide one consistent API with additional thread safety
This fails to compile for me:
#[macro_use]
extern crate dlopen_derive;
extern crate dlopen;
use dlopen::wrapper::WrapperApi;
use std::io::Result;
#[derive(WrapperApi)]
struct Example {
do_something: extern "C" fn(),
}
fn main () {
}
The error is:
error[E0244]: wrong number of type arguments: expected 1, found 2
--> src/main.rs:8:10
|
8 | #[derive(WrapperApi)]
| ^^^^^^^^^^ expected 1 type argument
error: aborting due to previous error
(if I take out the use std::io::Result;
line, it compiles)
I got this today. I think it is missing an
-> and
is desired here.
|
74 | #[derive(WrapperApi)]
| ^^^^^^^^^^
|
= help: message: Only bare functions, references an pointers are allowed in structures implementing WrapperApi trait
dlopen 0.1.8
dlopen_derive 0.1.4
If possible #[derive(WrapperApi)]
should copy the documentation from the corresponding field to the generated method.
An example of this not happening can be seen here:
For example,
rust-dlopen/src/wrapper/mod.rs
Line 11 in f9fed71
#
and Example
.Using libc (https://docs.rs/libc/0.2.60/libc/fn.dladdr.html) and Windows API (GetModuleFileName) you can obtain any address information. Example implementation in C:
https://gist.github.com/rdp/5e49fbbbcaca5a299a67b5daad4a2a3d
Rust on Windows has to call SymInitializeW
before generating a backtrace, and it doesn't call SymCleanup
afterward. If AddressInfoObtainer
is then used, it calls SymInitializeW
again, which fails because it has already been called.
Example code (set RUSTBACKTRACE=1
):
use std::panic::catch_unwind;
use dlopen::raw::{AddressInfoObtainer, Library};
fn main() {
let library = Library::open("example.dll").unwrap();
let pointer: *const () = unsafe { library.symbol("foo") }.unwrap();
catch_unwind(|| panic!()); // this generates a backtrace. Backtrace::capture() probably works too
// Panics because SymInitializeW returns an error
AddressInfoObtainer::new().obtain(pointer).unwrap();
}
There is some background on how the backtrace crate handled a similar conflict here (also explains why Rust doesn't call SymCleanup
).
I think dlopen should:
SymInitializeW
but ignore its return valueSymCleanup
This behavior is awkward, but I think it's the only way to be compatible with how Rust uses SymInitializeW
.
I can submit a PR for this if it's welcome.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.