1

Hello people of the internet,

I'm struggeling to invoke a function that is stored in a libc::c_void-Pointer. I can't tell Rust that the pointer is invokable and I can't figure out how to.

I want to translate this C++ Code

void * malloc(size_t size) {
    static void *(*real_malloc)(size_t) = nullptr;
    if (real_malloc == nullptr) {
        real_malloc = reinterpret_cast<void *(*)(size_t)> (dlsym(RTLD_NEXT, "malloc"));
    }
    // do some logging stuff
    void * ptr = real_malloc(size);
    return ptr;
}

to Rust.

#[no_mangle]
pub extern fn malloc(bytes: usize) {
    let c_string = "malloc\0".as_mut_ptr() as *mut i8; // char array for libc
    let real_malloc: *mut libc::c_void = libc::dlsym(libc::RTLD_NEXT, c_string);
    return real_malloc(bytes);
}

That's my progress so far after 1h of searching on the internet and trying. I'm new to Rust and not yet familiar with Rust/FFI / Rust with libc. I tried a lot with unsafe{}, casts with as but I always stuck at the following problem:

return real_malloc(bytes);
       ^^^^^^^^^^^^^^^^^^ expected (), found *-ptr

Q1: How can I call the function behind the void-Pointer stored in real_malloc?

Q2: Is my Rust-String to C-String conversion feasible this way?

phip1611
  • 5,460
  • 4
  • 30
  • 57
  • I think learn the basic, how tu return something from a function should be put in priority before trying to do ffi. – Stargateur Jul 17 '19 at 21:15
  • @Stargateur yes, indeed! Please have a look at my own answer to this post. I found the post you mentioned and it helped me! Thanks anyway! – phip1611 Jul 17 '19 at 21:21
  • 1
    I found it because you found it, as you said yourself this solve your problem so it's very likely to be a good duplicate candidate. It's just to "mark" this question as close because a more general answer exist that future reader can directly read if they read your question. – Stargateur Jul 17 '19 at 21:26
  • Alright! Don't know if it's really a duplicate because the buzz-word here is void-Pointer. I'm probably not the only person that searches for this instead of "raw address" - what do you think? – phip1611 Jul 17 '19 at 21:28
  • 2
    your question will not be removed, your answer will not be removed. Just we prevent future people to write an answer here instead we encourage people to write an answer on the duplicate (if needed). "Duplicate questions are not necessarily bad; different descriptions of the same problem help future visitors to find the answers they're looking for." see https://meta.stackexchange.com/questions/10841/how-should-duplicate-questions-be-handled – Stargateur Jul 17 '19 at 21:34

1 Answers1

1

I figured it out! Perhaps there is a better way but it works.

The trick is to "cast" the void-Pointer to c-function-Type with std::mem::transmute since it won't work with as

type LibCMallocT = fn(usize) -> *mut libc::c_void;

// C-Style string for symbol-name
let c_string = "malloc\0".as_ptr() as *mut i8; // char array for libc
// Void-Pointer to address of symbol
let real_malloc_addr: *mut libc::c_void = unsafe {libc::dlsym(libc::RTLD_NEXT, c_string)};
// transmute: "Reinterprets the bits of a value of one type as another type"
// Transform void-pointer-type to callable C-Function
let real_malloc: LibCMallocT = unsafe { std::mem::transmute(real_malloc_addr) }

When the shared object is build, one can verify that it works like this: LD_PRELOAD=./target/debug/libmalloc_log_lib.so some-binary


My full code:

extern crate libc;

use std::io::Write;

const MSG: &str = "HELLO WORLD\n";

type LibCMallocT = fn(usize) -> *mut libc::c_void;

#[no_mangle] // then "malloc" is the symbol name so that ELF-Files can find it (if this lib is preloaded)
pub extern fn malloc(bytes: usize) -> *mut libc::c_void {
    /// Disable logging aka return immediately the pointer from the real malloc (libc malloc)
    static mut RETURN_IMMEDIATELY: bool = false;

    // C-Style string for symbol-name
    let c_string = "malloc\0".as_ptr() as *mut i8; // char array for libc
    // Void-Pointer to address of symbol
    let real_malloc_addr: *mut libc::c_void = unsafe {libc::dlsym(libc::RTLD_NEXT, c_string)};
    // transmute: "Reinterprets the bits of a value of one type as another type"
    // Transform void-pointer-type to callable C-Function
    let real_malloc: LibCMallocT = unsafe { std::mem::transmute(real_malloc_addr) };

    unsafe {
        if !RETURN_IMMEDIATELY {
            // let's do logging and other stuff that potentially
            // needs malloc() itself

            // This Variable prevent infinite loops because 'std::io::stdout().write_all'
            // also uses malloc itself

            // TODO: Do proper synchronisazion
            //  (lock whole method? thread_local variable?)
            RETURN_IMMEDIATELY = true;
            match std::io::stdout().write_all(MSG.as_bytes()) {
                _ => ()
            };
            RETURN_IMMEDIATELY = false
        }
    }

    (real_malloc)(bytes)
}

PS: Thanks to https://stackoverflow.com/a/46134764/2891595 (After I googled a lot more I found the trick with transmute!)

phip1611
  • 5,460
  • 4
  • 30
  • 57