Home | History | Annotate | Line # | Download | only in libgcc
      1 /* Thread library support for -fsplit-stack.  */
      2 /* Copyright (C) 2009-2024 Free Software Foundation, Inc.
      3    Contributed by Ian Lance Taylor <iant (at) 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
     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
     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 *
    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
    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