Blame nptl/tst-thread-exit-clobber.cc

Packit 6c4009
/* Test that pthread_exit does not clobber callee-saved registers.
Packit 6c4009
   Copyright (C) 2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <support/check.h>
Packit 6c4009
#include <support/xthread.h>
Packit 6c4009
Packit 6c4009
/* This test attempts to check that callee-saved registers are
Packit 6c4009
   restored to their original values when destructors are run after
Packit 6c4009
   pthread_exit is called.  GCC PR 83641 causes this test to fail.
Packit 6c4009
Packit 6c4009
   The constants have been chosen randomly and are magic values which
Packit 6c4009
   are used to detect whether registers have been clobbered.  The idea
Packit 6c4009
   is that these values are hidden behind a compiler barrier and only
Packit 6c4009
   present in .rodata initially, so that it is less likely that they
Packit 6c4009
   are in a register by accident.
Packit 6c4009
Packit 6c4009
   The checker class can be stored in registers, and the magic values
Packit 6c4009
   are directly loaded into these registers.  The checker destructor
Packit 6c4009
   is eventually invoked by pthread_exit and calls one of the
Packit 6c4009
   check_magic functions to verify that the class contents (that is,
Packit 6c4009
   register value) is correct.
Packit 6c4009
Packit 6c4009
   These tests are performed both for unsigned int and double values,
Packit 6c4009
   to cover different calling conventions.  */
