summaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux/mips/mips64/sysdep-cancel.h
blob: daf75e73503fc34579b61533087bd2954fe0221b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/* Copyright (C) 2003-2016 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
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>
#include <sysdeps/generic/sysdep.h>
#include <tls.h>
#ifndef __ASSEMBLER__
# include <nptl/pthreadP.h>
#endif
#include <sys/asm.h>

/* Gas will put the initial save of $gp into the CIE, because it appears to
   happen before any instructions.  So we use cfi_same_value instead of
   cfi_restore.  */

#if IS_IN (libc) || IS_IN (libpthread) || IS_IN (librt)

#ifdef __PIC__
# undef PSEUDO
# define PSEUDO(name, syscall_name, args)				      \
      .align 2;								      \
  L(pseudo_start):							      \
      cfi_startproc;							      \
      cfi_adjust_cfa_offset (STKSPACE);					      \
      cfi_rel_offset (gp, STKOFF_GP);					      \
  99: PTR_LA t9,__syscall_error;					      \
      /* manual cpreturn */						      \
      REG_L gp, STKOFF_GP(sp);						      \
      cfi_same_value (gp);						      \
      RESTORESTK;							      \
      jr t9;								      \
  .type __##syscall_name##_nocancel, @function;				      \
  .globl __##syscall_name##_nocancel;					      \
  __##syscall_name##_nocancel:						      \
    SAVESTK;								      \
    .cpsetup t9, STKOFF_GP, __##syscall_name##_nocancel;		      \
    cfi_rel_offset (gp, STKOFF_GP);					      \
    li v0, SYS_ify(syscall_name);					      \
    syscall;								      \
    bne a3, zero, SYSCALL_ERROR_LABEL;			       		      \
    /* manual cpreturn */						      \
    REG_L gp, STKOFF_GP(sp);						      \
    cfi_same_value (gp);						      \
    RESTORESTK;								      \
    ret;								      \
    cfi_endproc;							      \
  .size __##syscall_name##_nocancel,.-__##syscall_name##_nocancel;	      \
  ENTRY (name)								      \
    SAVESTK;								      \
    .cpsetup t9, STKOFF_GP, name;					      \
    cfi_rel_offset (gp, STKOFF_GP);					      \
    SINGLE_THREAD_P(v1);						      \
    bne zero, v1, L(pseudo_cancel);					      \
    .set noreorder;							      \
    li v0, SYS_ify(syscall_name);					      \
    syscall;								      \
    .set reorder;							      \
    bne a3, zero, SYSCALL_ERROR_LABEL;			       		      \
    /* manual cpreturn */						      \
    REG_L gp, STKOFF_GP(sp);						      \
    cfi_same_value (gp);						      \
    RESTORESTK;								      \
    ret;								      \
  L(pseudo_cancel):							      \
    cfi_adjust_cfa_offset (STKSPACE);					      \
    cfi_rel_offset (gp, STKOFF_GP);					      \
    REG_S ra, STKOFF_RA(sp);						      \
    cfi_rel_offset (ra, STKOFF_RA);					      \
    PUSHARGS_##args;			/* save syscall args */	      	      \
    CENABLE;								      \
    REG_S v0, STKOFF_SVMSK(sp);		/* save mask */			      \
    POPARGS_##args;			/* restore syscall args */	      \
    .set noreorder;							      \
    li v0, SYS_ify (syscall_name);				      	      \
    syscall;								      \
    .set reorder;							      \
    REG_S v0, STKOFF_SC_V0(sp);		/* save syscall result */             \
    REG_S a3, STKOFF_SC_ERR(sp);	/* save syscall error flag */	      \
    REG_L a0, STKOFF_SVMSK(sp);		/* pass mask as arg1 */		      \
    CDISABLE;								      \
    REG_L a3, STKOFF_SC_ERR(sp);	/* restore syscall error flag */      \
    REG_L ra, STKOFF_RA(sp);		/* restore return address */	      \
    REG_L v0, STKOFF_SC_V0(sp);		/* restore syscall result */          \
    bne a3, zero, SYSCALL_ERROR_LABEL;					      \
    /* manual cpreturn */						      \
    REG_L gp, STKOFF_GP(sp);						      \
    cfi_same_value (gp);						      \
    RESTORESTK;								      \
  L(pseudo_end):
