blob: ca5677cc9a33edeebf15e7e3bfafab161a4c6c11 [file] [log] [blame]
yuezonghe824eb0c2024-06-27 02:32:26 -07001/* Special definitions for ix86 machine using segment register based
2 thread descriptor.
3 Copyright (C) 1998, 2000, 2001, 2002 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
5 Contributed by Ulrich Drepper <drepper@cygnus.com>.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; see the file COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22#ifndef __ASSEMBLER__
23#include <stddef.h> /* For offsetof. */
24#include <stdlib.h> /* For abort(). */
25
26
27/* We don't want to include the kernel header. So duplicate the
28 information. */
29
30/* Structure passed on `modify_ldt' call. */
31struct modify_ldt_ldt_s
32{
33 unsigned int entry_number;
34 unsigned long int base_addr;
35 unsigned int limit;
36 unsigned int seg_32bit:1;
37 unsigned int contents:2;
38 unsigned int read_exec_only:1;
39 unsigned int limit_in_pages:1;
40 unsigned int seg_not_present:1;
41 unsigned int useable:1;
42 unsigned int empty:25;
43};
44
45/* System call to set LDT entry. */
46extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t);
47
48
49/* Return the thread descriptor for the current thread.
50
51 The contained asm must *not* be marked volatile since otherwise
52 assignments like
53 pthread_descr self = thread_self();
54 do not get optimized away. */
55#define THREAD_SELF \
56({ \
57 register pthread_descr __self; \
58 __asm__ ("movl %%gs:%c1,%0" : "=r" (__self) \
59 : "i" (offsetof (struct _pthread_descr_struct, \
60 p_header.data.self))); \
61 __self; \
62})
63
64
65/* Initialize the thread-unique value. Two possible ways to do it. */
66
67#define DO_MODIFY_LDT(descr, nr) \
68({ \
69 struct modify_ldt_ldt_s ldt_entry = \
70 { nr, (unsigned long int) (descr), 0xfffff /* 4GB in pages */, \
71 1, 0, 0, 1, 0, 1, 0 }; \
72 if (__modify_ldt (1, &ldt_entry, sizeof (ldt_entry)) != 0) \
73 abort (); \
74 __asm__ ("movw %w0, %%gs" : : "q" (nr * 8 + 7)); \
75})
76
77#ifdef __PIC__
78# define USETLS_EBX_ARG "r"
79# define USETLS_LOAD_EBX "xchgl %3, %%ebx\n\t"
80#else
81# define USETLS_EBX_ARG "b"
82# define USETLS_LOAD_EBX
83#endif
84
85/* When using the new set_thread_area call, we don't need to change %gs
86 because we inherited the value set up in the main thread by TLS setup.
87 We need to extract that value and set up the same segment in this
88 thread. */
89#if USE_TLS
90# define DO_SET_THREAD_AREA_REUSE(nr) 1
91#else
92/* Without TLS, we do the initialization of the main thread, where NR == 0. */
93# define DO_SET_THREAD_AREA_REUSE(nr) (!__builtin_constant_p (nr) || (nr))
94#endif
95#define DO_SET_THREAD_AREA(descr, nr) \
96({ \
97 int __gs; \
98 if (DO_SET_THREAD_AREA_REUSE (nr)) \
99 { \
100 __asm__ ("movw %%gs, %w0" : "=q" (__gs)); \
101 struct modify_ldt_ldt_s ldt_entry = \
102 { (__gs & 0xffff) >> 3, \
103 (unsigned long int) (descr), 0xfffff /* 4GB in pages */, \
104 1, 0, 0, 1, 0, 1, 0 }; \
105 \
106 int __result; \
107 __asm__ (USETLS_LOAD_EBX \
108 "movl %2, %%eax\n\t" \
109 "int $0x80\n\t" \
110 USETLS_LOAD_EBX \
111 : "&a" (__result) \
112 : USETLS_EBX_ARG (&ldt_entry), "i" (__NR_set_thread_area)); \
113 if (__result == 0) \
114 __asm__ ("movw %w0, %%gs" :: "q" (__gs)); \
115 else \
116 __gs = -1; \
117 } \
118 else \
119 { \
120 struct modify_ldt_ldt_s ldt_entry = \
121 { -1, \
122 (unsigned long int) (descr), 0xfffff /* 4GB in pages */, \
123 1, 0, 0, 1, 0, 1, 0 }; \
124 int __result; \
125 __asm__ (USETLS_LOAD_EBX \
126 "movl %2, %%eax\n\t" \
127 "int $0x80\n\t" \
128 USETLS_LOAD_EBX \
129 : "&a" (__result) \
130 : USETLS_EBX_ARG (&ldt_entry), "i" (__NR_set_thread_area)); \
131 if (__result == 0) \
132 { \
133 __gs = (ldt_entry.entry_number << 3) + 3; \
134 __asm__ ("movw %w0, %%gs" : : "q" (__gs)); \
135 } \
136 else \
137 __gs = -1; \
138 } \
139 __gs; \
140})
141
142#if defined __ASSUME_SET_THREAD_AREA_SYSCALL
143# define INIT_THREAD_SELF(descr, nr) DO_SET_THREAD_AREA (descr, nr)
144#elif defined __NR_set_thread_area
145# define INIT_THREAD_SELF(descr, nr) \
146({ \
147 if (__builtin_expect (__have_no_set_thread_area, 0) \
148 || (DO_SET_THREAD_AREA (descr, DO_SET_THREAD_AREA_REUSE (nr)) == -1 \
149 && (__have_no_set_thread_area = 1))) \
150 DO_MODIFY_LDT (descr, nr); \
151})
152/* Defined in pspinlock.c. */
153extern int __have_no_set_thread_area;
154#else
155# define INIT_THREAD_SELF(descr, nr) DO_MODIFY_LDT (descr, nr)
156#endif
157
158/* Free resources associated with thread descriptor. */
159#ifdef __ASSUME_SET_THREAD_AREA_SYSCALL
160#define FREE_THREAD(descr, nr) do { } while (0)
161#elif defined __NR_set_thread_area
162#define FREE_THREAD(descr, nr) \
163{ \
164 int __gs; \
165 __asm__ __volatile__ ("movw %%gs, %w0" : "=q" (__gs)); \
166 if (__builtin_expect (__gs & 4, 0)) \
167 { \
168 struct modify_ldt_ldt_s ldt_entry = \
169 { nr, 0, 0, 0, 0, 1, 0, 1, 0, 0 }; \
170 __modify_ldt (1, &ldt_entry, sizeof (ldt_entry)); \
171 } \
172}
173#else
174#define FREE_THREAD(descr, nr) \
175{ \
176 struct modify_ldt_ldt_s ldt_entry = \
177 { nr, 0, 0, 0, 0, 1, 0, 1, 0, 0 }; \
178 __modify_ldt (1, &ldt_entry, sizeof (ldt_entry)); \
179}
180#endif
181
182/* Read member of the thread descriptor directly. */
183#define THREAD_GETMEM(descr, member) \
184({ \
185 __typeof__ (descr->member) __value; \
186 if (sizeof (__value) == 1) \
187 __asm__ __volatile__ ("movb %%gs:%P2,%b0" \
188 : "=q" (__value) \
189 : "0" (0), \
190 "i" (offsetof (struct _pthread_descr_struct, \
191 member))); \
192 else if (sizeof (__value) == 4) \
193 __asm__ __volatile__ ("movl %%gs:%P1,%0" \
194 : "=r" (__value) \
195 : "i" (offsetof (struct _pthread_descr_struct, \
196 member))); \
197 else \
198 { \
199 if (sizeof (__value) != 8) \
200 /* There should not be any value with a size other than 1, 4 or 8. */\
201 abort (); \
202 \
203 __asm__ __volatile__ ("movl %%gs:%P1,%%eax\n\t" \
204 "movl %%gs:%P2,%%edx" \
205 : "=A" (__value) \
206 : "i" (offsetof (struct _pthread_descr_struct, \
207 member)), \
208 "i" (offsetof (struct _pthread_descr_struct, \
209 member) + 4)); \
210 } \
211 __value; \
212})
213
214/* Same as THREAD_GETMEM, but the member offset can be non-constant. */
215#define THREAD_GETMEM_NC(descr, member) \
216({ \
217 __typeof__ (descr->member) __value; \
218 if (sizeof (__value) == 1) \
219 __asm__ __volatile__ ("movb %%gs:(%2),%b0" \
220 : "=q" (__value) \
221 : "0" (0), \
222 "r" (offsetof (struct _pthread_descr_struct, \
223 member))); \
224 else if (sizeof (__value) == 4) \
225 __asm__ __volatile__ ("movl %%gs:(%1),%0" \
226 : "=r" (__value) \
227 : "r" (offsetof (struct _pthread_descr_struct, \
228 member))); \
229 else \
230 { \
231 if (sizeof (__value) != 8) \
232 /* There should not be any value with a size other than 1, 4 or 8. */\
233 abort (); \
234 \
235 __asm__ __volatile__ ("movl %%gs:(%1),%%eax\n\t" \
236 "movl %%gs:4(%1),%%edx" \
237 : "=&A" (__value) \
238 : "r" (offsetof (struct _pthread_descr_struct, \
239 member))); \
240 } \
241 __value; \
242})
243
244/* Same as THREAD_SETMEM, but the member offset can be non-constant. */
245#define THREAD_SETMEM(descr, member, value) \
246({ \
247 __typeof__ (descr->member) __value = (value); \
248 if (sizeof (__value) == 1) \
249 __asm__ __volatile__ ("movb %0,%%gs:%P1" : \
250 : "q" (__value), \
251 "i" (offsetof (struct _pthread_descr_struct, \
252 member))); \
253 else if (sizeof (__value) == 4) \
254 __asm__ __volatile__ ("movl %0,%%gs:%P1" : \
255 : "r" (__value), \
256 "i" (offsetof (struct _pthread_descr_struct, \
257 member))); \
258 else \
259 { \
260 if (sizeof (__value) != 8) \
261 /* There should not be any value with a size other than 1, 4 or 8. */\
262 abort (); \
263 \
264 __asm__ __volatile__ ("movl %%eax,%%gs:%P1\n\n" \
265 "movl %%edx,%%gs:%P2" : \
266 : "A" (__value), \
267 "i" (offsetof (struct _pthread_descr_struct, \
268 member)), \
269 "i" (offsetof (struct _pthread_descr_struct, \
270 member) + 4)); \
271 } \
272})
273
274/* Set member of the thread descriptor directly. */
275#define THREAD_SETMEM_NC(descr, member, value) \
276({ \
277 __typeof__ (descr->member) __value = (value); \
278 if (sizeof (__value) == 1) \
279 __asm__ __volatile__ ("movb %0,%%gs:(%1)" : \
280 : "q" (__value), \
281 "r" (offsetof (struct _pthread_descr_struct, \
282 member))); \
283 else if (sizeof (__value) == 4) \
284 __asm__ __volatile__ ("movl %0,%%gs:(%1)" : \
285 : "r" (__value), \
286 "r" (offsetof (struct _pthread_descr_struct, \
287 member))); \
288 else \
289 { \
290 if (sizeof (__value) != 8) \
291 /* There should not be any value with a size other than 1, 4 or 8. */\
292 abort (); \
293 \
294 __asm__ __volatile__ ("movl %%eax,%%gs:(%1)\n\t" \
295 "movl %%edx,%%gs:4(%1)" : \
296 : "A" (__value), \
297 "r" (offsetof (struct _pthread_descr_struct, \
298 member))); \
299 } \
300})
301#endif
302
303/* We want the OS to assign stack addresses. */
304#define FLOATING_STACKS 1
305
306/* Maximum size of the stack if the rlimit is unlimited. */
307#define ARCH_STACK_MAX_SIZE 8*1024*1024