Blame tests/backtrace-child.c

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