#else
# undef PSEUDO
# define PSEUDO(name, syscall_name, args)				      \
      .align 2;								      \
  L(pseudo_start):							      \
      cfi_startproc;							      \
      cfi_adjust_cfa_offset (STKSPACE);					      \
  99: RESTORESTK;							      \
      j __syscall_error;						      \
  .type __##syscall_name##_nocancel, @function;				      \
  .globl __##syscall_name##_nocancel;					      \
  __##syscall_name##_nocancel:						      \
    SAVESTK;								      \
    li v0, SYS_ify(syscall_name);					      \
    syscall;								      \
    bne a3, zero, SYSCALL_ERROR_LABEL;			       		      \
    RESTORESTK;								      \
    ret;								      \
    cfi_endproc;							      \
  .size __##syscall_name##_nocancel,.-__##syscall_name##_nocancel;	      \
  ENTRY (name)								      \
    SAVESTK;								      \
    SINGLE_THREAD_P(v1);						      \
    bne zero, v1, L(pseudo_cancel);					      \
    .set noreorder;							      \
    li v0, SYS_ify(syscall_name);					      \
    syscall;								      \
    .set reorder;							      \
    bne a3, zero, SYSCALL_ERROR_LABEL;			       		      \
    RESTORESTK;								      \
    ret;								      \
  L(pseudo_cancel):							      \
    cfi_adjust_cfa_offset (STKSPACE);					      \
    REG_S ra, STKOFF_RA(sp);						      \
    cfi_rel_offset (ra, STKOFF_RA);					      \
    PUSHARGS_##args;			/* save syscall args */	      	      \
    CENABLE;								      \
    REG_S v0, STKOFF_SVMSK(sp);		/* save mask */			      \
    POPARGS_##args;			/* restore syscall args */	      \
    .set noreorder;							      \
    li v0, SYS_ify (syscall_name);				      	      \
    syscall;								      \
    .set reorder;							      \
    REG_S v0, STKOFF_SC_V0(sp);		/* save syscall result */             \
    REG_S a3, STKOFF_SC_ERR(sp);	/* save syscall error flag */	      \
    REG_L a0, STKOFF_SVMSK(sp);		/* pass mask as arg1 */		      \
    CDISABLE;								      \
    REG_L a3, STKOFF_SC_ERR(sp);	/* restore syscall error flag */      \
    REG_L ra, STKOFF_RA(sp);		/* restore return address */	      \
    REG_L v0, STKOFF_SC_V0(sp);		/* restore syscall result */          \
    bne a3, zero, SYSCALL_ERROR_LABEL;					      \
    RESTORESTK;								      \
  L(pseudo_end):
#endif

# undef PSEUDO_END
# define PSEUDO_END(sym) cfi_endproc; .end sym; .size sym,.-sym

# define PUSHARGS_0	/* nothing to do */
# define PUSHARGS_1	PUSHARGS_0 REG_S a0, STKOFF_A0(sp); cfi_rel_offset (a0, STKOFF_A0);
# define PUSHARGS_2	PUSHARGS_1 REG_S a1, STKOFF_A1(sp); cfi_rel_offset (a1, STKOFF_A1);
# define PUSHARGS_3	PUSHARGS_2 REG_S a2, STKOFF_A2(sp); cfi_rel_offset (a2, STKOFF_A2);
# define PUSHARGS_4	PUSHARGS_3 REG_S a3, STKOFF_A3(sp); cfi_rel_offset (a3, STKOFF_A3);
# define PUSHARGS_5	PUSHARGS_4 REG_S a4, STKOFF_A4(sp); cfi_rel_offset (a3, STKOFF_A4);
# define PUSHARGS_6	PUSHARGS_5 REG_S a5, STKOFF_A5(sp); cfi_rel_offset (a3, STKOFF_A5);

