Blame sysdeps/aarch64/dl-tlsdesc.S

Packit 6c4009
/* Thread-local storage handling in the ELF dynamic linker.
Packit 6c4009
   AArch64 version.
Packit 6c4009
   Copyright (C) 2011-2018 Free Software Foundation, Inc.
Packit 6c4009
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
#include <tls.h>
Packit 6c4009
#include "tlsdesc.h"
Packit 6c4009
Packit 6c4009
#define NSAVEDQREGPAIRS	16
Packit 6c4009
#define SAVE_Q_REGISTERS				\
Packit 6c4009
	stp	q0, q1,	[sp, #-32*NSAVEDQREGPAIRS]!;	\
Packit 6c4009
	cfi_adjust_cfa_offset (32*NSAVEDQREGPAIRS);	\
Packit 6c4009
	stp	 q2,  q3, [sp, #32*1];			\
Packit 6c4009
	stp	 q4,  q5, [sp, #32*2];			\
Packit 6c4009
	stp	 q6,  q7, [sp, #32*3];			\
Packit 6c4009
	stp	 q8,  q9, [sp, #32*4];			\
Packit 6c4009
	stp	q10, q11, [sp, #32*5];			\
Packit 6c4009
	stp	q12, q13, [sp, #32*6];			\
Packit 6c4009
	stp	q14, q15, [sp, #32*7];			\
Packit 6c4009
	stp	q16, q17, [sp, #32*8];			\
Packit 6c4009
	stp	q18, q19, [sp, #32*9];			\
Packit 6c4009
	stp	q20, q21, [sp, #32*10];			\
Packit 6c4009
	stp	q22, q23, [sp, #32*11];			\
Packit 6c4009
	stp	q24, q25, [sp, #32*12];			\
Packit 6c4009
	stp	q26, q27, [sp, #32*13];			\
Packit 6c4009
	stp	q28, q29, [sp, #32*14];			\
Packit 6c4009
	stp	q30, q31, [sp, #32*15];
Packit 6c4009
Packit 6c4009
#define RESTORE_Q_REGISTERS				\
Packit 6c4009
	ldp	 q2,  q3, [sp, #32*1];			\
Packit 6c4009
	ldp	 q4,  q5, [sp, #32*2];			\
Packit 6c4009
	ldp	 q6,  q7, [sp, #32*3];			\
Packit 6c4009
	ldp	 q8,  q9, [sp, #32*4];			\
Packit 6c4009
	ldp	q10, q11, [sp, #32*5];			\
Packit 6c4009
	ldp	q12, q13, [sp, #32*6];			\
Packit 6c4009
	ldp	q14, q15, [sp, #32*7];			\
Packit 6c4009
	ldp	q16, q17, [sp, #32*8];			\
Packit 6c4009
	ldp	q18, q19, [sp, #32*9];			\
Packit 6c4009
	ldp	q20, q21, [sp, #32*10];			\
Packit 6c4009
	ldp	q22, q23, [sp, #32*11];			\
Packit 6c4009
	ldp	q24, q25, [sp, #32*12];			\
Packit 6c4009
	ldp	q26, q27, [sp, #32*13];			\
Packit 6c4009
	ldp	q28, q29, [sp, #32*14];			\
Packit 6c4009
	ldp	q30, q31, [sp, #32*15];			\
Packit 6c4009
	ldp	 q0,  q1, [sp], #32*NSAVEDQREGPAIRS;	\
Packit 6c4009
	cfi_adjust_cfa_offset (-32*NSAVEDQREGPAIRS);
Packit 6c4009
Packit 6c4009
	.text
Packit 6c4009
Packit 6c4009
	/* Compute the thread pointer offset for symbols in the static
Packit 6c4009
	   TLS block. The offset is the same for all threads.
Packit 6c4009
	   Prototype:
Packit 6c4009
	   _dl_tlsdesc_return (tlsdesc *) ;
Packit 6c4009
	 */
Packit 6c4009
	.hidden _dl_tlsdesc_return
Packit 6c4009
	.global	_dl_tlsdesc_return
Packit 6c4009
	.type	_dl_tlsdesc_return,%function
Packit 6c4009
	cfi_startproc
Packit 6c4009
	.align 2
Packit 6c4009
_dl_tlsdesc_return:
Packit 6c4009
	DELOUSE (0)
