1 | /* Setup thread stack. Hurd/i386 version. |
2 | Copyright (C) 2000-2024 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
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, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <stdint.h> |
20 | #include <assert.h> |
21 | #include <mach.h> |
22 | #include <hurd.h> |
23 | |
24 | #include <pt-internal.h> |
25 | |
26 | /* The stack layout used on the x86 is: |
27 | |
28 | ----------------- |
29 | | ARG | |
30 | ----------------- |
31 | | START_ROUTINE | |
32 | ----------------- |
33 | | 0 | |
34 | ----------------- |
35 | */ |
36 | |
37 | /* Set up the stack for THREAD, such that it appears as if |
38 | START_ROUTINE and ARG were passed to the new thread's entry-point. |
39 | Return the stack pointer for the new thread. */ |
40 | static void * |
41 | stack_setup (struct __pthread *thread, |
42 | void *(*start_routine) (void *), void *arg) |
43 | { |
44 | error_t err; |
45 | uintptr_t *bottom, *top; |
46 | |
47 | /* Calculate the top of the new stack. */ |
48 | bottom = thread->stackaddr; |
49 | top = (uintptr_t *) ((uintptr_t) bottom + thread->stacksize |
50 | + ((thread->guardsize + __vm_page_size - 1) |
51 | / __vm_page_size) * __vm_page_size); |
52 | |
53 | if (start_routine != NULL) |
54 | { |
55 | /* And then the call frame. */ |
56 | top -= 3; |
57 | top = (uintptr_t *) ((uintptr_t) top & ~0xf); |
58 | top[2] = (uintptr_t) arg; /* Argument to START_ROUTINE. */ |
59 | top[1] = (uintptr_t) start_routine; |
60 | top[0] = (uintptr_t) thread; |
61 | *--top = 0; /* Fake return address. */ |
62 | } |
63 | |
64 | if (thread->guardsize) |
65 | { |
66 | err = __vm_protect (__mach_task_self (), (vm_address_t) bottom, |
67 | thread->guardsize, 0, 0); |
68 | assert_perror (err); |
69 | } |
70 | |
71 | return top; |
72 | } |
73 | |
74 | int |
75 | __pthread_setup (struct __pthread *thread, |
76 | void (*entry_point) (struct __pthread *, void *(*)(void *), |
77 | void *), void *(*start_routine) (void *), |
78 | void *arg) |
79 | { |
80 | error_t err; |
81 | |
82 | if (thread->kernel_thread == __hurd_thread_self ()) |
83 | /* Fix up the TCB for the main thread. The C library has already |
84 | installed a TCB, which we want to keep using. This TCB must not |
85 | be freed so don't register it in the thread structure. On the |
86 | other hand, it's not yet possible to reliably release a TCB. |
87 | Leave the unused one registered so that it doesn't leak. */ |
88 | return 0; |
89 | |
90 | thread->mcontext.pc = entry_point; |
91 | thread->mcontext.sp = stack_setup (thread, start_routine, arg); |
92 | |
93 | err = __thread_set_pcsptp (thread->kernel_thread, |
94 | 1, thread->mcontext.pc, |
95 | 1, thread->mcontext.sp, |
96 | 1, thread->tcb); |
97 | assert_perror (err); |
98 | |
99 | return 0; |
100 | } |
101 | |