# define POPARGS_0	/* nothing to do */
# define POPARGS_1	POPARGS_0 REG_L a0, STKOFF_A0(sp);
# define POPARGS_2	POPARGS_1 REG_L a1, STKOFF_A1(sp);
# define POPARGS_3	POPARGS_2 REG_L a2, STKOFF_A2(sp);
# define POPARGS_4	POPARGS_3 REG_L a3, STKOFF_A3(sp);
# define POPARGS_5	POPARGS_4 REG_L a4, STKOFF_A4(sp);
# define POPARGS_6	POPARGS_5 REG_L a5, STKOFF_A5(sp);

/* Save an even number of slots.  Should be 0 if an even number of slots
   are used below, or SZREG if an odd number are used.  */
# ifdef __PIC__
#  define STK_PAD	SZREG
# else
#  define STK_PAD	0
# endif

/* Place values that we are more likely to use later in this sequence, i.e.
   closer to the SP at function entry.  If you do that, the are more
   likely to already be in your d-cache.  */
# define STKOFF_A5	(STK_PAD)
# define STKOFF_A4	(STKOFF_A5 + SZREG)
# define STKOFF_A3	(STKOFF_A4 + SZREG)
# define STKOFF_A2	(STKOFF_A3 + SZREG)	/* MT and more args.  */
# define STKOFF_A1	(STKOFF_A2 + SZREG)	/* MT and 2 args.  */
# define STKOFF_A0	(STKOFF_A1 + SZREG)	/* MT and 1 arg.  */
# define STKOFF_RA	(STKOFF_A0 + SZREG)	/* Used if MT.  */
# define STKOFF_SC_V0	(STKOFF_RA + SZREG)	/* Used if MT.  */
# define STKOFF_SC_ERR	(STKOFF_SC_V0 + SZREG)	/* Used if MT.  */
# define STKOFF_SVMSK	(STKOFF_SC_ERR + SZREG)	/* Used if MT.  */

# ifdef __PIC__
#  define STKOFF_GP	(STKOFF_SVMSK + SZREG)	/* Always used.  */
#  define STKSPACE	(STKOFF_GP + SZREG)
# else
#  define STKSPACE	(STKOFF_SVMSK + SZREG)
# endif

# define SAVESTK 	PTR_SUBU sp, STKSPACE; cfi_adjust_cfa_offset(STKSPACE)
# define RESTORESTK 	PTR_ADDU sp, STKSPACE; cfi_adjust_cfa_offset(-STKSPACE)

# ifdef __PIC__
#  define PSEUDO_JMP(sym) PTR_LA t9, sym; jalr t9
# else
#  define PSEUDO_JMP(sym) jal sym
# endif

# if IS_IN (libpthread)
#  define CENABLE	PSEUDO_JMP (__pthread_enable_asynccancel)
#  define CDISABLE	PSEUDO_JMP (__pthread_disable_asynccancel)
# elif IS_IN (librt)
#  define CENABLE	PSEUDO_JMP (__librt_enable_asynccancel)
#  define CDISABLE	PSEUDO_JMP (__librt_disable_asynccancel)
# else
#  define CENABLE	PSEUDO_JMP (__libc_enable_asynccancel)
#  define CDISABLE	PSEUDO_JMP (__libc_disable_asynccancel)
# endif

# ifndef __ASSEMBLER__
#  define SINGLE_THREAD_P						\
	__builtin_expect (THREAD_GETMEM (THREAD_SELF,			\
					 header.multiple_threads)	\
			  == 0, 1)
# else
#  define SINGLE_THREAD_P(reg)						\
	READ_THREAD_POINTER(reg);					\
	lw reg, MULTIPLE_THREADS_OFFSET(reg)
#endif

#elif !defined __ASSEMBLER__

# define SINGLE_THREAD_P 1
# define NO_CANCELLATION 1

#endif

#ifndef __ASSEMBLER__
# define RTLD_SINGLE_THREAD_P \
  __builtin_expect (THREAD_GETMEM (THREAD_SELF, \
				   header.multiple_threads) == 0, 1)
#endif