Packit 6c4009
	ldr	PTR_REG (0), [x0, #PTR_SIZE]
Packit 6c4009
	RET
Packit 6c4009
	cfi_endproc
Packit 6c4009
	.size	_dl_tlsdesc_return, .-_dl_tlsdesc_return
Packit 6c4009
Packit 6c4009
	/* Handler for undefined weak TLS symbols.
Packit 6c4009
	   Prototype:
Packit 6c4009
	   _dl_tlsdesc_undefweak (tlsdesc *);
Packit 6c4009
Packit 6c4009
	   The second word of the descriptor contains the addend.
Packit 6c4009
	   Return the addend minus the thread pointer. This ensures
Packit 6c4009
	   that when the caller adds on the thread pointer it gets back
Packit 6c4009
	   the addend.  */
Packit 6c4009
Packit 6c4009
	.hidden _dl_tlsdesc_undefweak
Packit 6c4009
	.global	_dl_tlsdesc_undefweak
Packit 6c4009
	.type	_dl_tlsdesc_undefweak,%function
Packit 6c4009
	cfi_startproc
Packit 6c4009
	.align  2
Packit 6c4009
_dl_tlsdesc_undefweak:
Packit 6c4009
	str	x1, [sp, #-16]!
Packit 6c4009
	cfi_adjust_cfa_offset (16)
Packit 6c4009
	DELOUSE (0)
Packit 6c4009
	ldr	PTR_REG (0), [x0, #PTR_SIZE]
Packit 6c4009
	mrs	x1, tpidr_el0
Packit 6c4009
	sub	PTR_REG (0), PTR_REG (0), PTR_REG (1)
Packit 6c4009
	ldr	x1, [sp], #16
Packit 6c4009
	cfi_adjust_cfa_offset (-16)
Packit 6c4009
	RET
Packit 6c4009
	cfi_endproc
Packit 6c4009
	.size	_dl_tlsdesc_undefweak, .-_dl_tlsdesc_undefweak
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
	/* Handler for dynamic TLS symbols.
Packit 6c4009
	   Prototype:
Packit 6c4009
	   _dl_tlsdesc_dynamic (tlsdesc *) ;
Packit 6c4009
Packit 6c4009
	   The second word of the descriptor points to a
Packit 6c4009
	   tlsdesc_dynamic_arg structure.
Packit 6c4009
Packit 6c4009
	   Returns the offset between the thread pointer and the
Packit 6c4009
	   object referenced by the argument.
Packit 6c4009
Packit 6c4009
	   ptrdiff_t
Packit 6c4009
	   __attribute__ ((__regparm__ (1)))
Packit 6c4009
	   _dl_tlsdesc_dynamic (struct tlsdesc *tdp)
Packit 6c4009
	   {
Packit 6c4009
	     struct tlsdesc_dynamic_arg *td = tdp->arg;
Packit 6c4009
	     dtv_t *dtv = *(dtv_t **)((char *)__thread_pointer + TCBHEAD_DTV);
Packit 6c4009
	     if (__builtin_expect (td->gen_count <= dtv[0].counter
Packit 6c4009
		&& (dtv[td->tlsinfo.ti_module].pointer.val
Packit 6c4009
		    != TLS_DTV_UNALLOCATED),
Packit 6c4009
		1))
Packit 6c4009
	       return dtv[td->tlsinfo.ti_module].pointer.val
Packit 6c4009
		+ td->tlsinfo.ti_offset
Packit 6c4009
		- __thread_pointer;
Packit 6c4009
Packit 6c4009
	     return ___tls_get_addr (&td->tlsinfo) - __thread_pointer;
Packit 6c4009
	   }
Packit 6c4009
	 */
Packit 6c4009
Packit 6c4009
	.hidden _dl_tlsdesc_dynamic
Packit 6c4009
	.global	_dl_tlsdesc_dynamic
Packit 6c4009
	.type	_dl_tlsdesc_dynamic,%function
Packit 6c4009
	cfi_startproc
Packit 6c4009
	.align 2
Packit 6c4009
_dl_tlsdesc_dynamic:
Packit 6c4009
	DELOUSE (0)
Packit 6c4009
Packit 6c4009
	/* Save just enough registers to support fast path, if we fall
Packit 6c4009
	   into slow path we will save additional registers.  */
Packit 6c4009
	stp	x1,  x2, [sp, #-32]!
Packit 6c4009
	stp	x3,  x4, [sp, #16]
Packit 6c4009
	cfi_adjust_cfa_offset (32)
Packit 6c4009
	cfi_rel_offset (x1, 0)
Packit 6c4009
	cfi_rel_offset (x2, 8)
Packit 6c4009
	cfi_rel_offset (x3, 16)
Packit 6c4009
	cfi_rel_offset (x4, 24)
Packit 6c4009
Packit 6c4009
	mrs	x4, tpidr_el0
Packit 6c4009
	ldr	PTR_REG (1), [x0,#TLSDESC_ARG]
Packit 6c4009
	ldr	PTR_REG (0), [x4,#TCBHEAD_DTV]
Packit 6c4009
	ldr	PTR_REG (3), [x1,#TLSDESC_GEN_COUNT]
Packit 6c4009
	ldr	PTR_REG (2), [x0,#DTV_COUNTER]
Packit 6c4009
	cmp	PTR_REG (3), PTR_REG (2)
Packit 6c4009
	b.hi	2f
Packit 6c4009
	/* Load r2 = td->tlsinfo.ti_module and r3 = td->tlsinfo.ti_offset.  */
Packit 6c4009
	ldp	PTR_REG (2), PTR_REG (3), [x1,#TLSDESC_MODID]
Packit 6c4009
	add	PTR_REG (0), PTR_REG (0), PTR_REG (2), lsl #(PTR_LOG_SIZE + 1)
Packit 6c4009
	ldr	PTR_REG (0), [x0] /* Load val member of DTV entry.  */
Packit 6c4009
	cmp	PTR_REG (0), #TLS_DTV_UNALLOCATED
Packit 6c4009
	b.eq	2f
Packit 6c4009
	sub	PTR_REG (3), PTR_REG (3), PTR_REG (4)
Packit 6c4009
	add	PTR_REG (0), PTR_REG (0), PTR_REG (3)
Packit 6c4009
1:
Packit 6c4009
	ldp	 x3,  x4, [sp, #16]
Packit 6c4009
	ldp	 x1,  x2, [sp], #32
Packit 6c4009
	cfi_adjust_cfa_offset (-32)
Packit 6c4009
	RET
Packit 6c4009
2:
Packit 6c4009
	/* This is the slow path. We need to call __tls_get_addr() which
Packit 6c4009
	   means we need to save and restore all the register that the
Packit 6c4009
	   callee will trash.  */
Packit 6c4009
Packit 6c4009
	/* Save the remaining registers that we must treat as caller save.  */
Packit 6c4009
# define NSAVEXREGPAIRS 8
Packit 6c4009
	stp	x29, x30, [sp,#-16*NSAVEXREGPAIRS]!
Packit 6c4009
	cfi_adjust_cfa_offset (16*NSAVEXREGPAIRS)
Packit 6c4009
	cfi_rel_offset (x29, 0)
Packit 6c4009
	cfi_rel_offset (x30, 8)
Packit 6c4009
	mov	x29, sp
Packit 6c4009
	stp	 x5,  x6, [sp, #16*1]
Packit 6c4009
	stp	 x7,  x8, [sp, #16*2]
Packit 6c4009
	stp	 x9, x10, [sp, #16*3]
Packit 6c4009
	stp	x11, x12, [sp, #16*4]
Packit 6c4009
	stp	x13, x14, [sp, #16*5]
Packit 6c4009
	stp	x15, x16, [sp, #16*6]
Packit 6c4009
	stp	x17, x18, [sp, #16*7]
Packit 6c4009
	cfi_rel_offset (x5, 16*1)
Packit 6c4009
	cfi_rel_offset (x6, 16*1+8)
Packit 6c4009
	cfi_rel_offset (x7, 16*2)
Packit 6c4009
	cfi_rel_offset (x8, 16*2+8)
Packit 6c4009
	cfi_rel_offset (x9, 16*3)
Packit 6c4009
	cfi_rel_offset (x10, 16*3+8)
Packit 6c4009
	cfi_rel_offset (x11, 16*4)
Packit 6c4009
	cfi_rel_offset (x12, 16*4+8)
Packit 6c4009
	cfi_rel_offset (x13, 16*5)
Packit 6c4009
	cfi_rel_offset (x14, 16*5+8)
Packit 6c4009
	cfi_rel_offset (x15, 16*6)
Packit 6c4009
	cfi_rel_offset (x16, 16*6+8)
Packit 6c4009
	cfi_rel_offset (x17, 16*7)
Packit 6c4009
	cfi_rel_offset (x18, 16*7+8)
Packit 6c4009
Packit 6c4009
	SAVE_Q_REGISTERS
Packit 6c4009
Packit 6c4009
	mov	x0, x1
Packit 6c4009
	bl	__tls_get_addr
Packit 6c4009
Packit 6c4009
	mrs	x1, tpidr_el0
Packit 6c4009
	sub	PTR_REG (0), PTR_REG (0), PTR_REG (1)
Packit 6c4009
Packit 6c4009
	RESTORE_Q_REGISTERS
Packit 6c4009
Packit 6c4009
	ldp	 x5,  x6, [sp, #16*1]
Packit 6c4009
	ldp	 x7,  x8, [sp, #16*2]
Packit 6c4009
	ldp	 x9, x10, [sp, #16*3]
Packit 6c4009
	ldp	x11, x12, [sp, #16*4]
Packit 6c4009
	ldp	x13, x14, [sp, #16*5]
Packit 6c4009
	ldp	x15, x16, [sp, #16*6]
Packit 6c4009
	ldp	x17, x18, [sp, #16*7]
Packit 6c4009
Packit 6c4009
	ldp	x29, x30, [sp], #16*NSAVEXREGPAIRS
Packit 6c4009
	cfi_adjust_cfa_offset (-16*NSAVEXREGPAIRS)
Packit 6c4009
	cfi_restore (x29)
Packit 6c4009
	cfi_restore (x30)
Packit 6c4009
	b	1b
Packit 6c4009
	cfi_endproc
Packit 6c4009
	.size	_dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic
Packit 6c4009
# undef NSAVEXREGPAIRS
Packit 6c4009
#endif