Blame tests/backtrace-child.c

Packit Service 97d2fb
/* Test child for parent backtrace test.
Packit Service 97d2fb
   Copyright (C) 2013, 2016 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of the GNU General Public License as published by
Packit Service 97d2fb
   the Free Software Foundation; either version 3 of the License, or
Packit Service 97d2fb
   (at your option) any later version.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 97d2fb
   GNU General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received a copy of the GNU General Public License
Packit Service 97d2fb
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
/* Command line syntax: ./backtrace-child [--ptraceme|--gencore]
Packit Service 97d2fb
   --ptraceme will call ptrace (PTRACE_TRACEME) in the two threads.
Packit Service 97d2fb
   --gencore will call abort () at its end.
Packit Service 97d2fb
   Main thread will signal SIGUSR2.  Other thread will signal SIGUSR1.
Packit Service 97d2fb
   There used to be a difference between x86_64 and other architectures.
Packit Service 97d2fb
   To test getting a signal at the very first instruction of a function:
Packit Service 97d2fb
     PC will get changed to function 'jmp' by backtrace.c function
Packit Service 97d2fb
     prepare_thread.  Then SIGUSR2 will be signalled to backtrace-child
Packit Service 97d2fb
     which will invoke function sigusr2.
Packit Service 97d2fb
     This is all done so that signal interrupts execution of the very first
Packit Service 97d2fb
     instruction of a function.  Properly handled unwind should not slip into
Packit Service 97d2fb
     the previous unrelated function.
Packit Service 97d2fb
     The tested functionality is arch-independent but the code reproducing it
Packit Service 97d2fb
     has to be arch-specific.
Packit Service 97d2fb
   On non-x86_64:
Packit Service 97d2fb
     sigusr2 gets called by normal function call from function stdarg.
Packit Service 97d2fb
   On any arch then sigusr2 calls raise (SIGUSR1) for --ptraceme.
Packit Service 97d2fb
   abort () is called otherwise, expected for --gencore core dump.
Packit Service 97d2fb
Packit Service 97d2fb
   Expected x86_64 output:
Packit Service 97d2fb
   TID 10276:
Packit Service 97d2fb
   # 0 0x7f7ab61e9e6b      raise
Packit Service 97d2fb
   # 1 0x7f7ab661af47 - 1  main
Packit Service 97d2fb
   # 2 0x7f7ab5e3bb45 - 1  __libc_start_main
Packit Service 97d2fb
   # 3 0x7f7ab661aa09 - 1  _start
Packit Service 97d2fb
   TID 10278:
Packit Service 97d2fb
   # 0 0x7f7ab61e9e6b      raise
Packit Service 97d2fb
   # 1 0x7f7ab661ab3c - 1  sigusr2
Packit Service 97d2fb
   # 2 0x7f7ab5e4fa60      __restore_rt
Packit Service 97d2fb
   # 3 0x7f7ab661ab47      jmp
Packit Service 97d2fb
   # 4 0x7f7ab661ac92 - 1  stdarg
Packit Service 97d2fb
   # 5 0x7f7ab661acba - 1  backtracegen
Packit Service 97d2fb
   # 6 0x7f7ab661acd1 - 1  start
Packit Service 97d2fb
   # 7 0x7f7ab61e2c53 - 1  start_thread
Packit Service 97d2fb
   # 8 0x7f7ab5f0fdbd - 1  __clone
Packit Service 97d2fb
Packit Service 97d2fb
   Expected non-x86_64 (i386) output; __kernel_vsyscall are skipped if found:
Packit Service 97d2fb
   TID 10408:
Packit Service 97d2fb
   # 0 0xf779f430          __kernel_vsyscall
Packit Service 97d2fb
   # 1 0xf7771466 - 1      raise
Packit Service 97d2fb
   # 2 0xf77c1d07 - 1      main
Packit Service 97d2fb
   # 3 0xf75bd963 - 1      __libc_start_main
Packit Service 97d2fb
   # 4 0xf77c1761 - 1      _start
Packit Service 97d2fb
   TID 10412:
Packit Service 97d2fb
   # 0 0xf779f430          __kernel_vsyscall
Packit Service 97d2fb
   # 1 0xf7771466 - 1      raise
Packit Service 97d2fb
   # 2 0xf77c18f4 - 1      sigusr2
Packit Service 97d2fb
   # 3 0xf77c1a10 - 1      stdarg
Packit Service 97d2fb
   # 4 0xf77c1a2c - 1      backtracegen
Packit Service 97d2fb
   # 5 0xf77c1a48 - 1      start
Packit Service 97d2fb
   # 6 0xf77699da - 1      start_thread
Packit Service 97d2fb
   # 7 0xf769bbfe - 1      __clone
Packit Service 97d2fb
Packit Service 97d2fb
   But the raise jmp patching was unreliable. It depends on the CFI for the raise()
Packit Service 97d2fb
   function in glibc to be the same as for the jmp() function. This is not always
Packit Service 97d2fb
   the case. Some newer glibc versions rewrote raise() and now the CFA is calculated
Packit Service 97d2fb
   differently. So we disable raise jmp patching everywhere.
Packit Service 97d2fb
   */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef __x86_64__
