From patchwork Tue Jun 20 13:59:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 21138 Received: (qmail 98861 invoked by alias); 20 Jun 2017 13:59:19 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 96749 invoked by uid 89); 20 Jun 2017 13:59:18 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy= X-HELO: mx1.redhat.com DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com B7A303A191 Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=fweimer@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com B7A303A191 Subject: Re: [PATCH] x86-64: Align the stack in __tls_get_addr [BZ #21609] To: libc-alpha@sourceware.org References: Cc: nmiell@gmail.com, Jakub Jelinek From: Florian Weimer Message-ID: <14a88e5f-a8c6-cd78-a363-1368d175f605@redhat.com> Date: Tue, 20 Jun 2017 15:59:12 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.1.0 MIME-Version: 1.0 In-Reply-To: On 06/19/2017 12:18 PM, Florian Weimer wrote: > This is a follow-up to bug 15128, where an unaligned stack was > compensated for in the dynamic linker relocation trampoline. > __tls_get_addr itself was not fixed, though, and the function can reach > deeply into libc due to its malloc dependency, so can result crashes > with an unaligned stack, too. > > The attached patch adds a compatibility implementation of __tls_get_addr > which aligns the stack, but only on the slow path. Internal calls go to > the default implementation (from elf/dl-tls.c) which does not perform > stack realignment. > > I plan to submit a follow-up patch which adds a new symbol version for > __tls_get_addr which bypasses the stack alignment for new binaries. > > In my patch, the CFI annotations need review. I have never written > those before. I went over the CFI annotations with Jakub. He suggested some simplifications, incorporated in the attached patch. We don't need .cfi_restore because the %rbp value at the specified offset is protected by the red zone until the function returns. Thanks, Florian x86-64: Align the stack in __tls_get_addr [BZ #21609] This change forces realignment of the stack pointer in __tls_get_addr, so that old GCC-compiled binaries continue to work even if vector instructions are used in glibc which require the ABI stack realignment. The new assembler implementation of __tls_get_addr calls the default implementation (from elf/dl-tls.c, now call __tls_get_addr_default) after realigning the stack (but only does so on the slow path). Internal calls go directly to __tls_get_addr_default because they do not need stack realignment. 2017-06-20 Florian Weimer [BZ #21609] __tls_get_addr implementation with stack alignment for older GCC. * sysdeps/x86_64/dl-tls.h: Add multiple inclusion guards. * sysdeps/x86_64/tls_get_addr_compat.c: New file. * sysdeps/x86_64/dl-tls.c: Likewise. * sysdeps/x86_64/rtld-offets.h: Likewise. * sysdeps/x86_64/dl-tlsdesc.h: Include . (tls_index): Remove definition. * sysdeps/x86_64/tlsdesc.sym (TI_MODULE_OFFSET, TI_OFFSET_OFFSET): Add. * sysdeps/x86_64/Makefile (sysdep-dl-routines): Add tls_get_addr_compat. (gen-as-const-headers): Add rtld-offsets.sym. diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile index 5075c91..5901b14 100644 --- a/sysdeps/x86_64/Makefile +++ b/sysdeps/x86_64/Makefile @@ -27,7 +27,7 @@ ifeq ($(subdir),elf) CFLAGS-.os += $(if $(filter $(@F),$(patsubst %,%.os,$(all-rtld-routines))),\ -mno-mmx) -sysdep-dl-routines += tlsdesc dl-tlsdesc +sysdep-dl-routines += tlsdesc dl-tlsdesc tls_get_addr_compat tests += ifuncmain8 modules-names += ifuncmod8 @@ -120,5 +120,5 @@ endif endif ifeq ($(subdir),csu) -gen-as-const-headers += tlsdesc.sym +gen-as-const-headers += tlsdesc.sym rtld-offsets.sym endif diff --git a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c new file mode 100644 index 0000000..f5441e1 --- /dev/null +++ b/sysdeps/x86_64/dl-tls.c @@ -0,0 +1,42 @@ +/* Thread-local storage handling in the ELF dynamic linker. x86-64 version. + Copyright (C) 2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifdef SHARED +/* Work around GCC PR58066, due to which __tls_get_addr may be called + with an unaligned stack. The compat implementation is in + tls_get_addr-compat.S. */ + +# include +# include + +/* Define __tls_get_addr within elf/dl-tls.c under a different + name. */ +extern __typeof__ (__tls_get_addr) __tls_get_addr_default; + +# define __tls_get_addr __tls_get_addr_default +# include + +# undef __tls_get_addr_default +hidden_ver (__tls_get_addr_default, __tls_get_addr) + +#else + +/* No compatibility symbol needed. */ +# include + +#endif diff --git a/sysdeps/x86_64/dl-tls.h b/sysdeps/x86_64/dl-tls.h index 4a59d2a..c2fb56c 100644 --- a/sysdeps/x86_64/dl-tls.h +++ b/sysdeps/x86_64/dl-tls.h @@ -16,6 +16,9 @@ License along with the GNU C Library; if not, see . */ +#ifndef _X86_64_DL_TLS_H +#define _X86_64_DL_TLS_H + #include /* Type used for the representation of TLS information in the GOT. */ @@ -27,3 +30,5 @@ typedef struct dl_tls_index extern void *__tls_get_addr (tls_index *ti); + +#endif /* _X86_64_DL_TLS_H */ diff --git a/sysdeps/x86_64/dl-tlsdesc.h b/sysdeps/x86_64/dl-tlsdesc.h index 14019a2..5437b7f 100644 --- a/sysdeps/x86_64/dl-tlsdesc.h +++ b/sysdeps/x86_64/dl-tlsdesc.h @@ -17,10 +17,11 @@ License along with the GNU C Library; if not, see . */ -#include - #ifndef _X86_64_DL_TLSDESC_H -# define _X86_64_DL_TLSDESC_H 1 +#define _X86_64_DL_TLSDESC_H + +#include +#include /* Type used to represent a TLS descriptor in the GOT. */ struct tlsdesc @@ -39,12 +40,6 @@ struct tlsdesc }; }; -typedef struct dl_tls_index -{ - uint64_t ti_module; - uint64_t ti_offset; -} tls_index; - /* Type used as the argument in a TLS descriptor for a symbol that needs dynamic TLS offsets. */ struct tlsdesc_dynamic_arg @@ -59,12 +54,12 @@ extern ptrdiff_t attribute_hidden _dl_tlsdesc_resolve_rela(struct tlsdesc *on_rax), _dl_tlsdesc_resolve_hold(struct tlsdesc *on_rax); -# ifdef SHARED +#ifdef SHARED extern void *_dl_make_tlsdesc_dynamic (struct link_map *map, size_t ti_offset) internal_function attribute_hidden; extern ptrdiff_t attribute_hidden _dl_tlsdesc_dynamic(struct tlsdesc *); -# endif - #endif + +#endif /* _X86_64_DL_TLSDESC_H */ diff --git a/sysdeps/x86_64/rtld-offsets.sym b/sysdeps/x86_64/rtld-offsets.sym new file mode 100644 index 0000000..fd41b51 --- /dev/null +++ b/sysdeps/x86_64/rtld-offsets.sym @@ -0,0 +1,6 @@ +#define SHARED +#include + +-- + +GL_TLS_GENERATION_OFFSET offsetof (struct rtld_global, _dl_tls_generation) diff --git a/sysdeps/x86_64/tls_get_addr_compat.S b/sysdeps/x86_64/tls_get_addr_compat.S new file mode 100644 index 0000000..3b3f0cf --- /dev/null +++ b/sysdeps/x86_64/tls_get_addr_compat.S @@ -0,0 +1,61 @@ +/* Stack-aligning implementation of __tls_get_addr. + Copyright (C) 2004-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifdef SHARED + +# include +# include "tlsdesc.h" +# include "rtld-offsets.h" + +/* See __tls_get_addr in dl-tls.c. This compat function calls + the default __tls_get_addr implementation on both slow paths. It + realigns the stack before the call to work around GCC PR58066. */ + .align 16 +__tls_get_addr_compat: + .type __tls_get_addr_compat,@function + .global __tls_get_addr_compat + strong_alias (__tls_get_addr_compat, __tls_get_addr) + cfi_startproc + movq %fs:DTV_OFFSET, %rdx + movq GL_TLS_GENERATION_OFFSET+_rtld_local(%rip), %rax + cmpq %rax, (%rdx) /* GL(dl_tls_generation) == dtv[0].counter */ + jne 1f + movq TI_MODULE_OFFSET(%rdi), %rax + salq $4, %rax + movq (%rdx,%rax), %rax /* dtv[ti->ti_module] */ + cmpq $-1, %rax + je 1f + addq TI_OFFSET_OFFSET(%rdi), %rax + ret +1: + /* On the slow path, align the stack. */ + pushq %rbp + cfi_def_cfa_offset (16) + cfi_offset (%rbp, -16) + movq %rsp, %rbp + cfi_def_cfa_register (%rbp) + andq $-16, %rsp + call HIDDEN_JUMPTARGET (__tls_get_addr) + movq %rbp, %rsp + popq %rbp + cfi_def_cfa (%rsp, 8) + ret + cfi_endproc + .size __tls_get_addr_compat, .-__tls_get_addr_compat + +#endif /* SHARED */ diff --git a/sysdeps/x86_64/tlsdesc.sym b/sysdeps/x86_64/tlsdesc.sym index 3385497..fc897ab 100644 --- a/sysdeps/x86_64/tlsdesc.sym +++ b/sysdeps/x86_64/tlsdesc.sym @@ -15,3 +15,6 @@ TLSDESC_ARG offsetof(struct tlsdesc, arg) TLSDESC_GEN_COUNT offsetof(struct tlsdesc_dynamic_arg, gen_count) TLSDESC_MODID offsetof(struct tlsdesc_dynamic_arg, tlsinfo.ti_module) TLSDESC_MODOFF offsetof(struct tlsdesc_dynamic_arg, tlsinfo.ti_offset) + +TI_MODULE_OFFSET offsetof(tls_index, ti_module) +TI_OFFSET_OFFSET offsetof(tls_index, ti_offset)