#[repr(i8)]pub enum Mode {
Native = 0,
NativeTlsId = 1,
Gettid = 2,
}
Expand description
Modes of operation for this module.
Variants§
Native = 0
Delegate back to ELF native thread local storage. This is the fastest option, and simplest with respect to our own code, but is unsound. We should probably ultimately disable or remove it.
In native thread local storage for ELF executables, an access to a
thread-local variable (with C storage specifier __thread
) from a
dynamically shared object (like the Shadow shim) involves implicitly calling
the libc function __tls_get_addr
. That function is not guaranteed to be
async-signal-safe (See signal-safety(7)
), and can end up making system
calls and doing memory allocation. This has caused problems with some versions of
glibc (Can’t find the issue #…), and recently when running managed
processed compiled with asan https://github.com/shadow/shadow/issues/2790.
SAFETY: __tls_get_addr
in the linked version of libc must not make
system calls or do anything async-signal unsafe. This basically
can’t be ensured, but is often true in practice.
NativeTlsId = 1
This mode takes advantage of ELF native thread local storage, but only leverages it as a cheap-to-retrieve thread identifier. It does not call into libc or store anything directly in the native thread local storage.
In particular, based on 3.4.6 and 3.4.2 of [ELF-TLS], we know that we
can retrieve the “thread pointer” by loading offset zero of the fs
register; i.e. %fs:0
.
The contents of the data pointed to by the thread pointer are an implementation detail of the compiler, linker, and libc, so we don’t depend on it. However it seems reasonable to assume that this address is unique to each live thread, and doesn’t change during the lifetime of a thread. Therefore we use the address as a thread-identifier, which we in turn use as key to our own allocated thread-local-storage.
This mode is nearly as fast as native, but:
- Assumes that if the “thread pointer” in
%fs:0
is non-NULL for a given thread, that it is stable and unique for the lifetime of that thread. This seems like a fairly reasonable assumption, and seems to hold so far, but isn’t guaranteed. - Requires that each thread using thread local storage from this module
calls
ThreadLocalStorage::unregister_current_thread
before exiting, since the thread pointer may subsequently be used for another thread.
[ELF-TLS]: “ELF Handling For Thread-Local Storage”, by Ulrich Drepper. https://www.akkadia.org/drepper/tls.pdf
SAFETY: Requires that each thread using this thread local storage
calls ThreadLocalStorage::unregister_current_thread
before exiting.
Gettid = 2
This mode is similar to NativeTlsId
, but instead of using the ELF thread
pointer to identify each thread, it uses the system thread ID as retrieved by
the gettid
syscall.
Unlike NativeTlsId
, this approach doesn’t rely on any assumptions about
the implementation details of thread local storage in the managed process.
It also usually still works without calling ThreadLocalStorage::unregister_current_thread
,
but technically still requires it to guarantee soundness, since thread
Unfortunately this mode is much slower than the others.
SAFETY: Each thread using this thread local storage must call
ThreadLocalStorage::unregister_current_thread
before exiting.