I am currently playing with POSIX functions defined in dlfcn.h
with Rust, with the goal of calling a function in a separated .so
file.
The project actually contains 2 crates:
- The binary loading the shared object and calling the function.
- The dynamic library.
The dynamic library is 4 lines long and here is the code:
<code>#[no_mangle]
pub extern "Rust" fn hello_world() -> String {
String::from("Hello World !")
}
</code>
<code>#[no_mangle]
pub extern "Rust" fn hello_world() -> String {
String::from("Hello World !")
}
</code>
#[no_mangle]
pub extern "Rust" fn hello_world() -> String {
String::from("Hello World !")
}
But the executable binary is a bit more complex and looks like this:
<code>#![feature(str_from_raw_parts)]
fn open_dl(path: &str) -> Result<*mut libc::c_void, String> {
let path = format!("{path}");
let path: *const libc::c_char = unsafe { std::mem::transmute(path.as_ptr()) };
let handle = unsafe { libc::dlopen(path, libc::RTLD_NOW) };
if handle.is_null() {
let dl_error: &str = unsafe {
let ptr = libc::dlerror();
let len = libc::strlen(ptr);
std::str::from_raw_parts(std::mem::transmute(ptr), len)
};
return Err(String::from(dl_error));
}
Ok(handle)
}
fn get_symbol<T>(handle: *mut libc::c_void, symbol: &str) -> Result<&T, String> {
let symbol_name = format!("{symbol}");
let symbol = unsafe { libc::dlsym(handle, std::mem::transmute(symbol_name.as_ptr())) };
if symbol.is_null() {
let dl_error: &str = unsafe {
let ptr = libc::dlerror();
let len = libc::strlen(ptr);
std::str::from_raw_parts(std::mem::transmute(ptr), len)
};
return Err(String::from(dl_error));
}
return unsafe { Ok(std::mem::transmute(symbol)) };
}
fn main() -> Result<(), String> {
let handle = open_dl("./libfoo.so")?;
// Tricky part, get the symbol with the correct signature and calling it
let symbol: &fn() -> String = get_symbol(handle, "hello_world")?;
let val = symbol(); // Blows up
println!("{val}");
Ok(())
}
</code>
<code>#![feature(str_from_raw_parts)]
fn open_dl(path: &str) -> Result<*mut libc::c_void, String> {
let path = format!("{path}");
let path: *const libc::c_char = unsafe { std::mem::transmute(path.as_ptr()) };
let handle = unsafe { libc::dlopen(path, libc::RTLD_NOW) };
if handle.is_null() {
let dl_error: &str = unsafe {
let ptr = libc::dlerror();
let len = libc::strlen(ptr);
std::str::from_raw_parts(std::mem::transmute(ptr), len)
};
return Err(String::from(dl_error));
}
Ok(handle)
}
fn get_symbol<T>(handle: *mut libc::c_void, symbol: &str) -> Result<&T, String> {
let symbol_name = format!("{symbol}");
let symbol = unsafe { libc::dlsym(handle, std::mem::transmute(symbol_name.as_ptr())) };
if symbol.is_null() {
let dl_error: &str = unsafe {
let ptr = libc::dlerror();
let len = libc::strlen(ptr);
std::str::from_raw_parts(std::mem::transmute(ptr), len)
};
return Err(String::from(dl_error));
}
return unsafe { Ok(std::mem::transmute(symbol)) };
}
fn main() -> Result<(), String> {
let handle = open_dl("./libfoo.so")?;
// Tricky part, get the symbol with the correct signature and calling it
let symbol: &fn() -> String = get_symbol(handle, "hello_world")?;
let val = symbol(); // Blows up
println!("{val}");
Ok(())
}
</code>
#![feature(str_from_raw_parts)]
fn open_dl(path: &str) -> Result<*mut libc::c_void, String> {
let path = format!("{path}");
let path: *const libc::c_char = unsafe { std::mem::transmute(path.as_ptr()) };
let handle = unsafe { libc::dlopen(path, libc::RTLD_NOW) };
if handle.is_null() {
let dl_error: &str = unsafe {
let ptr = libc::dlerror();
let len = libc::strlen(ptr);
std::str::from_raw_parts(std::mem::transmute(ptr), len)
};
return Err(String::from(dl_error));
}
Ok(handle)
}
fn get_symbol<T>(handle: *mut libc::c_void, symbol: &str) -> Result<&T, String> {
let symbol_name = format!("{symbol}");
let symbol = unsafe { libc::dlsym(handle, std::mem::transmute(symbol_name.as_ptr())) };
if symbol.is_null() {
let dl_error: &str = unsafe {
let ptr = libc::dlerror();
let len = libc::strlen(ptr);
std::str::from_raw_parts(std::mem::transmute(ptr), len)
};
return Err(String::from(dl_error));
}
return unsafe { Ok(std::mem::transmute(symbol)) };
}
fn main() -> Result<(), String> {
let handle = open_dl("./libfoo.so")?;
// Tricky part, get the symbol with the correct signature and calling it
let symbol: &fn() -> String = get_symbol(handle, "hello_world")?;
let val = symbol(); // Blows up
println!("{val}");
Ok(())
}
My problem is that get_symbol seems to work fine, the obtained pointer is not null, so I would expect that calling the function behind it would cause no issue, but I get a SEGFAULT when I try to. Do you wonder why it does SEGFAULT ?