reverie-rs / reverie

trace and intercept linux syscalls.
Other
14 stars 5 forks source link

Link-method / format for Rust plugins that statically link libc #48

Closed rrnewton closed 5 years ago

rrnewton commented 5 years ago

It is a long term goal to provide libc / standard library functionality for writers of instrumentation tools/plugins. The topic of linking methods is mentioned in other issues:

We've found ourselves constrained by the Rust toolchain, and in particular by a bug with MUSL and the --crate-type=cdylib compilation mode. For this issue, we focus on plugins written in Rust. Below is a summary of several strategies and what their respective blockers are. (Please update this directly in addition to commenting below.)

The idea with a static binary or static library would be to avoid any dependence from the plugin on any symbols in the guest, and provide our own means for the instrumentor (patcher, trampolines) to find the addresses needed within the plugin once it is injected into memory.

rrnewton commented 5 years ago

Creating a libcsupport milestone for all issues that are related to (or must come after) libc support. If we can make do with no_std, this can potentially be deferred a long while. For now, the goal is to document what the blocking issues are and convince ourselves we could solve it if we needed to.

wangbj commented 5 years ago

I find out a hack-ish way to generate freestanding libTOOL.so by using crate-type=staticlib thanks to your insight. it requires some post-processing but seems a lot easier than all the alternatives. I wish there's a cargo build post command, like mentioned in https://github.com/rust-lang/cargo/issues/545 almost five years ago, certain things move fairly slowly in rust than I would have expected. Wonder if it would be better if there's a bigger name behind rust (like go).

wangbj commented 5 years ago

I created a simple tool for this issue: https://github.com/wangbj/rust-staticlib-linker

wangbj commented 5 years ago

@rrnewton I don't think it is a good idea anymore (by using rust-staticlib-linker). So we have managed to create a freestanding libTOOL.so, and we have rust libstd code in it, rust (libstd) loves thread local storage (TLS), it pretty much use it anywhere when it is possible, mainly to get rid of mutable global variables. However, TLS is implemented in libc; because our libTOOL.so statically linked musl libc code, and the traced application (being LD_PRELOAD-ed) is likely linked against glibc, they have totally different TLS implementations! Getting TLS offset is basically something like:

function pthread_self() is used for the first bulletin, in glibc, it is something like

mov    %fs:0x10,%rax

, while in musl:

mov    %fs:0x0,%rax

I even tried to patch libTOOL.so 's __tls_get_addr, to use the same one as ld-linux.so, but the app would crash/hang elsewhere, here is a snippet (gdb) where it hung at __lll_lock_wait_private():

#0  __lll_lock_wait_private () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:95
#1  0x00007ffff76684a1 in tcache_init () at malloc.c:2986
#2  0x00007ffff766c306 in tcache_init () at malloc.c:2983
#3  __libc_calloc (n=n@entry=1, elem_size=elem_size@entry=32) at malloc.c:3402
#4  0x00007ffff76157c2 in __cxa_thread_atexit_impl (func=0x7ffff7a846c0 <std::thread::local::fast::destroy_value>, obj=0x7ffff7fb37e0, 
    dso_symbol=0x7ffff7dd10b0 <_rust_extern_with_linkage___dso_handle>) at cxa_thread_atexit_impl.c:106
#5  0x00007ffff7a8f91a in std::sys::unix::fast_thread_local::register_dtor () at src/libstd/sys/unix/fast_thread_local.rs:28
#6  std::thread::local::fast::Key<T>::register_dtor () at src/libstd/thread/local.rs:386
#7  std::thread::local::fast::Key<T>::get () at src/libstd/thread/local.rs:377
#8  std::io::stdio::LOCAL_STDERR::__getit () at src/libstd/thread/local.rs:185
#9  std::thread::local::LocalKey<T>::try_with () at src/libstd/thread/local.rs:296
#10 std::io::stdio::print_to () at src/libstd/io/stdio.rs:780
#11 std::io::stdio::_eprint () at src/libstd/io/stdio.rs:811
#12 0x00007ffff79dcba3 in captured_syscall (p=0x7ffff7fee000, t=0x7fffffffd520, no=12, a0=0, a1=140737347574848, a2=131696, a3=-4096, a4=2, 
    a5=0) at examples/echo/src/entry.rs:41
#13 0x00007ffff79dbea4 in syscall_hook (info=0x7fffffffd5e0) at examples/echo/src/ffi.rs:75
#14 0x00007ffff79db16c in _syscall_hook_trampoline () at ../../trampoline/trampoline.S:80
#15 0x000000000000000c in ?? ()
#16 0x0000000000000000 in ?? ()

So we cannot really make libTOOL.so totally isolated from other shared libraries, because of TLS and threading; If we cannot use threads and TLS, I don't think there're much benefits compare to no_std.

wangbj commented 5 years ago

we now use dlmopen to get around of this issue, changes have been pushed into mater.

rrnewton commented 5 years ago

That's an amazing feat! Rare are two libc's coexisting in one process!