Home | History | Annotate | Download | only in tests
      1 /* Test child for parent backtrace test.
      2    Copyright (C) 2013 Red Hat, Inc.
      3    This file is part of elfutils.
      4 
      5    This file is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 3 of the License, or
      8    (at your option) any later version.
      9 
     10    elfutils is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     17 
     18 /* Command line syntax: ./backtrace-child [--ptraceme|--gencore]
     19    --ptraceme will call ptrace (PTRACE_TRACEME) in the two threads.
     20    --gencore will call abort () at its end.
     21    Main thread will signal SIGUSR2.  Other thread will signal SIGUSR1.
     22    On x86_64 only:
     23      PC will get changed to function 'jmp' by backtrace.c function
     24      prepare_thread.  Then SIGUSR2 will be signalled to backtrace-child
     25      which will invoke function sigusr2.
     26      This is all done so that signal interrupts execution of the very first
     27      instruction of a function.  Properly handled unwind should not slip into
     28      the previous unrelated function.
     29      The tested functionality is arch-independent but the code reproducing it
     30      has to be arch-specific.
     31    On non-x86_64:
     32      sigusr2 gets called by normal function call from function stdarg.
     33    On any arch then sigusr2 calls raise (SIGUSR1) for --ptraceme.
     34    abort () is called otherwise, expected for --gencore core dump.
     35 
     36    Expected x86_64 output:
     37    TID 10276:
     38    # 0 0x7f7ab61e9e6b      raise
     39    # 1 0x7f7ab661af47 - 1  main
     40    # 2 0x7f7ab5e3bb45 - 1  __libc_start_main
     41    # 3 0x7f7ab661aa09 - 1  _start
     42    TID 10278:
     43    # 0 0x7f7ab61e9e6b      raise
     44    # 1 0x7f7ab661ab3c - 1  sigusr2
     45    # 2 0x7f7ab5e4fa60      __restore_rt
     46    # 3 0x7f7ab661ab47      jmp
     47    # 4 0x7f7ab661ac92 - 1  stdarg
     48    # 5 0x7f7ab661acba - 1  backtracegen
     49    # 6 0x7f7ab661acd1 - 1  start
     50    # 7 0x7f7ab61e2c53 - 1  start_thread
     51    # 8 0x7f7ab5f0fdbd - 1  __clone
     52 
     53    Expected non-x86_64 (i386) output; __kernel_vsyscall are skipped if found:
     54    TID 10408:
     55    # 0 0xf779f430          __kernel_vsyscall
     56    # 1 0xf7771466 - 1      raise
     57    # 2 0xf77c1d07 - 1      main
     58    # 3 0xf75bd963 - 1      __libc_start_main
     59    # 4 0xf77c1761 - 1      _start
     60    TID 10412:
     61    # 0 0xf779f430          __kernel_vsyscall
     62    # 1 0xf7771466 - 1      raise
     63    # 2 0xf77c18f4 - 1      sigusr2
     64    # 3 0xf77c1a10 - 1      stdarg
     65    # 4 0xf77c1a2c - 1      backtracegen
     66    # 5 0xf77c1a48 - 1      start
     67    # 6 0xf77699da - 1      start_thread
     68    # 7 0xf769bbfe - 1      __clone
     69    */
     70 
     71 #include <config.h>
     72 #include <assert.h>
     73 #include <stdlib.h>
     74 #include <signal.h>
     75 #include <errno.h>
     76 #include <sys/ptrace.h>
     77 #include <string.h>
     78 #include <pthread.h>
     79 #include <stdio.h>
     80 #include <unistd.h>
     81 
     82 #ifndef __linux__
     83 
     84 int
     85 main (int argc __attribute__ ((unused)), char **argv)
     86 {
     87   fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
     88            argv[0]);
     89   return 77;
     90 }
     91 
     92 #else /* __linux__ */
     93 
     94 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
     95 #define NOINLINE_NOCLONE __attribute__ ((noinline, noclone))
     96 #else
     97 #define NOINLINE_NOCLONE __attribute__ ((noinline))
     98 #endif
     99 
    100 #define NORETURN __attribute__ ((noreturn))
    101 #define UNUSED __attribute__ ((unused))
    102 #define USED __attribute__ ((used))
    103 
    104 static int ptraceme, gencore;
    105 
    106 /* Execution will arrive here from jmp by an artificial ptrace-spawn signal.  */
    107 
    108 static NOINLINE_NOCLONE void
    109 sigusr2 (int signo)
    110 {
    111   assert (signo == SIGUSR2);
    112   if (! gencore)
    113     {
    114       raise (SIGUSR1);
    115       /* Do not return as stack may be invalid due to ptrace-patched PC to the
    116 	 jmp function.  */
    117       pthread_exit (NULL);
    118       /* Not reached.  */
    119       abort ();
    120     }
    121   /* Here we dump the core for --gencore.  */
    122   raise (SIGABRT);
    123   /* Avoid tail call optimization for the raise call.  */
    124   asm volatile ("");
    125 }
    126 
    127 static NOINLINE_NOCLONE void
    128 dummy1 (void)
    129 {
    130   asm volatile ("");
    131 }
    132 
    133 #ifdef __x86_64__
    134 static NOINLINE_NOCLONE USED void
    135 jmp (void)
    136 {
    137   /* Not reached, signal will get ptrace-spawn to jump into sigusr2.  */
    138   abort ();
    139 }
    140 #endif
    141 
    142 static NOINLINE_NOCLONE void
    143 dummy2 (void)
    144 {
    145   asm volatile ("");
    146 }
    147 
    148 static NOINLINE_NOCLONE NORETURN void
    149 stdarg (int f UNUSED, ...)
    150 {
    151   sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
    152   assert (sigusr2_orig == SIG_DFL);
    153   errno = 0;
    154   if (ptraceme)
    155     {
    156       long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
    157       assert (errno == 0);
    158       assert (l == 0);
    159     }
    160 #ifdef __x86_64__
    161   if (! gencore)
    162     {
    163       /* Execution will get PC patched into function jmp.  */
    164       raise (SIGUSR1);
    165     }
    166 #endif
    167   sigusr2 (SIGUSR2);
    168   /* Not reached.  */
    169   abort ();
    170 }
    171 
    172 static NOINLINE_NOCLONE void
    173 dummy3 (void)
    174 {
    175   asm volatile ("");
    176 }
    177 
    178 static NOINLINE_NOCLONE void
    179 backtracegen (void)
    180 {
    181   stdarg (1);
    182   /* Here should be no instruction after the stdarg call as it is noreturn
    183      function.  It must be stdarg so that it is a call and not jump (jump as
    184      a tail-call).  */
    185 }
    186 
    187 static NOINLINE_NOCLONE void
    188 dummy4 (void)
    189 {
    190   asm volatile ("");
    191 }
    192 
    193 static void *
    194 start (void *arg UNUSED)
    195 {
    196   backtracegen ();
    197   /* Not reached.  */
    198   abort ();
    199 }
    200 
    201 int
    202 main (int argc UNUSED, char **argv)
    203 {
    204   setbuf (stdout, NULL);
    205   assert (*argv++);
    206   ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
    207   argv += ptraceme;
    208   gencore = (*argv && strcmp (*argv, "--gencore") == 0);
    209   argv += gencore;
    210   assert (!*argv);
    211   /* These dummy* functions are there so that each of their surrounding
    212      functions has some unrelated code around.  The purpose of some of the
    213      tests is verify unwinding the very first / after the very last instruction
    214      does not inappropriately slip into the unrelated code around.  */
    215   dummy1 ();
    216   dummy2 ();
    217   dummy3 ();
    218   dummy4 ();
    219   if (gencore)
    220     printf ("%ld\n", (long) getpid ());
    221   pthread_t thread;
    222   int i = pthread_create (&thread, NULL, start, NULL);
    223   // pthread_* functions do not set errno.
    224   assert (i == 0);
    225   if (ptraceme)
    226     {
    227       errno = 0;
    228       long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
    229       assert (errno == 0);
    230       assert (l == 0);
    231     }
    232   if (gencore)
    233     pthread_join (thread, NULL);
    234   else
    235     raise (SIGUSR2);
    236   return 0;
    237 }
    238 
    239 #endif /* ! __linux__ */
    240 
    241