initial commit
[glibc.git] / sysdeps / unix / bsd / bsd4.4 / kfreebsd / i386 / start_thread.S
1 /* Copyright (C) 2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Bruno Haible <bruno@clisp.org>, 2002.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
19
20 /* __start_thread (flags, stack, func, arg)
21 calls __rfork (flags), and in the child sets the stack pointer and then
22 calls _exit (func (arg)).
23 It cannot be done in portable C. */
24
25 #include <sysdep.h>
26 #include <asm-syntax.h>
27 #define SIG_SETMASK 3
28
29 .text
30 ENTRY (__start_thread)
31 /* End FDE now, because in the child the unwind info will be
32 wrong. */
33 cfi_endproc
34
35 /* There is a window of a few instructions, right after the rfork
36 system call, where the handling of a signal would write garbage
37 into the stack shared by the parent and the child (assuming
38 RFMEM is set in flags). To solve this: 1. We block all signals
39 around the rfork system call and unblock them afterwards in
40 the parent and in the child (but only after changing the stack
41 pointer). 2. The child accesses only values passed in registers
42 and on its own stack. This way, if the parent is scheduled to
43 run first, and handles a signal, it will not affect the child;
44 and if the child runs first, and handles a signal, it will use
45 the child's stack and not affect the parent.
46 We need to pass 7 words of info to the child: stack, func, arg,
47 and the signal mask to restore. Since we have only 4 call-saved
48 registers available (%ebx, %esi, %edi, %ebp), we pass only the
49 stack pointer in a register, and the rest through the child's
50 stack. */
51 pushl %ebp
52 movl %esp, %ebp
53 subl $36, %esp
54 movl %ebx, 32(%esp)
55
56 movl 8(%ebp), %eax /* flags */
57 testl $32, %eax /* flags & RFMEM */
58 jz L(simple)
59
60 /* Block all signals. */
61 movl $-1, %eax
62 movl %eax, 16(%esp)
63 movl %eax, 20(%esp)
64 movl %eax, 24(%esp)
65 movl %eax, 28(%esp)
66 leal 16(%esp), %eax
67 movl $SIG_SETMASK, 4(%esp)
68 movl %eax, 8(%esp)
69 movl %eax, 12(%esp)
70 DO_CALL (sigprocmask, 3)
71 jb L(error)
72
73 /* Copy all info to the child's stack. */
74 movl 12(%ebp), %ebx /* stack */
75 subl $32, %ebx /* room for func, arg, sigset_t */
76 andl $-16, %ebx /* make it 16-bytes aligned */
77 movl 16(%ebp), %eax /* func */
78 movl 20(%ebp), %edx /* arg */
79 movl %eax, 4(%ebx)
80 movl %edx, (%ebx)
81 movl 16(%esp), %eax /* sigset_t word 0 */
82 movl 20(%esp), %edx /* sigset_t word 1 */
83 movl %eax, 16(%ebx)
84 movl %edx, 20(%ebx)
85 movl 24(%esp), %eax /* sigset_t word 2 */
86 movl 28(%esp), %edx /* sigset_t word 3 */
87 movl %eax, 24(%ebx)
88 movl %edx, 28(%ebx)
89
90 /* Perform the rfork system call. */
91 movl 8(%ebp), %eax /* flags */
92 movl %eax, 4(%esp)
93 DO_CALL (rfork, 1)
94 jb L(error_unmask)
95
96 /* %edx is now 0 for the parent and 1 for the child. */
97 testl %edx, %edx
98 jnz L(child)
99
100 /* Save the child pid, currently in %eax. */
101 movl %eax, %ebx
102
103 /* Restore the previous signal mask. */
104 leal 16(%esp), %eax
105 movl $SIG_SETMASK, 4(%esp)
106 movl %eax, 8(%esp)
107 movl $0, 12(%esp)
108 DO_CALL (sigprocmask, 3)
109
110 /* Return the child pid, currently in %ebx. */
111 movl %ebx, %eax
112 addl $32, %esp
113 popl %ebx
114 popl %ebp
115 ret
116
117 L(child):/* Here we are in the child thread. */
118
119 /* Set the stack pointer. */
120 movl %ebx, %esp
121 /* Terminate the stack frame. */
122 subl %ebp, %ebp
123
124 movl 4(%esp), %edi
125
126 /* Restore the previous signal mask. */
127 leal 16(%esp), %eax
128 movl $SIG_SETMASK, 4(%esp)
129 movl %eax, 8(%esp)
130 movl $0, 12(%esp)
131 DO_CALL (sigprocmask, 3)
132
133 L(child1):
134 /* Call func (arg). */
135 call *%edi
136
137 /* Call _exit. */
138 #ifdef PIC
139 call L(here)
140 L(here):
141 popl %ebx
142 addl $_GLOBAL_OFFSET_TABLE_+[.-L(here)], %ebx
143 #endif
144 pushl %eax
145 call HIDDEN_JUMPTARGET (_exit)
146
147 L(simple):/* Simple case without signal mask handling. */
148
149 /* Copy all info to the child's stack. */
150 movl 12(%ebp), %ebx /* stack */
151 subl $8, %ebx /* room for func, arg */
152 andl $-16, %ebx /* make it 16-bytes aligned */
153 movl 16(%ebp), %eax /* func */
154 movl 20(%ebp), %edx /* arg */
155 movl %eax, 4(%ebx)
156 movl %edx, (%ebx)
157
158 /* Perform the rfork system call. */
159 movl 8(%ebp), %eax /* flags */
160 movl %eax, 4(%esp)
161 DO_CALL (rfork, 1)
162 jb L(error)
163
164 /* %edx is now 0 for the parent and 1 for the child. */
165 testl %edx, %edx
166 jnz L(simple_child)
167
168 /* Return the child pid, currently in %eax. */
169 addl $32, %esp
170 popl %ebx
171 popl %ebp
172 L(pseudo_end):
173 ret
174
175 L(simple_child):/* Here we are in the child thread. */
176
177 /* Set the stack pointer. */
178 movl %ebx, %esp
179 /* Terminate the stack frame. */
180 subl %ebp, %ebp
181
182 movl 4(%esp), %edi
183
184 jmp L(child1)
185
186 L(error_unmask):
187
188 /* Save the error code, currently in %eax. */
189 movl %eax, %ebx
190
191 /* Restore the previous signal mask. */
192 leal 16(%esp), %eax
193 movl $SIG_SETMASK, 4(%esp)
194 movl %eax, 8(%esp)
195 movl $0, 12(%esp)
196 DO_CALL (sigprocmask, 3)
197
198 /* load saved error code */
199 movl %ebx, %eax
200
201 L(error):
202 addl $32, %esp
203 popl %ebx
204 popl %ebp
205 jmp SYSCALL_ERROR_LABEL
206 cfi_startproc
207 PSEUDO_END (__start_thread)