In x86_64, thread local storage (tls) is implemented in userspace, mostly in libc. However, with dynamically
linked application the dynamic linker also must initialize tls for the application, most importantly, it exports
function __tls_get_addr (__tls_get_new is an weak alias to this function). tls function related to pthread
are often implemented in libc or libpthread.
Because __tls_get_addr is provided by dynamic linker, it has several implications:
-
writing a dynamic linker becomes much harder.
__tls_get_addrusually returns some vector fromthreadstructure, meaning it is completely implementation specific, musl and glibc implementations are totally non-interchangeable.Because the dynamic linker has to implements
__tls_get_addr, which is somehow(p)threadrelated, to write a dynamic linker also implies you have to write a libc with pthreads, this increase the complexiy by an order of magnitude: see This. -
the dynamic linker is also a dynamic shared object (DSO) at the same time
for glibc,
ld-linux.soalso exports several symbols, but they doesn’t have ABI compatibily: if you’re not glibc it is very unlikely you can get much sense of the exported symbols -
you cannot really have two libcs linked/mapped in the same application.
It may sound stupid to link two libcs in a single application, but it can happen, i.e.: you may create a freestanding DSO statically compiled with musl libc (but as a whole it is still a DSO!), and then use
LD_PRELOADto preload with an application linked against glibc. At first everything might seems fine, until you use tls: because the two libcs have completely different tls implimentations. This could be fine if theLD_PRELOADDSO was written in low level language like C, for rust (cdylib) it is totally a different story - rust use tls extensively - pretty much any (mutable) global variables rust encourage usethread_local(tls), or use it implicitly so wenprintln!uses tls. -
Application can be built in different ways.
Application can link against glibc, or musl libc, or statically linked with them; or it can be a go application, who is statically linked, but does not use tls at all.
Say if we
LD_PRELOADa freestanding DSO (statically linked musl libc), with an application dynamically linked aginst glibc, it is somehow possible to force the former use the same__tls_get_addrin (glibc’s)ld-linux.so, however it is a super dirty hack, requires patching the freestanding DSO during runtime; and it is not easy to support so many different configurations, let alone different architecture.
I really hope tls, at least __tls_get_addr is provided by Kernel, as get_thread_area from x86 (not available in x86_64),
so that we don’t have to go through different tls implementations; because __tls_get_addr from %fs:<offset>, it is
unlikely tls can be virtualized, or allow trapping.