Home | History | Annotate | Line # | Download | only in nat
linux-ptrace.c revision 1.1
      1 /* Linux-specific ptrace manipulation routines.
      2    Copyright (C) 2012-2015 Free Software Foundation, Inc.
      3 
      4    This file is part of GDB.
      5 
      6    This program is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by
      8    the Free Software Foundation; either version 3 of the License, or
      9    (at your option) any later version.
     10 
     11    This program is distributed in the hope that it will be useful,
     12    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14    GNU General Public License for more details.
     15 
     16    You should have received a copy of the GNU General Public License
     17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     18 
     19 #include "common-defs.h"
     20 #include "linux-ptrace.h"
     21 #include "linux-procfs.h"
     22 #include "linux-waitpid.h"
     23 #include "buffer.h"
     24 #include "gdb_wait.h"
     25 
     26 #include <stdint.h>
     27 
     28 /* Stores the currently supported ptrace options.  A value of
     29    -1 means we did not check for features yet.  A value of 0 means
     30    there are no supported features.  */
     31 static int current_ptrace_options = -1;
     32 
     33 /* Additional flags to test.  */
     34 
     35 static int additional_flags;
     36 
     37 /* Find all possible reasons we could fail to attach PID and append
     38    these as strings to the already initialized BUFFER.  '\0'
     39    termination of BUFFER must be done by the caller.  */
     40 
     41 void
     42 linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer)
     43 {
     44   pid_t tracerpid;
     45 
     46   tracerpid = linux_proc_get_tracerpid_nowarn (pid);
     47   if (tracerpid > 0)
     48     buffer_xml_printf (buffer, _("process %d is already traced "
     49 				 "by process %d"),
     50 		       (int) pid, (int) tracerpid);
     51 
     52   if (linux_proc_pid_is_zombie_nowarn (pid))
     53     buffer_xml_printf (buffer, _("process %d is a zombie "
     54 				 "- the process has already terminated"),
     55 		       (int) pid);
     56 }
     57 
     58 /* See linux-ptrace.h.  */
     59 
     60 char *
     61 linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
     62 {
     63   static char *reason_string;
     64   struct buffer buffer;
     65   char *warnings;
     66   long lwpid = ptid_get_lwp (ptid);
     67 
     68   xfree (reason_string);
     69 
     70   buffer_init (&buffer);
     71   linux_ptrace_attach_fail_reason (lwpid, &buffer);
     72   buffer_grow_str0 (&buffer, "");
     73   warnings = buffer_finish (&buffer);
     74   if (warnings[0] != '\0')
     75     reason_string = xstrprintf ("%s (%d), %s",
     76 				strerror (err), err, warnings);
     77   else
     78     reason_string = xstrprintf ("%s (%d)",
     79 				strerror (err), err);
     80   xfree (warnings);
     81   return reason_string;
     82 }
     83 
     84 #if defined __i386__ || defined __x86_64__
     85 
     86 /* Address of the 'ret' instruction in asm code block below.  */
     87 extern void (linux_ptrace_test_ret_to_nx_instr) (void);
     88 
     89 #include <sys/reg.h>
     90 #include <sys/mman.h>
     91 #include <signal.h>
     92 
     93 #endif /* defined __i386__ || defined __x86_64__ */
     94 
     95 /* Test broken off-trunk Linux kernel patchset for NX support on i386.  It was
     96    removed in Fedora kernel 88fa1f0332d188795ed73d7ac2b1564e11a0b4cd.
     97 
     98    Test also x86_64 arch for PaX support.  */
     99 
    100 static void
    101 linux_ptrace_test_ret_to_nx (void)
    102 {
    103 #if defined __i386__ || defined __x86_64__
    104   pid_t child, got_pid;
    105   gdb_byte *return_address, *pc;
    106   long l;
    107   int status, kill_status;
    108 
    109   return_address = mmap (NULL, 2, PROT_READ | PROT_WRITE,
    110 			 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    111   if (return_address == MAP_FAILED)
    112     {
    113       warning (_("linux_ptrace_test_ret_to_nx: Cannot mmap: %s"),
    114 	       strerror (errno));
    115       return;
    116     }
    117 
    118   /* Put there 'int3'.  */
    119   *return_address = 0xcc;
    120 
    121   child = fork ();
    122   switch (child)
    123     {
    124     case -1:
    125       warning (_("linux_ptrace_test_ret_to_nx: Cannot fork: %s"),
    126 	       strerror (errno));
    127       return;
    128 
    129     case 0:
    130       l = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) NULL,
    131 		  (PTRACE_TYPE_ARG4) NULL);
    132       if (l != 0)
    133 	warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: %s"),
    134 		 strerror (errno));
    135       else
    136 	{
    137 #if defined __i386__
    138 	  asm volatile ("pushl %0;"
    139 			".globl linux_ptrace_test_ret_to_nx_instr;"
    140 			"linux_ptrace_test_ret_to_nx_instr:"
    141 			"ret"
    142 			: : "r" (return_address) : "%esp", "memory");
    143 #elif defined __x86_64__
    144 	  asm volatile ("pushq %0;"
    145 			".globl linux_ptrace_test_ret_to_nx_instr;"
    146 			"linux_ptrace_test_ret_to_nx_instr:"
    147 			"ret"
    148 			: : "r" ((uint64_t) (uintptr_t) return_address)
    149 			: "%rsp", "memory");
    150 #else
    151 # error "!__i386__ && !__x86_64__"
    152 #endif
    153 	  gdb_assert_not_reached ("asm block did not terminate");
    154 	}
    155 
    156       _exit (1);
    157     }
    158 
    159   errno = 0;
    160   got_pid = waitpid (child, &status, 0);
    161   if (got_pid != child)
    162     {
    163       warning (_("linux_ptrace_test_ret_to_nx: waitpid returned %ld: %s"),
    164 	       (long) got_pid, strerror (errno));
    165       return;
    166     }
    167 
    168   if (WIFSIGNALED (status))
    169     {
    170       if (WTERMSIG (status) != SIGKILL)
    171 	warning (_("linux_ptrace_test_ret_to_nx: WTERMSIG %d is not SIGKILL!"),
    172 		 (int) WTERMSIG (status));
    173       else
    174 	warning (_("Cannot call inferior functions, Linux kernel PaX "
    175 		   "protection forbids return to non-executable pages!"));
    176       return;
    177     }
    178 
    179   if (!WIFSTOPPED (status))
    180     {
    181       warning (_("linux_ptrace_test_ret_to_nx: status %d is not WIFSTOPPED!"),
    182 	       status);
    183       return;
    184     }
    185 
    186   /* We may get SIGSEGV due to missing PROT_EXEC of the return_address.  */
    187   if (WSTOPSIG (status) != SIGTRAP && WSTOPSIG (status) != SIGSEGV)
    188     {
    189       warning (_("linux_ptrace_test_ret_to_nx: "
    190 		 "WSTOPSIG %d is neither SIGTRAP nor SIGSEGV!"),
    191 	       (int) WSTOPSIG (status));
    192       return;
    193     }
    194 
    195   errno = 0;
    196 #if defined __i386__
    197   l = ptrace (PTRACE_PEEKUSER, child, (PTRACE_TYPE_ARG3) (uintptr_t) (EIP * 4),
    198 	      (PTRACE_TYPE_ARG4) NULL);
    199 #elif defined __x86_64__
    200   l = ptrace (PTRACE_PEEKUSER, child, (PTRACE_TYPE_ARG3) (uintptr_t) (RIP * 8),
    201 	      (PTRACE_TYPE_ARG4) NULL);
    202 #else
    203 # error "!__i386__ && !__x86_64__"
    204 #endif
    205   if (errno != 0)
    206     {
    207       warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_PEEKUSER: %s"),
    208 	       strerror (errno));
    209       return;
    210     }
    211   pc = (void *) (uintptr_t) l;
    212 
    213   kill (child, SIGKILL);
    214   ptrace (PTRACE_KILL, child, (PTRACE_TYPE_ARG3) NULL,
    215 	  (PTRACE_TYPE_ARG4) NULL);
    216 
    217   errno = 0;
    218   got_pid = waitpid (child, &kill_status, 0);
    219   if (got_pid != child)
    220     {
    221       warning (_("linux_ptrace_test_ret_to_nx: "
    222 		 "PTRACE_KILL waitpid returned %ld: %s"),
    223 	       (long) got_pid, strerror (errno));
    224       return;
    225     }
    226   if (!WIFSIGNALED (kill_status))
    227     {
    228       warning (_("linux_ptrace_test_ret_to_nx: "
    229 		 "PTRACE_KILL status %d is not WIFSIGNALED!"),
    230 	       status);
    231       return;
    232     }
    233 
    234   /* + 1 is there as x86* stops after the 'int3' instruction.  */
    235   if (WSTOPSIG (status) == SIGTRAP && pc == return_address + 1)
    236     {
    237       /* PASS */
    238       return;
    239     }
    240 
    241   /* We may get SIGSEGV due to missing PROT_EXEC of the RETURN_ADDRESS page.  */
    242   if (WSTOPSIG (status) == SIGSEGV && pc == return_address)
    243     {
    244       /* PASS */
    245       return;
    246     }
    247 
    248   if ((void (*) (void)) pc != &linux_ptrace_test_ret_to_nx_instr)
    249     warning (_("linux_ptrace_test_ret_to_nx: PC %p is neither near return "
    250 	       "address %p nor is the return instruction %p!"),
    251 	     pc, return_address, &linux_ptrace_test_ret_to_nx_instr);
    252   else
    253     warning (_("Cannot call inferior functions on this system - "
    254 	       "Linux kernel with broken i386 NX (non-executable pages) "
    255 	       "support detected!"));
    256 #endif /* defined __i386__ || defined __x86_64__ */
    257 }
    258 
    259 /* Helper function to fork a process and make the child process call
    260    the function FUNCTION, passing CHILD_STACK as parameter.
    261 
    262    For MMU-less targets, clone is used instead of fork, and
    263    CHILD_STACK is used as stack space for the cloned child.  If NULL,
    264    stack space is allocated via malloc (and subsequently passed to
    265    FUNCTION).  For MMU targets, CHILD_STACK is ignored.  */
    266 
    267 static int
    268 linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *))
    269 {
    270   int child_pid;
    271 
    272   /* Sanity check the function pointer.  */
    273   gdb_assert (function != NULL);
    274 
    275 #if defined(__UCLIBC__) && defined(HAS_NOMMU)
    276 #define STACK_SIZE 4096
    277 
    278     if (child_stack == NULL)
    279       child_stack = xmalloc (STACK_SIZE * 4);
    280 
    281     /* Use CLONE_VM instead of fork, to support uClinux (no MMU).  */
    282 #ifdef __ia64__
    283       child_pid = __clone2 (function, child_stack, STACK_SIZE,
    284 			    CLONE_VM | SIGCHLD, child_stack + STACK_SIZE * 2);
    285 #else /* !__ia64__ */
    286       child_pid = clone (function, child_stack + STACK_SIZE,
    287 			 CLONE_VM | SIGCHLD, child_stack + STACK_SIZE * 2);
    288 #endif /* !__ia64__ */
    289 #else /* !defined(__UCLIBC) && defined(HAS_NOMMU) */
    290   child_pid = fork ();
    291 
    292   if (child_pid == 0)
    293     function (NULL);
    294 #endif /* defined(__UCLIBC) && defined(HAS_NOMMU) */
    295 
    296   if (child_pid == -1)
    297     perror_with_name (("fork"));
    298 
    299   return child_pid;
    300 }
    301 
    302 /* A helper function for linux_check_ptrace_features, called after
    303    the child forks a grandchild.  */
    304 
    305 static void
    306 linux_grandchild_function (gdb_byte *child_stack)
    307 {
    308   /* Free any allocated stack.  */
    309   xfree (child_stack);
    310 
    311   /* This code is only reacheable by the grandchild (child's child)
    312      process.  */
    313   _exit (0);
    314 }
    315 
    316 /* A helper function for linux_check_ptrace_features, called after
    317    the parent process forks a child.  The child allows itself to
    318    be traced by its parent.  */
    319 
    320 static void
    321 linux_child_function (gdb_byte *child_stack)
    322 {
    323   ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
    324   kill (getpid (), SIGSTOP);
    325 
    326   /* Fork a grandchild.  */
    327   linux_fork_to_function (child_stack, linux_grandchild_function);
    328 
    329   /* This code is only reacheable by the child (grandchild's parent)
    330      process.  */
    331   _exit (0);
    332 }
    333 
    334 static void linux_test_for_tracesysgood (int child_pid);
    335 static void linux_test_for_tracefork (int child_pid);
    336 static void linux_test_for_exitkill (int child_pid);
    337 
    338 /* Determine ptrace features available on this target.  */
    339 
    340 static void
    341 linux_check_ptrace_features (void)
    342 {
    343   int child_pid, ret, status;
    344 
    345   /* Initialize the options.  */
    346   current_ptrace_options = 0;
    347 
    348   /* Fork a child so we can do some testing.  The child will call
    349      linux_child_function and will get traced.  The child will
    350      eventually fork a grandchild so we can test fork event
    351      reporting.  */
    352   child_pid = linux_fork_to_function (NULL, linux_child_function);
    353 
    354   ret = my_waitpid (child_pid, &status, 0);
    355   if (ret == -1)
    356     perror_with_name (("waitpid"));
    357   else if (ret != child_pid)
    358     error (_("linux_check_ptrace_features: waitpid: unexpected result %d."),
    359 	   ret);
    360   if (! WIFSTOPPED (status))
    361     error (_("linux_check_ptrace_features: waitpid: unexpected status %d."),
    362 	   status);
    363 
    364   linux_test_for_tracesysgood (child_pid);
    365 
    366   linux_test_for_tracefork (child_pid);
    367 
    368   linux_test_for_exitkill (child_pid);
    369 
    370   /* Clean things up and kill any pending children.  */
    371   do
    372     {
    373       ret = ptrace (PTRACE_KILL, child_pid, (PTRACE_TYPE_ARG3) 0,
    374 		    (PTRACE_TYPE_ARG4) 0);
    375       if (ret != 0)
    376 	warning (_("linux_check_ptrace_features: failed to kill child"));
    377       my_waitpid (child_pid, &status, 0);
    378     }
    379   while (WIFSTOPPED (status));
    380 }
    381 
    382 /* Determine if PTRACE_O_TRACESYSGOOD can be used to catch
    383    syscalls.  */
    384 
    385 static void
    386 linux_test_for_tracesysgood (int child_pid)
    387 {
    388   int ret;
    389 
    390   if ((additional_flags & PTRACE_O_TRACESYSGOOD) == 0)
    391     return;
    392 
    393   ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
    394 		(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
    395 
    396   if (ret == 0)
    397     current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
    398 }
    399 
    400 /* Determine if PTRACE_O_TRACEFORK can be used to follow fork
    401    events.  */
    402 
    403 static void
    404 linux_test_for_tracefork (int child_pid)
    405 {
    406   int ret, status;
    407   long second_pid;
    408 
    409   /* First, set the PTRACE_O_TRACEFORK option.  If this fails, we
    410      know for sure that it is not supported.  */
    411   ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
    412 		(PTRACE_TYPE_ARG4) PTRACE_O_TRACEFORK);
    413 
    414   if (ret != 0)
    415     return;
    416 
    417   if ((additional_flags & PTRACE_O_TRACEVFORKDONE) != 0)
    418     {
    419       /* Check if the target supports PTRACE_O_TRACEVFORKDONE.  */
    420       ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
    421 		    (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
    422 					| PTRACE_O_TRACEVFORKDONE));
    423       if (ret == 0)
    424 	current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
    425     }
    426 
    427   /* Setting PTRACE_O_TRACEFORK did not cause an error, however we
    428      don't know for sure that the feature is available; old
    429      versions of PTRACE_SETOPTIONS ignored unknown options.
    430      Therefore, we attach to the child process, use PTRACE_SETOPTIONS
    431      to enable fork tracing, and let it fork.  If the process exits,
    432      we assume that we can't use PTRACE_O_TRACEFORK; if we get the
    433      fork notification, and we can extract the new child's PID, then
    434      we assume that we can.
    435 
    436      We do not explicitly check for vfork tracing here.  It is
    437      assumed that vfork tracing is available whenever fork tracing
    438      is available.  */
    439   ret = ptrace (PTRACE_CONT, child_pid, (PTRACE_TYPE_ARG3) 0,
    440 		(PTRACE_TYPE_ARG4) 0);
    441   if (ret != 0)
    442     warning (_("linux_test_for_tracefork: failed to resume child"));
    443 
    444   ret = my_waitpid (child_pid, &status, 0);
    445 
    446   /* Check if we received a fork event notification.  */
    447   if (ret == child_pid && WIFSTOPPED (status)
    448       && linux_ptrace_get_extended_event (status) == PTRACE_EVENT_FORK)
    449     {
    450       /* We did receive a fork event notification.  Make sure its PID
    451 	 is reported.  */
    452       second_pid = 0;
    453       ret = ptrace (PTRACE_GETEVENTMSG, child_pid, (PTRACE_TYPE_ARG3) 0,
    454 		    (PTRACE_TYPE_ARG4) &second_pid);
    455       if (ret == 0 && second_pid != 0)
    456 	{
    457 	  int second_status;
    458 
    459 	  /* We got the PID from the grandchild, which means fork
    460 	     tracing is supported.  */
    461 	  current_ptrace_options |= PTRACE_O_TRACECLONE;
    462 	  current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
    463                                                          | PTRACE_O_TRACEVFORK
    464                                                          | PTRACE_O_TRACEEXEC));
    465 
    466 	  /* Do some cleanup and kill the grandchild.  */
    467 	  my_waitpid (second_pid, &second_status, 0);
    468 	  ret = ptrace (PTRACE_KILL, second_pid, (PTRACE_TYPE_ARG3) 0,
    469 			(PTRACE_TYPE_ARG4) 0);
    470 	  if (ret != 0)
    471 	    warning (_("linux_test_for_tracefork: "
    472 		       "failed to kill second child"));
    473 	  my_waitpid (second_pid, &status, 0);
    474 	}
    475     }
    476   else
    477     warning (_("linux_test_for_tracefork: unexpected result from waitpid "
    478 	     "(%d, status 0x%x)"), ret, status);
    479 }
    480 
    481 /* Determine if PTRACE_O_EXITKILL can be used.  */
    482 
    483 static void
    484 linux_test_for_exitkill (int child_pid)
    485 {
    486   int ret;
    487 
    488   ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
    489 		(PTRACE_TYPE_ARG4) PTRACE_O_EXITKILL);
    490 
    491   if (ret == 0)
    492     current_ptrace_options |= PTRACE_O_EXITKILL;
    493 }
    494 
    495 /* Enable reporting of all currently supported ptrace events.
    496    ATTACHED should be nonzero if we have attached to the inferior.  */
    497 
    498 void
    499 linux_enable_event_reporting (pid_t pid, int attached)
    500 {
    501   int ptrace_options;
    502 
    503   /* Check if we have initialized the ptrace features for this
    504      target.  If not, do it now.  */
    505   if (current_ptrace_options == -1)
    506     linux_check_ptrace_features ();
    507 
    508   ptrace_options = current_ptrace_options;
    509   if (attached)
    510     {
    511       /* When attached to our inferior, we do not want the inferior
    512 	 to die with us if we terminate unexpectedly.  */
    513       ptrace_options &= ~PTRACE_O_EXITKILL;
    514     }
    515 
    516   /* Set the options.  */
    517   ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
    518 	  (PTRACE_TYPE_ARG4) (uintptr_t) ptrace_options);
    519 }
    520 
    521 /* Disable reporting of all currently supported ptrace events.  */
    522 
    523 void
    524 linux_disable_event_reporting (pid_t pid)
    525 {
    526   /* Set the options.  */
    527   ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0, 0);
    528 }
    529 
    530 /* Returns non-zero if PTRACE_OPTIONS is contained within
    531    CURRENT_PTRACE_OPTIONS, therefore supported.  Returns 0
    532    otherwise.  */
    533 
    534 static int
    535 ptrace_supports_feature (int ptrace_options)
    536 {
    537   if (current_ptrace_options == -1)
    538     linux_check_ptrace_features ();
    539 
    540   return ((current_ptrace_options & ptrace_options) == ptrace_options);
    541 }
    542 
    543 /* Returns non-zero if PTRACE_EVENT_FORK is supported by ptrace,
    544    0 otherwise.  Note that if PTRACE_EVENT_FORK is supported so is
    545    PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
    546    since they were all added to the kernel at the same time.  */
    547 
    548 int
    549 linux_supports_tracefork (void)
    550 {
    551   return ptrace_supports_feature (PTRACE_O_TRACEFORK);
    552 }
    553 
    554 /* Returns non-zero if PTRACE_EVENT_CLONE is supported by ptrace,
    555    0 otherwise.  Note that if PTRACE_EVENT_CLONE is supported so is
    556    PTRACE_EVENT_FORK, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
    557    since they were all added to the kernel at the same time.  */
    558 
    559 int
    560 linux_supports_traceclone (void)
    561 {
    562   return ptrace_supports_feature (PTRACE_O_TRACECLONE);
    563 }
    564 
    565 /* Returns non-zero if PTRACE_O_TRACEVFORKDONE is supported by
    566    ptrace, 0 otherwise.  */
    567 
    568 int
    569 linux_supports_tracevforkdone (void)
    570 {
    571   return ptrace_supports_feature (PTRACE_O_TRACEVFORKDONE);
    572 }
    573 
    574 /* Returns non-zero if PTRACE_O_TRACESYSGOOD is supported by ptrace,
    575    0 otherwise.  */
    576 
    577 int
    578 linux_supports_tracesysgood (void)
    579 {
    580   return ptrace_supports_feature (PTRACE_O_TRACESYSGOOD);
    581 }
    582 
    583 /* Display possible problems on this system.  Display them only once per GDB
    584    execution.  */
    585 
    586 void
    587 linux_ptrace_init_warnings (void)
    588 {
    589   static int warned = 0;
    590 
    591   if (warned)
    592     return;
    593   warned = 1;
    594 
    595   linux_ptrace_test_ret_to_nx ();
    596 }
    597 
    598 /* Set additional ptrace flags to use.  Some such flags may be checked
    599    by the implementation above.  This function must be called before
    600    any other function in this file; otherwise the flags may not take
    601    effect appropriately.  */
    602 
    603 void
    604 linux_ptrace_set_additional_flags (int flags)
    605 {
    606   additional_flags = flags;
    607 }
    608 
    609 /* Extract extended ptrace event from wait status.  */
    610 
    611 int
    612 linux_ptrace_get_extended_event (int wstat)
    613 {
    614   return (wstat >> 16);
    615 }
    616 
    617 /* Determine whether wait status denotes an extended event.  */
    618 
    619 int
    620 linux_is_extended_waitstatus (int wstat)
    621 {
    622   return (linux_ptrace_get_extended_event (wstat) != 0);
    623 }
    624