xref: /netbsd-src/external/gpl3/gcc/dist/libgcc/generic-morestack-thread.c (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* Thread library support for -fsplit-stack.  */
2 /* Copyright (C) 2009-2022 Free Software Foundation, Inc.
3    Contributed by Ian Lance Taylor <iant@google.com>.
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11 
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 Under Section 7 of GPL version 3, you are granted additional
18 permissions described in the GCC Runtime Library Exception, version
19 3.1, as published by the Free Software Foundation.
20 
21 You should have received a copy of the GNU General Public License and
22 a copy of the GCC Runtime Library Exception along with this program;
23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24 <http://www.gnu.org/licenses/>.  */
25 
26 #include "tconfig.h"
27 #include "tsystem.h"
28 #include "coretypes.h"
29 #include "tm.h"
30 #include "libgcc_tm.h"
31 
32 /* If inhibit_libc is defined, we cannot compile this file.  The
33    effect is that people will not be able to use -fsplit-stack.  That
34    is much better than failing the build particularly since people
35    will want to define inhibit_libc while building a compiler which
36    can build glibc.  */
37 
38 #ifndef inhibit_libc
39 
40 #include <errno.h>
41 #include <signal.h>
42 #include <pthread.h>
43 
44 #include "generic-morestack.h"
45 
46 /* We declare the pthread functions we need as weak, so that
47    libgcc_s.so does not need to be linked against -lpthread.  */
48 
49 extern int pthread_once (pthread_once_t *, void (*) (void))
50   __attribute__ ((weak));
51 
52 extern int pthread_key_create (pthread_key_t *, void (*) (void *))
53   __attribute__ ((weak));
54 
55 extern int pthread_setspecific (pthread_key_t, const void *)
56   __attribute__ ((weak));
57 
58 extern int pthread_sigmask (int, const sigset_t *, sigset_t *)
59   __attribute__ ((weak));
60 
61 /* The key for the list of stack segments to free when the thread
62    exits.  This is created by pthread_key_create.  */
63 
64 static pthread_key_t segment_list_key;
65 
66 /* Used to only run create_key once.  */
67 
68 static pthread_once_t create_key_once = PTHREAD_ONCE_INIT;
69 
70 /* Release all the segments for a thread.  This is the destructor
71    function used by pthread_key_create, and is called when a thread
72    exits.  */
73 
74 static void
free_segments(void * arg)75 free_segments (void* arg)
76 {
77   /* We must block signals in case the signal handler tries to split
78      the stack.  We leave them blocked while the thread exits.  */
79   if (pthread_sigmask)
80     {
81       sigset_t mask;
82 
83       sigfillset (&mask);
84       pthread_sigmask (SIG_BLOCK, &mask, NULL);
85     }
86 
87   __morestack_release_segments ((struct stack_segment **) arg, 1);
88 }
89 
90 /* Set up the key for the list of segments.  This is called via
91    pthread_once.  */
92 
93 static void
create_key(void)94 create_key (void)
95 {
96   int err;
97 
98   err = pthread_key_create (&segment_list_key, free_segments);
99   if (err != 0)
100     {
101       static const char msg[] = "pthread_key_create failed: errno ";
102       __morestack_fail (msg, sizeof msg - 1, err);
103     }
104 }
105 
106 /* Pass information from the pthread_create wrapper to
107    stack_split_initialize_thread.  */
108 
109 struct pthread_create_args
110 {
111   void *(*start_routine) (void *);
112   void *arg;
113 };
114 
115 /* Initialize a thread.  This is called via pthread_create.  It calls
116    a target dependent function to set up any required stack guard.  */
117 
118 static void* stack_split_initialize_thread (void *)
119   __attribute__ ((no_split_stack));
120 
121 static void *
stack_split_initialize_thread(void * varg)122 stack_split_initialize_thread (void *varg)
123 {
124   struct pthread_create_args *args = (struct pthread_create_args *) varg;
125   int err;
126   void *(*start_routine) (void *);
127   void *arg;
128 
129   __stack_split_initialize ();
130 
131   err = pthread_setspecific (segment_list_key, (void *) &__morestack_segments);
132   if (err != 0)
133     {
134       static const char msg[] = "pthread_setspecific failed: errno ";
135       __morestack_fail (msg, sizeof msg - 1, err);
136     }
137 
138   start_routine = args->start_routine;
139   arg = args->arg;
140   free (args);
141   return (*start_routine) (arg);
142 }
143 
144 /* This function wraps calls to pthread_create to make sure that the
145    stack guard is initialized for new threads.  FIXME: This hack will
146    not be necessary if glibc supports -fsplit-stack directly.  */
147 
148 int __wrap_pthread_create (pthread_t *, const pthread_attr_t *,
149 			   void *(*start_routine) (void *), void *)
150   __attribute__ ((visibility ("hidden")));
151 
152 extern int __real_pthread_create (pthread_t *, const pthread_attr_t *,
153 				  void *(*start_routine) (void *), void *)
154   __attribute__ ((weak));
155 
156 int
__wrap_pthread_create(pthread_t * tid,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)157 __wrap_pthread_create (pthread_t *tid, const pthread_attr_t *attr,
158 		       void *(*start_routine) (void *), void *arg)
159 {
160   int err;
161   struct pthread_create_args* args;
162 
163   err = pthread_once (&create_key_once, create_key);
164   if (err != 0)
165     {
166       static const char msg[] = "pthread_once failed: errno ";
167       __morestack_fail (msg, sizeof msg - 1, err);
168     }
169 
170   args = malloc (sizeof (struct pthread_create_args));
171   if (args == NULL)
172     return EAGAIN;
173   args->start_routine = start_routine;
174   args->arg = arg;
175   return __real_pthread_create (tid, attr, stack_split_initialize_thread, args);
176 }
177 
178 #endif /* !defined (inhibit_libc) */
179