mach_dep.c raw

   1  /*
   2   * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
   3   * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
   4   * Copyright (c) 2008-2022 Ivan Maidanski
   5   *
   6   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   7   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   8   *
   9   * Permission is hereby granted to use or copy this program
  10   * for any purpose, provided the above notices are retained on all copies.
  11   * Permission to modify the code and to distribute modified code is granted,
  12   * provided the above notices are retained, and a notice that the code was
  13   * modified is included with the above copyright notice.
  14   */
  15  
  16  #include "private/gc_priv.h"
  17  
  18  #if !defined(PLATFORM_MACH_DEP) && !defined(SN_TARGET_PSP2)
  19  
  20  #  if defined(IA64) && !defined(THREADS)
  21  GC_INNER ptr_t GC_save_regs_ret_val = NULL;
  22  #  endif
  23  
  24  #  if defined(UNIX_LIKE) && !defined(STACK_NOT_SCANNED)
  25  #    include <signal.h>
  26  #    ifndef NO_GETCONTEXT
  27  #      if defined(DARWIN)                  \
  28            && (MAC_OS_X_VERSION_MAX_ALLOWED \
  29                >= 1060 /* `MAC_OS_X_VERSION_10_6` */)
  30  #        include <sys/ucontext.h>
  31  #      else
  32  #        include <ucontext.h>
  33  #      endif /* !DARWIN */
  34  #      ifdef GETCONTEXT_FPU_EXCMASK_BUG
  35  #        include <fenv.h>
  36  #      endif
  37  #    endif /* !NO_GETCONTEXT */
  38  #  endif
  39  
  40  GC_ATTR_NOINLINE GC_ATTR_NO_SANITIZE_ADDR GC_INNER void
  41  GC_with_callee_saves_pushed(GC_with_callee_saves_func fn, ptr_t arg)
  42  {
  43    volatile int dummy;
  44    volatile ptr_t context = 0;
  45  #  if defined(EMSCRIPTEN) || defined(HAVE_BUILTIN_UNWIND_INIT)               \
  46        || defined(STACK_NOT_SCANNED) || (defined(NO_CRT) && defined(MSWIN32)) \
  47        || !defined(NO_UNDERSCORE_SETJMP)
  48  #    define volatile_arg arg
  49  #  else
  50    /*
  51     * Note: `volatile` to avoid "arg might be clobbered by setjmp" warning
  52     * produced by some compilers.
  53     */
  54    volatile ptr_t volatile_arg = arg;
  55  #  endif
  56  
  57  #  if defined(EMSCRIPTEN) || defined(STACK_NOT_SCANNED)
  58    /* No-op, "registers" are pushed in `GC_push_other_roots()`. */
  59  #  else
  60  #    if defined(UNIX_LIKE) && !defined(NO_GETCONTEXT)
  61    /*
  62     * Older versions of Darwin seem to lack `getcontext()`.
  63     * Linux on ARM and MIPS often does not provide a real `getcontext()`.
  64     */
  65  
  66    /* The variable is set to -1 (means broken) or 1 (means it works). */
  67    static signed char getcontext_works = 0;
  68    ucontext_t ctxt;
  69  #      ifdef GETCONTEXT_FPU_EXCMASK_BUG
  70    /*
  71     * Workaround a bug (clearing the FPU exception mask) in `getcontext`
  72     * on Linux/x86_64.
  73     */
  74  #        ifdef X86_64
  75    /*
  76     * We manipulate FPU control word here just not to force the client
  77     * application to use `-lm` linker option.
  78     */
  79    unsigned short old_fcw;
  80  
  81  #          if defined(CPPCHECK)
  82    GC_noop1_ptr(&old_fcw);
  83  #          endif
  84    __asm__ __volatile__("fstcw %0" : "=m"(*&old_fcw));
  85  #        else
  86    int except_mask = fegetexcept();
  87  #        endif
  88  #      endif
  89  
  90    if (getcontext_works >= 0) {
  91      if (getcontext(&ctxt) < 0) {
  92        WARN("getcontext failed:"
  93             " using another register retrieval method...\n",
  94             0);
  95        /*
  96         * `getcontext()` is broken, do not try again.
  97         * E.g., to workaround a bug in Docker `ubuntu_32bit`.
  98         */
  99      } else {
 100        context = (ptr_t)&ctxt;
 101      }
 102      if (UNLIKELY(0 == getcontext_works))
 103        getcontext_works = context != NULL ? 1 : -1;
 104    }
 105  #      ifdef GETCONTEXT_FPU_EXCMASK_BUG
 106  #        ifdef X86_64
 107    __asm__ __volatile__("fldcw %0" : : "m"(*&old_fcw));
 108    {
 109      unsigned mxcsr;
 110      /* And now correct the exception mask in SSE `mxcsr`. */
 111      __asm__ __volatile__("stmxcsr %0" : "=m"(*&mxcsr));
 112      mxcsr = (mxcsr & ~(FE_ALL_EXCEPT << 7)) | ((old_fcw & FE_ALL_EXCEPT) << 7);
 113      __asm__ __volatile__("ldmxcsr %0" : : "m"(*&mxcsr));
 114    }
 115  #        else /* !X86_64 */
 116    if (feenableexcept(except_mask) < 0)
 117      ABORT("feenableexcept failed");
 118  #        endif
 119  #      endif /* GETCONTEXT_FPU_EXCMASK_BUG */
 120  #      if defined(IA64) || defined(SPARC)
 121    /*
 122     * On a register-window machine, we need to save register contents on
 123     * the stack for this to work.  This may already be subsumed by the
 124     * `getcontext()` call.
 125     */
 126  #        if defined(IA64) && !defined(THREADS)
 127    GC_save_regs_ret_val =
 128  #        endif
 129        GC_save_regs_in_stack();
 130  #      endif
 131    if (NULL == context) /*< `getcontext` failed */
 132  #    endif /* !NO_GETCONTEXT */
 133    {
 134  #    if defined(HAVE_BUILTIN_UNWIND_INIT)
 135      /*
 136       * This was suggested as the way to force callee-save registers and
 137       * register windows onto the stack.
 138       */
 139      __builtin_unwind_init();
 140  #    elif defined(NO_CRT) && defined(MSWIN32)
 141      CONTEXT ctx;
 142  
 143      RtlCaptureContext(&ctx);
 144  #    else
 145      /* Generic code. */
 146      jmp_buf regs;
 147  
 148      /*
 149       * `setjmp()` does not always clear all of the buffer.
 150       * That tends to preserve garbage.  Clear it.
 151       */
 152      BZERO(regs, sizeof(regs));
 153  #      ifdef NO_UNDERSCORE_SETJMP
 154      (void)setjmp(regs);
 155  #      else
 156      /*
 157       * We do not want to mess with signals.  According to the SUSv3
 158       * (Single UNIX Specification v3), `setjmp` may or may not save
 159       * signal mask.  `_setjmp` will not, but is less portable.
 160       */
 161      (void)_setjmp(regs);
 162  #      endif
 163  #    endif
 164    }
 165  #  endif
 166    /*
 167     * TODO: `context` here is sometimes just zero.  At the moment, the
 168     * callees do not really need it.  Cast `fn` to a `volatile` type
 169     * to prevent call inlining.
 170     */
 171    (*(GC_with_callee_saves_func volatile *)&fn)(
 172        volatile_arg, CAST_AWAY_VOLATILE_PVOID(context));
 173    /*
 174     * Strongly discourage the compiler from treating the above as a tail-call,
 175     * since that would pop the register contents before we get a chance to
 176     * look at them.
 177     */
 178    GC_noop1(COVERT_DATAFLOW(ADDR(&dummy)));
 179  #  undef volatile_arg
 180  }
 181  
 182  #endif /* !PLATFORM_MACH_DEP && !SN_TARGET_PSP2 */
 183