I'm trying to link statically against library written in Rust:
#![crate_type = "staticlib"]
#[no_mangle]
pub extern "C" fn foo() {
println!("bork!");
}
Using following code in C:
void foo();
int main()
{
foo();
return 0;
}
Compile lib with rustc:
rustc foo.rs
Compile binary and link with library:
gcc -g bar.c libfoo.a -ldl -lpthread -lrt -lgcc_s -lpthread -lc -lm -o bar
Run inside debugger:
(gdb) run
Starting program: /home/kykc/rusttest/bar
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff72117df in __cxa_thread_atexit_impl (func=<optimized out>, obj=<optimized out>, dso_symbol=0x0) at cxa_thread_atexit_impl.c:67
67 cxa_thread_atexit_impl.c: No such file or directory.
gcc:
gcc-4.8.real (Ubuntu 4.8.2-19ubuntu1) 4.8.2
rustc:
rustc 1.0.0-beta (9854143cb 2015-04-02) (built 2015-04-02)
It works completely fine with dylib. What am I doing wrong?
The problem here is that thread-locals with destructors can only be used in position-independent executables (due to a bug) . Fix: pass the -pie
flag to gcc, or wait a day or two.
This is caused by std::io::stdio::_print
and std::io::stdio::LOCAL_STDOUT
in the standard library:
/// Stdout used by print! and println! macros
thread_local! {
static LOCAL_STDOUT: RefCell<Option<Box<Write + Send>>> = {
RefCell::new(None)
}
}
// ...
pub fn _print(args: fmt::Arguments) {
let result = LOCAL_STDOUT.with(|s| {
if s.borrow_state() == BorrowState::Unused {
if let Some(w) = s.borrow_mut().as_mut() {
return w.write_fmt(args);
}
}
stdout().write_fmt(args)
});
if let Err(e) = result {
panic!("failed printing to stdout: {}", e);
}
}
The Box
in LOCAL_STDOUT
has the destructor in that case, so the executable crashes when the thread-local variable is touched. I've filed #24445, and Alex Crichton has diagnosed and fixed the underlying cause already.
Printing doesn't require initialising the threads or anything; there's fallback to stdout()
in the case that LOCAL_STDOUT
contains None
(which is the default). Just passing -pie
is enough to convince the executable to print bork!
instead of crashing.