Packit Service 97d2fb
/* #define RAISE_JMP_PATCHING 1 */
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include <config.h>
Packit Service 97d2fb
#include <assert.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <errno.h>
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
#include <pthread.h>
Packit Service 97d2fb
#include <stdio.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
Packit Service 97d2fb
#ifndef __linux__
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
main (int argc __attribute__ ((unused)), char **argv)
Packit Service 97d2fb
{
Packit Service 97d2fb
  fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
Packit Service 97d2fb
           argv[0]);
Packit Service 97d2fb
  return 77;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
#else /* __linux__ */
Packit Service 97d2fb
#include <sys/ptrace.h>
Packit Service 97d2fb
#include <signal.h>
Packit Service 97d2fb
Packit Service 97d2fb
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
Packit Service 97d2fb
#define NOINLINE_NOCLONE __attribute__ ((noinline, noclone))
Packit Service 97d2fb
#else
Packit Service 97d2fb
#define NOINLINE_NOCLONE __attribute__ ((noinline))
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#define NORETURN __attribute__ ((noreturn))
Packit Service 97d2fb
#define UNUSED __attribute__ ((unused))
Packit Service 97d2fb
#define USED __attribute__ ((used))
Packit Service 97d2fb
Packit Service 97d2fb
static int ptraceme, gencore;
Packit Service 97d2fb
Packit Service 97d2fb
/* Execution will arrive here from jmp by an artificial ptrace-spawn signal.  */
Packit Service 97d2fb
Packit Service 97d2fb
static NOINLINE_NOCLONE void
Packit Service 97d2fb
sigusr2 (int signo)
Packit Service 97d2fb
{
Packit Service 97d2fb
  assert (signo == SIGUSR2);
Packit Service 97d2fb
  if (! gencore)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      raise (SIGUSR1);
Packit Service 97d2fb
      /* Do not return as stack may be invalid due to ptrace-patched PC to the
Packit Service 97d2fb
	 jmp function.  */
Packit Service 97d2fb
      pthread_exit (NULL);
Packit Service 97d2fb
      /* Not reached.  */
Packit Service 97d2fb
      abort ();
Packit Service 97d2fb
    }
Packit Service 97d2fb
  /* Here we dump the core for --gencore.  */
Packit Service 97d2fb
  raise (SIGABRT);
Packit Service 97d2fb
  /* Avoid tail call optimization for the raise call.  */