Packit 6c4009
Packit 6c4009
template <class T>
Packit 6c4009
struct values
Packit 6c4009
{
Packit 6c4009
  T v0;
Packit 6c4009
  T v1;
Packit 6c4009
  T v2;
Packit 6c4009
  T v3;
Packit 6c4009
  T v4;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static const values<unsigned int> magic_values =
Packit 6c4009
  {
Packit 6c4009
    0x57f7fc72,
Packit 6c4009
    0xe582daba,
Packit 6c4009
    0x5f6ac994,
Packit 6c4009
    0x35efddb7,
Packit 6c4009
    0x1fbf5a74,
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
static const values<double> magic_values_double =
Packit 6c4009
  {
Packit 6c4009
    0.6764041905675465,
Packit 6c4009
    0.9533336788140494,
Packit 6c4009
    0.6091161359041452,
Packit 6c4009
    0.7668653957125336,
Packit 6c4009
    0.010374520235509666,
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
/* Special index value which tells check_magic that no check should be
Packit 6c4009
   performed.  */
Packit 6c4009
enum { no_check = -1 };
Packit 6c4009
Packit 6c4009
/* Check that VALUE is the magic value for INDEX, behind a compiler
Packit 6c4009
   barrier.  */
Packit 6c4009
__attribute__ ((noinline, noclone, weak))
Packit 6c4009
void
Packit 6c4009
check_magic (int index, unsigned int value)
Packit 6c4009
{
Packit 6c4009
  switch (index)
Packit 6c4009
    {
Packit 6c4009
    case 0:
Packit 6c4009
      TEST_COMPARE (value, magic_values.v0);
Packit 6c4009
      break;
Packit 6c4009
    case 1:
Packit 6c4009
      TEST_COMPARE (value, magic_values.v1);
Packit 6c4009
      break;
Packit 6c4009
    case 2:
Packit 6c4009
      TEST_COMPARE (value, magic_values.v2);
Packit 6c4009
      break;
Packit 6c4009
    case 3:
Packit 6c4009
      TEST_COMPARE (value, magic_values.v3);
Packit 6c4009
      break;
Packit 6c4009
    case 4:
Packit 6c4009
      TEST_COMPARE (value, magic_values.v4);
Packit 6c4009
      break;
Packit 6c4009
    case no_check:
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      FAIL_EXIT1 ("invalid magic value index %d", index);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Check that VALUE is the magic value for INDEX, behind a compiler
Packit 6c4009
   barrier.  Double variant.  */
Packit 6c4009
__attribute__ ((noinline, noclone, weak))
Packit 6c4009
void
Packit 6c4009
check_magic (int index, double value)
Packit 6c4009
{
Packit 6c4009
  switch (index)
Packit 6c4009
    {
Packit 6c4009
    case 0:
Packit 6c4009
      TEST_VERIFY (value == magic_values_double.v0);
Packit 6c4009
      break;
Packit 6c4009
    case 1:
Packit 6c4009
      TEST_VERIFY (value == magic_values_double.v1);
Packit 6c4009
      break;
Packit 6c4009
    case 2:
Packit 6c4009
      TEST_VERIFY (value == magic_values_double.v2);
Packit 6c4009
      break;
Packit 6c4009
    case 3:
Packit 6c4009
      TEST_VERIFY (value == magic_values_double.v3);
Packit 6c4009
      break;
Packit 6c4009
    case 4:
Packit 6c4009
      TEST_VERIFY (value == magic_values_double.v4);
Packit 6c4009
      break;
Packit 6c4009
    case no_check:
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      FAIL_EXIT1 ("invalid magic value index %d", index);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Store a magic value and check, via the destructor, that it has the
Packit 6c4009
   expected value.  */
Packit 6c4009
template <class T, int I>
Packit 6c4009
struct checker
Packit 6c4009
{
Packit 6c4009
  T value;
Packit 6c4009
Packit 6c4009
  checker (T v)
Packit 6c4009
    : value (v)
Packit 6c4009
  {
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  ~checker ()
Packit 6c4009
  {
Packit 6c4009
    check_magic (I, value);
Packit 6c4009
  }
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* The functions call_pthread_exit_0, call_pthread_exit_1,
Packit 6c4009
   call_pthread_exit are used to call pthread_exit indirectly, with
Packit 6c4009
   the intent of clobbering the register values.  */
Packit 6c4009
Packit 6c4009
__attribute__ ((noinline, noclone, weak))
Packit 6c4009
void
Packit 6c4009
call_pthread_exit_0 (const values<unsigned int> *pvalues)
Packit 6c4009
{
Packit 6c4009
  checker<unsigned int, no_check> c0 (pvalues->v0);
Packit 6c4009
  checker<unsigned int, no_check> c1 (pvalues->v1);
Packit 6c4009
  checker<unsigned int, no_check> c2 (pvalues->v2);
Packit 6c4009
  checker<unsigned int, no_check> c3 (pvalues->v3);
Packit 6c4009
  checker<unsigned int, no_check> c4 (pvalues->v4);
Packit 6c4009
Packit 6c4009
  pthread_exit (NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
__attribute__ ((noinline, noclone, weak))
Packit 6c4009
void
Packit 6c4009
call_pthread_exit_1 (const values<double> *pvalues)
Packit 6c4009
{
Packit 6c4009
  checker<double, no_check> c0 (pvalues->v0);
Packit 6c4009
  checker<double, no_check> c1 (pvalues->v1);
Packit 6c4009
  checker<double, no_check> c2 (pvalues->v2);
Packit 6c4009
  checker<double, no_check> c3 (pvalues->v3);
Packit 6c4009
  checker<double, no_check> c4 (pvalues->v4);
Packit 6c4009
Packit 6c4009
  values<unsigned int> other_values = { 0, };
Packit 6c4009
  call_pthread_exit_0 (&other_values);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
__attribute__ ((noinline, noclone, weak))
Packit 6c4009
void
Packit 6c4009
call_pthread_exit ()
Packit 6c4009
{
Packit 6c4009
  values<double> other_values = { 0, };
Packit 6c4009
  call_pthread_exit_1 (&other_values);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Create on-stack objects and check that their values are restored by
Packit 6c4009
   pthread_exit.  If Nested is true, call pthread_exit indirectly via
Packit 6c4009
   call_pthread_exit.  */
Packit 6c4009
template <class T, bool Nested>
Packit 6c4009
__attribute__ ((noinline, noclone, weak))
Packit 6c4009
void *
Packit 6c4009
threadfunc (void *closure)
Packit 6c4009
{
Packit 6c4009
  const values<T> *pvalues = static_cast<const values<T> *> (closure);
Packit 6c4009
Packit 6c4009
  checker<T, 0> c0 (pvalues->v0);
Packit 6c4009
  checker<T, 1> c1 (pvalues->v1);
Packit 6c4009
  checker<T, 2> c2 (pvalues->v2);
Packit 6c4009
  checker<T, 3> c3 (pvalues->v3);
Packit 6c4009
  checker<T, 4> c4 (pvalues->v4);
Packit 6c4009
Packit 6c4009
  if (Nested)
Packit 6c4009
    call_pthread_exit ();
Packit 6c4009
  else
Packit 6c4009
    pthread_exit (NULL);
Packit 6c4009
Packit 6c4009
  /* This should not be reached.  */
Packit 6c4009
  return const_cast<char *> ("");
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test ()
Packit 6c4009
{
Packit 6c4009
  puts ("info: unsigned int, direct pthread_exit call");
Packit 6c4009
  pthread_t thr
Packit 6c4009
    = xpthread_create (NULL, &threadfunc<unsigned int, false>,
Packit 6c4009
                       const_cast<values<unsigned int> *> (&magic_values));
Packit 6c4009
  TEST_VERIFY (xpthread_join (thr) == NULL);
Packit 6c4009
Packit 6c4009
  puts ("info: double, direct pthread_exit call");
Packit 6c4009
  thr = xpthread_create (NULL, &threadfunc<double, false>,
Packit 6c4009
                         const_cast<values<double> *> (&magic_values_double));
Packit 6c4009
  TEST_VERIFY (xpthread_join (thr) == NULL);
Packit 6c4009
Packit 6c4009
  puts ("info: unsigned int, indirect pthread_exit call");
Packit 6c4009
  thr = xpthread_create (NULL, &threadfunc<unsigned int, true>,
Packit 6c4009
                       const_cast<values<unsigned int> *> (&magic_values));
Packit 6c4009
  TEST_VERIFY (xpthread_join (thr) == NULL);
Packit 6c4009
Packit 6c4009
  puts ("info: double, indirect pthread_exit call");
Packit 6c4009
  thr = xpthread_create (NULL, &threadfunc<double, true>,
Packit 6c4009
                         const_cast<values<double> *> (&magic_values_double));
Packit 6c4009
  TEST_VERIFY (xpthread_join (thr) == NULL);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#include <support/test-driver.c>