]>
Commit | Line | Data |
---|---|---|
6de9cd9a | 1 | /* Mudflap: narrow-pointer bounds-checking by tree rewriting. |
23701e90 | 2 | Copyright (C) 2002-2013 Free Software Foundation, Inc. |
6de9cd9a DN |
3 | Contributed by Frank Ch. Eigler <fche@redhat.com> |
4 | and Graydon Hoare <graydon@redhat.com> | |
5 | ||
6 | This file is part of GCC. | |
7 | ||
8 | GCC is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
748086b7 | 10 | Software Foundation; either version 3, or (at your option) any later |
6de9cd9a DN |
11 | version. |
12 | ||
6de9cd9a DN |
13 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
748086b7 JJ |
18 | Under Section 7 of GPL version 3, you are granted additional |
19 | permissions described in the GCC Runtime Library Exception, version | |
20 | 3.1, as published by the Free Software Foundation. | |
21 | ||
22 | You should have received a copy of the GNU General Public License and | |
23 | a copy of the GCC Runtime Library Exception along with this program; | |
24 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
25 | <http://www.gnu.org/licenses/>. */ | |
6de9cd9a DN |
26 | |
27 | ||
28 | #include "config.h" | |
29 | ||
30 | #ifndef HAVE_SOCKLEN_T | |
31 | #define socklen_t int | |
32 | #endif | |
33 | ||
34 | /* These attempt to coax various unix flavours to declare all our | |
35 | needed tidbits in the system headers. */ | |
36 | #if !defined(__FreeBSD__) && !defined(__APPLE__) | |
37 | #define _POSIX_SOURCE | |
38 | #endif /* Some BSDs break <sys/socket.h> if this is defined. */ | |
fb925a51 | 39 | #define _GNU_SOURCE |
6de9cd9a DN |
40 | #define _XOPEN_SOURCE |
41 | #define _BSD_TYPES | |
42 | #define __EXTENSIONS__ | |
43 | #define _ALL_SOURCE | |
44 | #define _LARGE_FILE_API | |
45 | #define _XOPEN_SOURCE_EXTENDED 1 | |
891a2e42 | 46 | #define _REENTRANT |
6de9cd9a DN |
47 | |
48 | #include <string.h> | |
49 | #include <stdio.h> | |
50 | #include <stdlib.h> | |
6de9cd9a DN |
51 | #include <unistd.h> |
52 | #include <assert.h> | |
53 | #include <errno.h> | |
7544a87f | 54 | #include <stdbool.h> |
6de9cd9a DN |
55 | |
56 | #include "mf-runtime.h" | |
57 | #include "mf-impl.h" | |
58 | ||
59 | #ifdef _MUDFLAP | |
60 | #error "Do not compile this file with -fmudflap!" | |
61 | #endif | |
62 | ||
6de9cd9a DN |
63 | #ifndef LIBMUDFLAPTH |
64 | #error "pthreadstuff is to be included only in libmudflapth" | |
65 | #endif | |
66 | ||
7544a87f RH |
67 | /* ??? Why isn't this done once in the header files. */ |
68 | DECLARE(void *, malloc, size_t sz); | |
69 | DECLARE(void, free, void *ptr); | |
70 | DECLARE(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr, | |
71 | void * (*start) (void *), void *arg); | |
6de9cd9a DN |
72 | |
73 | ||
7544a87f | 74 | /* Multithreading support hooks. */ |
6de9cd9a DN |
75 | |
76 | ||
5cf9cc96 | 77 | #if !defined(HAVE_TLS) || defined(USE_EMUTLS) |
7544a87f RH |
78 | /* We don't have TLS. Ordinarily we could use pthread keys, but since we're |
79 | commandeering malloc/free that presents a few problems. The first is that | |
80 | we'll recurse from __mf_get_state to pthread_setspecific to malloc back to | |
81 | __mf_get_state during thread startup. This can be solved with clever uses | |
82 | of a mutex. The second problem is that thread shutdown is indistinguishable | |
83 | from thread startup, since libpthread is deallocating our state variable. | |
84 | I've no good solution for this. | |
6de9cd9a | 85 | |
7544a87f | 86 | Which leaves us to handle this mess by totally by hand. */ |
6de9cd9a | 87 | |
7544a87f RH |
88 | /* Yes, we want this prime. If pthread_t is a pointer, it's almost always |
89 | page aligned, and if we use a smaller power of 2, this results in "%N" | |
90 | being the worst possible hash -- all threads hash to zero. */ | |
91 | #define LIBMUDFLAPTH_THREADS_MAX 1021 | |
6de9cd9a | 92 | |
7544a87f RH |
93 | struct mf_thread_data |
94 | { | |
95 | pthread_t self; | |
96 | unsigned char used_p; | |
97 | unsigned char state; | |
98 | }; | |
6de9cd9a | 99 | |
7544a87f RH |
100 | static struct mf_thread_data mf_thread_data[LIBMUDFLAPTH_THREADS_MAX]; |
101 | static pthread_mutex_t mf_thread_data_lock = PTHREAD_MUTEX_INITIALIZER; | |
6de9cd9a | 102 | |
7544a87f | 103 | #define PTHREAD_HASH(p) ((unsigned long) (p) % LIBMUDFLAPTH_THREADS_MAX) |
6de9cd9a | 104 | |
7544a87f RH |
105 | static struct mf_thread_data * |
106 | __mf_find_threadinfo (int alloc) | |
6de9cd9a | 107 | { |
7544a87f RH |
108 | pthread_t self = pthread_self (); |
109 | unsigned long hash = PTHREAD_HASH (self); | |
110 | unsigned long rehash; | |
111 | ||
112 | #ifdef __alpha__ | |
113 | /* Alpha has the loosest memory ordering rules of all. We need a memory | |
114 | barrier to flush the reorder buffer before considering a *read* of a | |
115 | shared variable. Since we're not always taking a lock, we have to do | |
116 | this by hand. */ | |
117 | __sync_synchronize (); | |
6de9cd9a | 118 | #endif |
6de9cd9a | 119 | |
7544a87f RH |
120 | rehash = hash; |
121 | while (1) | |
122 | { | |
123 | if (mf_thread_data[rehash].used_p && mf_thread_data[rehash].self == self) | |
124 | return &mf_thread_data[rehash]; | |
125 | ||
126 | rehash += 7; | |
127 | if (rehash >= LIBMUDFLAPTH_THREADS_MAX) | |
128 | rehash -= LIBMUDFLAPTH_THREADS_MAX; | |
129 | if (rehash == hash) | |
130 | break; | |
131 | } | |
6de9cd9a | 132 | |
7544a87f | 133 | if (alloc) |
6de9cd9a | 134 | { |
7544a87f RH |
135 | pthread_mutex_lock (&mf_thread_data_lock); |
136 | ||
137 | rehash = hash; | |
138 | while (1) | |
6de9cd9a | 139 | { |
7544a87f RH |
140 | if (!mf_thread_data[rehash].used_p) |
141 | { | |
142 | mf_thread_data[rehash].self = self; | |
143 | __sync_synchronize (); | |
144 | mf_thread_data[rehash].used_p = 1; | |
145 | ||
146 | pthread_mutex_unlock (&mf_thread_data_lock); | |
147 | return &mf_thread_data[rehash]; | |
148 | } | |
149 | ||
150 | rehash += 7; | |
151 | if (rehash >= LIBMUDFLAPTH_THREADS_MAX) | |
152 | rehash -= LIBMUDFLAPTH_THREADS_MAX; | |
153 | if (rehash == hash) | |
154 | break; | |
6de9cd9a | 155 | } |
7544a87f RH |
156 | |
157 | pthread_mutex_unlock (&mf_thread_data_lock); | |
6de9cd9a | 158 | } |
fb925a51 | 159 | |
6de9cd9a DN |
160 | return NULL; |
161 | } | |
162 | ||
7544a87f RH |
163 | enum __mf_state_enum |
164 | __mf_get_state (void) | |
6de9cd9a | 165 | { |
7544a87f RH |
166 | struct mf_thread_data *data = __mf_find_threadinfo (0); |
167 | if (data) | |
168 | return data->state; | |
169 | ||
be53afcf UW |
170 | /* If we've never seen this thread before, consider it to be in the |
171 | reentrant state. The state gets reset to active for the main thread | |
172 | in __mf_init, and for child threads in __mf_pthread_spawner. | |
7544a87f RH |
173 | |
174 | The trickiest bit here is that the LinuxThreads pthread_manager thread | |
175 | should *always* be considered to be reentrant, so that none of our | |
176 | hooks actually do anything. Why? Because that thread isn't a real | |
177 | thread from the point of view of the thread library, and so lots of | |
178 | stuff isn't initialized, leading to SEGV very quickly. Even calling | |
179 | pthread_self is a bit suspect, but it happens to work. */ | |
180 | ||
be53afcf | 181 | return reentrant; |
7544a87f | 182 | } |
6de9cd9a | 183 | |
7544a87f RH |
184 | void |
185 | __mf_set_state (enum __mf_state_enum new_state) | |
186 | { | |
187 | struct mf_thread_data *data = __mf_find_threadinfo (1); | |
188 | data->state = new_state; | |
6de9cd9a | 189 | } |
7544a87f | 190 | #endif |
6de9cd9a | 191 | |
7544a87f RH |
192 | /* The following two functions are used only with __mf_opts.heur_std_data. |
193 | We're interested in recording the location of the thread-local errno | |
194 | variable. | |
6de9cd9a | 195 | |
7544a87f RH |
196 | Note that this doesn't handle TLS references in general; we have no |
197 | visibility into __tls_get_data for when that memory is allocated at | |
198 | runtime. Hopefully we get to see the malloc or mmap operation that | |
199 | eventually allocates the backing store. */ | |
6de9cd9a | 200 | |
7544a87f RH |
201 | /* Describe the startup information for a new user thread. */ |
202 | struct mf_thread_start_info | |
6de9cd9a | 203 | { |
7544a87f RH |
204 | /* The user's thread entry point and argument. */ |
205 | void * (*user_fn)(void *); | |
206 | void *user_arg; | |
207 | }; | |
6de9cd9a DN |
208 | |
209 | ||
fb925a51 | 210 | static void |
6de9cd9a DN |
211 | __mf_pthread_cleanup (void *arg) |
212 | { | |
7544a87f RH |
213 | if (__mf_opts.heur_std_data) |
214 | __mf_unregister (&errno, sizeof (errno), __MF_TYPE_GUESS); | |
6de9cd9a | 215 | |
5cf9cc96 | 216 | #if !defined(HAVE_TLS) || defined(USE_EMUTLS) |
7544a87f RH |
217 | struct mf_thread_data *data = __mf_find_threadinfo (0); |
218 | if (data) | |
219 | data->used_p = 0; | |
220 | #endif | |
6de9cd9a DN |
221 | } |
222 | ||
223 | ||
224 | static void * | |
225 | __mf_pthread_spawner (void *arg) | |
226 | { | |
6de9cd9a DN |
227 | void *result = NULL; |
228 | ||
7544a87f | 229 | __mf_set_state (active); |
fb925a51 | 230 | |
7544a87f RH |
231 | /* NB: We could use __MF_TYPE_STATIC here, but we guess that the thread |
232 | errno is coming out of some dynamically allocated pool that we already | |
233 | know of as __MF_TYPE_HEAP. */ | |
6de9cd9a | 234 | if (__mf_opts.heur_std_data) |
7544a87f RH |
235 | __mf_register (&errno, sizeof (errno), __MF_TYPE_GUESS, |
236 | "errno area (thread)"); | |
6de9cd9a DN |
237 | |
238 | /* We considered using pthread_key_t objects instead of these | |
239 | cleanup stacks, but they were less cooperative with the | |
240 | interposed malloc hooks in libmudflap. */ | |
7544a87f RH |
241 | /* ??? The pthread_key_t problem is solved above... */ |
242 | pthread_cleanup_push (__mf_pthread_cleanup, NULL); | |
6de9cd9a | 243 | |
7544a87f RH |
244 | /* Extract given entry point and argument. */ |
245 | struct mf_thread_start_info *psi = arg; | |
246 | void * (*user_fn)(void *) = psi->user_fn; | |
247 | void *user_arg = psi->user_arg; | |
248 | CALL_REAL (free, arg); | |
fb925a51 | 249 | |
7544a87f | 250 | result = (*user_fn)(user_arg); |
6de9cd9a DN |
251 | |
252 | pthread_cleanup_pop (1 /* execute */); | |
253 | ||
6de9cd9a DN |
254 | return result; |
255 | } | |
256 | ||
257 | ||
258 | #if PIC | |
259 | /* A special bootstrap variant. */ | |
260 | int | |
fb925a51 | 261 | __mf_0fn_pthread_create (pthread_t *thr, const pthread_attr_t *attr, |
6de9cd9a DN |
262 | void * (*start) (void *), void *arg) |
263 | { | |
264 | return -1; | |
265 | } | |
266 | #endif | |
267 | ||
268 | ||
269 | #undef pthread_create | |
fb925a51 | 270 | WRAPPER(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr, |
6de9cd9a DN |
271 | void * (*start) (void *), void *arg) |
272 | { | |
22f99b82 | 273 | struct mf_thread_start_info *si; |
6de9cd9a DN |
274 | |
275 | TRACE ("pthread_create\n"); | |
276 | ||
22f99b82 UW |
277 | /* Fill in startup-control fields. */ |
278 | si = CALL_REAL (malloc, sizeof (*si)); | |
279 | si->user_fn = start; | |
280 | si->user_arg = arg; | |
6de9cd9a | 281 | |
22f99b82 UW |
282 | /* Actually create the thread. */ |
283 | return CALL_REAL (pthread_create, thr, attr, __mf_pthread_spawner, si); | |
6de9cd9a | 284 | } |