The suspicion on the RUST forum is that assocaited functions are not working correctly for cdylib
Associated RUST forum thread
https://users.rust-lang.org/t/ld-preload-works-as-dylib-and-doesnt-as-cdylib-which-is-the-right-choice/48174
I am building an LD_PRELOAD library.
It was recommended in the rust forum that I use cdylib as crate type to build the Rust LD_PRELOAD shared library I am working with. I was using dylib before that.
My reading of other threads i this group also backsup his recommendation
https://users.rust-lang.org/t/what-is-the-difference-between-dylib-and-cdylib/28847
Interestingly enough dylib was working for me. I then switched cdylib and the LD_PRELOAD intercept portion stopped working. Though the .so still gets exectued as shown below.
Here is an example rust shared library code I used for testing. It intercepts readlink to print some debug print messages and continues to the original readlink.
extern crate core;
extern crate libc;
#[macro_use]
extern crate ctor;
use libc::{c_void,c_char,c_int,size_t,ssize_t};
use std::sync::atomic;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub mod dyld_insert_libraries;
/* Some Rust library functionality (e.g., jemalloc) initializes
* lazily, after the hooking library has inserted itself into the call
* path. If the initialization uses any hooked functions, this will lead
* to an infinite loop. Work around this by running some initialization
* code in a static constructor, and bypassing all hooks until it has
* completed. */
static INIT_STATE: atomic::AtomicBool = atomic::AtomicBool::new(false);
pub fn initialized() -> bool {
INIT_STATE.load(atomic::Ordering::SeqCst)
}
#[ctor]
fn initialize() {
Box::new(0u8);
INIT_STATE.store(true, atomic::Ordering::SeqCst);
println!("Constructor");
}
#[link(name = "dl")]
extern "C" {
fn dlsym(handle: *const c_void, symbol: *const c_char) -> *const c_void;
}
const RTLD_NEXT: *const c_void = -1isize as *const c_void;
pub unsafe fn dlsym_next(symbol: &'static str) -> *const u8 {
let ptr = dlsym(RTLD_NEXT, symbol.as_ptr() as *const c_char);
if ptr.is_null() {
panic!("redhook: Unable to find underlying function for {}", symbol);
}
ptr as *const u8
}
#[allow(non_camel_case_types)]
pub struct readlink {__private_field: ()}
#[allow(non_upper_case_globals)]
static readlink: readlink = readlink {__private_field: ()};
impl readlink {
fn get(&self) -> unsafe extern fn (path: *const c_char, buf: *mut c_char, bufsiz: size_t) -> ssize_t {
use ::std::sync::Once;
static mut REAL: *const u8 = 0 as *const u8;
static mut ONCE: Once = Once::new();
unsafe {
ONCE.call_once(|| {
REAL = dlsym_next(concat!("readlink", "\0"));
});
::std::mem::transmute(REAL)
}
}
#[no_mangle]
pub unsafe extern "C" fn readlink(path: *const c_char, buf: *mut c_char, bufsiz: size_t) -> ssize_t {
println!("readlink");
if initialized() {
println!("initialized");
::std::panic::catch_unwind(|| my_readlink ( path, buf, bufsiz )).ok()
} else {
println!("not initialized");
None
}.unwrap_or_else(|| readlink.get() ( path, buf, bufsiz ))
}
}
pub unsafe fn my_readlink(path: *const c_char, buf: *mut c_char, bufsiz: size_t) -> ssize_t {
println!("my_readlink");
readlink.get()(path, buf, bufsiz)
}
My Cargo.toml looks like this
[package]
name = "readlink"
version = "0.1.0"
authors = ["Saravanan Shanmugham <sarvi@cisco.com>"]
[lib]
name = "readlink"
crate_type = ["dylib"]
[dependencies]
libc = "0.2"
ctor = "0.1.15"
And this works. I see the constructor executing, and my_readlink which is my itnercept function and ls -al /tmp/link works and shows the symlink as expected, so the original readlink was executed as well. So all is well here
bash-4.4$ LD_PRELOAD=target/debug/libreadlink.so ls -al /tmp/link
Constructor
readlink
initialized
my_readlink
lrwxrwxrwx 1 sarvi eng 9 Aug 31 11:11 /tmp/link -> /tmp/file
bash-4.4$
I then changed
crate_type = ["dylib"]
to
crate_type = ["cdylib"]
And I see this. Only the constructor of libreadlink.so gets executed. But none of the interception happens.
bash-4.4$ LD_PRELOAD=target/debug/libreadlink.so ls -al /tmp/link
Constructor
lrwxrwxrwx 1 sarvi eng 9 Aug 31 11:11 /tmp/link -> /tmp/file
bash-4.4$
Is cdylib the right choice? and why doesnt my interception not work as the recommended cdylib but works as a dylib ?
Considering dylib is supposed to be smaller and is working. Can I continue as dylib? Or should I be worried about any other pitfalls for dylibs exposing C externs for calls from other C programs?
I expected to see this happen: explanation
Instead, this happened: explanation
Meta
rustc --version --verbose:
bash-4.4$ rustc -vV
rustc 1.47.0-nightly (7e6d6e5f5 2020-08-16)
binary: rustc
commit-hash: 7e6d6e5f535321c2223f044caba16f97b825009c
commit-date: 2020-08-16
host: x86_64-unknown-linux-gnu
release: 1.47.0-nightly
LLVM versio
Backtrace
The suspicion on the RUST forum is that assocaited functions are not working correctly for cdylib
Associated RUST forum thread
https://users.rust-lang.org/t/ld-preload-works-as-dylib-and-doesnt-as-cdylib-which-is-the-right-choice/48174
I am building an LD_PRELOAD library.
It was recommended in the rust forum that I use cdylib as crate type to build the Rust LD_PRELOAD shared library I am working with. I was using dylib before that.
My reading of other threads i this group also backsup his recommendation
https://users.rust-lang.org/t/what-is-the-difference-between-dylib-and-cdylib/28847
Interestingly enough dylib was working for me. I then switched cdylib and the LD_PRELOAD intercept portion stopped working. Though the .so still gets exectued as shown below.
Here is an example rust shared library code I used for testing. It intercepts readlink to print some debug print messages and continues to the original readlink.
My Cargo.toml looks like this
And this works. I see the constructor executing, and my_readlink which is my itnercept function and ls -al /tmp/link works and shows the symlink as expected, so the original readlink was executed as well. So all is well here
I then changed
crate_type = ["dylib"]to
crate_type = ["cdylib"]And I see this. Only the constructor of libreadlink.so gets executed. But none of the interception happens.
Is cdylib the right choice? and why doesnt my interception not work as the recommended cdylib but works as a dylib ?
Considering dylib is supposed to be smaller and is working. Can I continue as dylib? Or should I be worried about any other pitfalls for dylibs exposing C externs for calls from other C programs?
<code>I expected to see this happen: explanation
Instead, this happened: explanation
Meta
rustc --version --verbose:Backtrace