summaryrefslogtreecommitdiff
path: root/libmudflap/mf-hooks3.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmudflap/mf-hooks3.c')
-rw-r--r--libmudflap/mf-hooks3.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/libmudflap/mf-hooks3.c b/libmudflap/mf-hooks3.c
new file mode 100644
index 000000000..79a5d5e8d
--- /dev/null
+++ b/libmudflap/mf-hooks3.c
@@ -0,0 +1,284 @@
+/* Mudflap: narrow-pointer bounds-checking by tree rewriting.
+ Copyright (C) 2002, 2003, 2004, 2005, 2009
+ Free Software Foundation, Inc.
+ Contributed by Frank Ch. Eigler <fche@redhat.com>
+ and Graydon Hoare <graydon@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+#include "config.h"
+
+#ifndef HAVE_SOCKLEN_T
+#define socklen_t int
+#endif
+
+/* These attempt to coax various unix flavours to declare all our
+ needed tidbits in the system headers. */
+#if !defined(__FreeBSD__) && !defined(__APPLE__)
+#define _POSIX_SOURCE
+#endif /* Some BSDs break <sys/socket.h> if this is defined. */
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE
+#define _BSD_TYPES
+#define __EXTENSIONS__
+#define _ALL_SOURCE
+#define _LARGE_FILE_API
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "mf-runtime.h"
+#include "mf-impl.h"
+
+#ifdef _MUDFLAP
+#error "Do not compile this file with -fmudflap!"
+#endif
+
+#ifndef LIBMUDFLAPTH
+#error "pthreadstuff is to be included only in libmudflapth"
+#endif
+
+/* ??? Why isn't this done once in the header files. */
+DECLARE(void *, malloc, size_t sz);
+DECLARE(void, free, void *ptr);
+DECLARE(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr,
+ void * (*start) (void *), void *arg);
+
+
+/* Multithreading support hooks. */
+
+
+#if !defined(HAVE_TLS) || defined(USE_EMUTLS)
+/* We don't have TLS. Ordinarily we could use pthread keys, but since we're
+ commandeering malloc/free that presents a few problems. The first is that
+ we'll recurse from __mf_get_state to pthread_setspecific to malloc back to
+ __mf_get_state during thread startup. This can be solved with clever uses
+ of a mutex. The second problem is that thread shutdown is indistinguishable
+ from thread startup, since libpthread is deallocating our state variable.
+ I've no good solution for this.
+
+ Which leaves us to handle this mess by totally by hand. */
+
+/* Yes, we want this prime. If pthread_t is a pointer, it's almost always
+ page aligned, and if we use a smaller power of 2, this results in "%N"
+ being the worst possible hash -- all threads hash to zero. */
+#define LIBMUDFLAPTH_THREADS_MAX 1021
+
+struct mf_thread_data
+{
+ pthread_t self;
+ unsigned char used_p;
+ unsigned char state;
+};
+
+static struct mf_thread_data mf_thread_data[LIBMUDFLAPTH_THREADS_MAX];
+static pthread_mutex_t mf_thread_data_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define PTHREAD_HASH(p) ((unsigned long) (p) % LIBMUDFLAPTH_THREADS_MAX)
+
+static struct mf_thread_data *
+__mf_find_threadinfo (int alloc)
+{
+ pthread_t self = pthread_self ();
+ unsigned long hash = PTHREAD_HASH (self);
+ unsigned long rehash;
+
+#ifdef __alpha__
+ /* Alpha has the loosest memory ordering rules of all. We need a memory
+ barrier to flush the reorder buffer before considering a *read* of a
+ shared variable. Since we're not always taking a lock, we have to do
+ this by hand. */
+ __sync_synchronize ();
+#endif
+
+ rehash = hash;
+ while (1)
+ {
+ if (mf_thread_data[rehash].used_p && mf_thread_data[rehash].self == self)
+ return &mf_thread_data[rehash];
+
+ rehash += 7;
+ if (rehash >= LIBMUDFLAPTH_THREADS_MAX)
+ rehash -= LIBMUDFLAPTH_THREADS_MAX;
+ if (rehash == hash)
+ break;
+ }
+
+ if (alloc)
+ {
+ pthread_mutex_lock (&mf_thread_data_lock);
+
+ rehash = hash;
+ while (1)
+ {
+ if (!mf_thread_data[rehash].used_p)
+ {
+ mf_thread_data[rehash].self = self;
+ __sync_synchronize ();
+ mf_thread_data[rehash].used_p = 1;
+
+ pthread_mutex_unlock (&mf_thread_data_lock);
+ return &mf_thread_data[rehash];
+ }
+
+ rehash += 7;
+ if (rehash >= LIBMUDFLAPTH_THREADS_MAX)
+ rehash -= LIBMUDFLAPTH_THREADS_MAX;
+ if (rehash == hash)
+ break;
+ }
+
+ pthread_mutex_unlock (&mf_thread_data_lock);
+ }
+
+ return NULL;
+}
+
+enum __mf_state_enum
+__mf_get_state (void)
+{
+ struct mf_thread_data *data = __mf_find_threadinfo (0);
+ if (data)
+ return data->state;
+
+ /* If we've never seen this thread before, consider it to be in the
+ reentrant state. The state gets reset to active for the main thread
+ in __mf_init, and for child threads in __mf_pthread_spawner.
+
+ The trickiest bit here is that the LinuxThreads pthread_manager thread
+ should *always* be considered to be reentrant, so that none of our
+ hooks actually do anything. Why? Because that thread isn't a real
+ thread from the point of view of the thread library, and so lots of
+ stuff isn't initialized, leading to SEGV very quickly. Even calling
+ pthread_self is a bit suspect, but it happens to work. */
+
+ return reentrant;
+}
+
+void
+__mf_set_state (enum __mf_state_enum new_state)
+{
+ struct mf_thread_data *data = __mf_find_threadinfo (1);
+ data->state = new_state;
+}
+#endif
+
+/* The following two functions are used only with __mf_opts.heur_std_data.
+ We're interested in recording the location of the thread-local errno
+ variable.
+
+ Note that this doesn't handle TLS references in general; we have no
+ visibility into __tls_get_data for when that memory is allocated at
+ runtime. Hopefully we get to see the malloc or mmap operation that
+ eventually allocates the backing store. */
+
+/* Describe the startup information for a new user thread. */
+struct mf_thread_start_info
+{
+ /* The user's thread entry point and argument. */
+ void * (*user_fn)(void *);
+ void *user_arg;
+};
+
+
+static void
+__mf_pthread_cleanup (void *arg)
+{
+ if (__mf_opts.heur_std_data)
+ __mf_unregister (&errno, sizeof (errno), __MF_TYPE_GUESS);
+
+#if !defined(HAVE_TLS) || defined(USE_EMUTLS)
+ struct mf_thread_data *data = __mf_find_threadinfo (0);
+ if (data)
+ data->used_p = 0;
+#endif
+}
+
+
+static void *
+__mf_pthread_spawner (void *arg)
+{
+ void *result = NULL;
+
+ __mf_set_state (active);
+
+ /* NB: We could use __MF_TYPE_STATIC here, but we guess that the thread
+ errno is coming out of some dynamically allocated pool that we already
+ know of as __MF_TYPE_HEAP. */
+ if (__mf_opts.heur_std_data)
+ __mf_register (&errno, sizeof (errno), __MF_TYPE_GUESS,
+ "errno area (thread)");
+
+ /* We considered using pthread_key_t objects instead of these
+ cleanup stacks, but they were less cooperative with the
+ interposed malloc hooks in libmudflap. */
+ /* ??? The pthread_key_t problem is solved above... */
+ pthread_cleanup_push (__mf_pthread_cleanup, NULL);
+
+ /* Extract given entry point and argument. */
+ struct mf_thread_start_info *psi = arg;
+ void * (*user_fn)(void *) = psi->user_fn;
+ void *user_arg = psi->user_arg;
+ CALL_REAL (free, arg);
+
+ result = (*user_fn)(user_arg);
+
+ pthread_cleanup_pop (1 /* execute */);
+
+ return result;
+}
+
+
+#if PIC
+/* A special bootstrap variant. */
+int
+__mf_0fn_pthread_create (pthread_t *thr, const pthread_attr_t *attr,
+ void * (*start) (void *), void *arg)
+{
+ return -1;
+}
+#endif
+
+
+#undef pthread_create
+WRAPPER(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr,
+ void * (*start) (void *), void *arg)
+{
+ struct mf_thread_start_info *si;
+
+ TRACE ("pthread_create\n");
+
+ /* Fill in startup-control fields. */
+ si = CALL_REAL (malloc, sizeof (*si));
+ si->user_fn = start;
+ si->user_arg = arg;
+
+ /* Actually create the thread. */
+ return CALL_REAL (pthread_create, thr, attr, __mf_pthread_spawner, si);
+}