Packit Service 97d2fb
  asm volatile ("");
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static NOINLINE_NOCLONE void
Packit Service 97d2fb
dummy1 (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  asm volatile ("");
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef RAISE_JMP_PATCHING
Packit Service 97d2fb
static NOINLINE_NOCLONE USED void
Packit Service 97d2fb
jmp (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* Not reached, signal will get ptrace-spawn to jump into sigusr2.  */
Packit Service 97d2fb
  abort ();
Packit Service 97d2fb
}
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
static NOINLINE_NOCLONE void
Packit Service 97d2fb
dummy2 (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  asm volatile ("");
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static NOINLINE_NOCLONE NORETURN void
Packit Service 97d2fb
stdarg (int f UNUSED, ...)
Packit Service 97d2fb
{
Packit Service 97d2fb
  sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
Packit Service 97d2fb
  assert (sigusr2_orig == SIG_DFL);
Packit Service 97d2fb
  errno = 0;
Packit Service 97d2fb
  if (ptraceme)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
Packit Service 97d2fb
      assert (l == 0);
Packit Service 97d2fb
    }
Packit Service 97d2fb
#ifdef RAISE_JMP_PATCHING
Packit Service 97d2fb
  if (! gencore)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Execution will get PC patched into function jmp.  */
Packit Service 97d2fb
      raise (SIGUSR1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
#endif
Packit Service 97d2fb
  sigusr2 (SIGUSR2);
Packit Service 97d2fb
  /* Not reached.  */
Packit Service 97d2fb
  abort ();
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static NOINLINE_NOCLONE void
Packit Service 97d2fb
dummy3 (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  asm volatile ("");
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static NOINLINE_NOCLONE void
Packit Service 97d2fb
backtracegen (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  stdarg (1);
Packit Service 97d2fb
  /* Here should be no instruction after the stdarg call as it is noreturn
Packit Service 97d2fb
     function.  It must be stdarg so that it is a call and not jump (jump as
Packit Service 97d2fb
     a tail-call).  */
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static NOINLINE_NOCLONE void
Packit Service 97d2fb
dummy4 (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  asm volatile ("");
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static void *
Packit Service 97d2fb
start (void *arg UNUSED)
Packit Service 97d2fb
{
Packit Service 97d2fb
  backtracegen ();
Packit Service 97d2fb
  /* Not reached.  */
Packit Service 97d2fb
  abort ();
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
main (int argc UNUSED, char **argv)
Packit Service 97d2fb
{
Packit Service 97d2fb
  setbuf (stdout, NULL);
Packit Service 97d2fb
  assert (*argv++);
Packit Service 97d2fb
  ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
Packit Service 97d2fb
  argv += ptraceme;
Packit Service 97d2fb
  gencore = (*argv && strcmp (*argv, "--gencore") == 0);
Packit Service 97d2fb
  argv += gencore;
Packit Service 97d2fb
  assert (!*argv);
Packit Service 97d2fb
  /* These dummy* functions are there so that each of their surrounding
Packit Service 97d2fb
     functions has some unrelated code around.  The purpose of some of the
Packit Service 97d2fb
     tests is verify unwinding the very first / after the very last instruction
Packit Service 97d2fb
     does not inappropriately slip into the unrelated code around.  */
Packit Service 97d2fb
  dummy1 ();
Packit Service 97d2fb
  dummy2 ();
Packit Service 97d2fb
  dummy3 ();
Packit Service 97d2fb
  dummy4 ();
Packit Service 97d2fb
  if (gencore)
Packit Service 97d2fb
    printf ("%ld\n", (long) getpid ());
Packit Service 97d2fb
  pthread_t thread;
Packit Service 97d2fb
  int i = pthread_create (&thread, NULL, start, NULL);
Packit Service 97d2fb
  // pthread_* functions do not set errno.
Packit Service 97d2fb
  assert (i == 0);
Packit Service 97d2fb
  if (ptraceme)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      errno = 0;
Packit Service 97d2fb
      long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
Packit Service 97d2fb
      assert (l == 0);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (gencore)
Packit Service 97d2fb
    pthread_join (thread, NULL);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    raise (SIGUSR2);
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
#endif /* ! __linux__ */
Packit Service 97d2fb