When the initial-exec TLS model is used, as in our tst-tls.so, the application's TLS area moves OSv's own TLS area by an offset of "initial_tls_size" which wasn't aligned with any particular alignment.
But the linker knows (see arch/x64/loader.ld) that the TLS template segment is aligned at a 64-byte boundary, and objects placed there were placed with a particular alignment. For example, the thread_signal_mask TLS variable is set (by sigprocmask()) for every new pthread, and because this is a 128-bit variable, Gcc, starting with version 7, uses the "MOVAPS" SSE instruction to set it. This instruction assumes that this variable is 16-byte aligned, as was explicitly specified for this variable. But if initial_tls_size is 8 (mod 16), as it is in the "tst-tls.so" test, the thread_signal_mask TLS variable is no longer 16-byte aligned, and the test crashes with a #GP fault. So this patch fixes the initial-exec initial_tls_size to be 64-byte aligned so not to break the alignment of OSv's own TLS variables. Also, to be on the safe size, we use aligned_alloc to do the actual allocation, although this is not strictly necessary (all allocations larger than a page are page-aligned in any case). Fixes #894. Signed-off-by: Nadav Har'El <n...@scylladb.com> --- arch/x64/arch-switch.hh | 12 +++++++++--- core/elf.cc | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/arch/x64/arch-switch.hh b/arch/x64/arch-switch.hh index 8be8c426..d1a039a9 100644 --- a/arch/x64/arch-switch.hh +++ b/arch/x64/arch-switch.hh @@ -121,7 +121,7 @@ void thread::setup_tcb() assert(tls.size); void* user_tls_data; - auto user_tls_size = 0; + size_t user_tls_size = 0; if (_app_runtime) { auto obj = _app_runtime->app.lib(); assert(obj); @@ -129,8 +129,14 @@ void thread::setup_tcb() user_tls_data = obj->initial_tls(); } - // FIXME: respect alignment - void* p = malloc(sched::tls.size + user_tls_size + sizeof(*_tcb)); + // In arch/x64/loader.ld, the TLS template segment is aligned to 64 + // bytes, and that's what the objects placed in it assume. So make + // sure our copy is allocated with the same 64-byte alignment, and + // verify that object::init_static_tls() ensured that user_tls_size + // also doesn't break this alignment . + assert(align_check(tls.size, (size_t)64)); + assert(align_check(user_tls_size, (size_t)64)); + void* p = aligned_alloc(64, sched::tls.size + user_tls_size + sizeof(*_tcb)); if (user_tls_size) { memcpy(p, user_tls_data, user_tls_size); } diff --git a/core/elf.cc b/core/elf.cc index 2a413a4d..6c250a12 100644 --- a/core/elf.cc +++ b/core/elf.cc @@ -1003,6 +1003,9 @@ void object::init_static_tls() } static_tls |= obj->_static_tls; _initial_tls_size = std::max(_initial_tls_size, obj->static_tls_end()); + // Align initial_tls_size to 64 bytes, to not break the 64-byte + // alignment of the TLS segment defined in loader.ld. + _initial_tls_size = align_up(_initial_tls_size, (size_t)64); } if (!static_tls) { _initial_tls_size = 0; -- 2.13.3 -- You received this message because you are subscribed to the Google Groups "OSv Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to osv-dev+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.