103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes/* Get Dwarf Frame state for target live PID process. 203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Copyright (C) 2013, 2014 Red Hat, Inc. 303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes This file is part of elfutils. 403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes This file is free software; you can redistribute it and/or modify 603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes it under the terms of either 703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes * the GNU Lesser General Public License as published by the Free 903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Software Foundation; either version 3 of the License, or (at 1003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes your option) any later version 1103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 1203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes or 1303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 1403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes * the GNU General Public License as published by the Free 1503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Software Foundation; either version 2 of the License, or (at 1603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes your option) any later version 1703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 1803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes or both in parallel, as here. 1903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 2003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes elfutils is distributed in the hope that it will be useful, but 2103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes WITHOUT ANY WARRANTY; without even the implied warranty of 2203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes General Public License for more details. 2403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 2503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes You should have received copies of the GNU General Public License and 2603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes the GNU Lesser General Public License along with this program. If 2703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes not, see <http://www.gnu.org/licenses/>. */ 2803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 2903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#include "libdwflP.h" 3003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#include <sys/ptrace.h> 3103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#include <sys/wait.h> 3203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#include <dirent.h> 3303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#include <sys/syscall.h> 3403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#include <unistd.h> 3503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 3603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#ifndef MAX 3703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes# define MAX(a, b) ((a) > (b) ? (a) : (b)) 3803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#endif 3903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 4003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#ifdef __linux__ 4103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 4203333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic bool 4303333823c75a1c1887e923828113a1b0fd12020cElliott Hugheslinux_proc_pid_is_stopped (pid_t pid) 4403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 4503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes char buffer[64]; 4603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes FILE *procfile; 4703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes bool retval, have_state; 4803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 4903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) pid); 5003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes procfile = fopen (buffer, "r"); 5103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (procfile == NULL) 5203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 5303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 5403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes have_state = false; 5503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes while (fgets (buffer, sizeof (buffer), procfile) != NULL) 5603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (strncmp (buffer, "State:", 6) == 0) 5703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 5803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes have_state = true; 5903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes break; 6003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 6103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes retval = (have_state && strstr (buffer, "T (stopped)") != NULL); 6203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes fclose (procfile); 6303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return retval; 6403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 6503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 6603333823c75a1c1887e923828113a1b0fd12020cElliott Hughesbool 6703333823c75a1c1887e923828113a1b0fd12020cElliott Hughesinternal_function 6803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes__libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp) 6903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 7003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0) 7103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 7203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 7303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 7403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 7503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes *tid_was_stoppedp = linux_proc_pid_is_stopped (tid); 7603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (*tid_was_stoppedp) 7703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 7803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes /* Make sure there is a SIGSTOP signal pending even when the process is 7903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes already State: T (stopped). Older kernels might fail to generate 8003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes a SIGSTOP notification in that case in response to our PTRACE_ATTACH 8103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes above. Which would make the waitpid below wait forever. So emulate 8203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes it. Since there can only be one SIGSTOP notification pending this is 8303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes safe. See also gdb/linux-nat.c linux_nat_post_attach_wait. */ 8403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes syscall (__NR_tkill, tid, SIGSTOP); 8503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes ptrace (PTRACE_CONT, tid, NULL, NULL); 8603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 8703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes for (;;) 8803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 8903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes int status; 9003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (waitpid (tid, &status, __WALL) != tid || !WIFSTOPPED (status)) 9103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 9203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes int saved_errno = errno; 9303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes ptrace (PTRACE_DETACH, tid, NULL, NULL); 9403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = saved_errno; 9503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 9603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 9703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 9803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (WSTOPSIG (status) == SIGSTOP) 9903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes break; 10003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (ptrace (PTRACE_CONT, tid, NULL, 10103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes (void *) (uintptr_t) WSTOPSIG (status)) != 0) 10203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 10303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes int saved_errno = errno; 10403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes ptrace (PTRACE_DETACH, tid, NULL, NULL); 10503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = saved_errno; 10603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 10703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 10803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 10903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 11003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return true; 11103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 11203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 11303333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic bool 11403333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg) 11503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 11603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes struct __libdwfl_pid_arg *pid_arg = arg; 11703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_t tid = pid_arg->tid_attached; 11803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes assert (tid > 0); 11903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Dwfl_Process *process = dwfl->process; 12003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (ebl_get_elfclass (process->ebl) == ELFCLASS64) 12103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 12203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#if SIZEOF_LONG == 8 12303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = 0; 12403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL); 12503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return errno == 0; 12603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#else /* SIZEOF_LONG != 8 */ 12703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes /* This should not happen. */ 12803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 12903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#endif /* SIZEOF_LONG != 8 */ 13003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 13103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#if SIZEOF_LONG == 8 13203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes /* We do not care about reads unaliged to 4 bytes boundary. 13303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes But 0x...ffc read of 8 bytes could overrun a page. */ 13403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes bool lowered = (addr & 4) != 0; 13503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (lowered) 13603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes addr -= 4; 13703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#endif /* SIZEOF_LONG == 8 */ 13803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = 0; 13903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL); 14003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (errno != 0) 14103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 14203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#if SIZEOF_LONG == 8 14303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes# if BYTE_ORDER == BIG_ENDIAN 14403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (! lowered) 14503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes *result >>= 32; 14603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes# else 14703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (lowered) 14803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes *result >>= 32; 14903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes# endif 15003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#endif /* SIZEOF_LONG == 8 */ 15103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes *result &= 0xffffffff; 15203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return true; 15303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 15403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 15503333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic pid_t 15603333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg, 15703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void **thread_argp) 15803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 15903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes struct __libdwfl_pid_arg *pid_arg = dwfl_arg; 16003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes struct dirent *dirent; 16103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes /* Start fresh on first traversal. */ 16203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (*thread_argp == NULL) 16303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes rewinddir (pid_arg->dir); 16403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes do 16503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 16603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = 0; 16703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes dirent = readdir (pid_arg->dir); 16803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (dirent == NULL) 16903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 17003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (errno != 0) 17103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 17203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 17303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return -1; 17403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 17503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return 0; 17603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 17703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 17803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes while (strcmp (dirent->d_name, ".") == 0 17903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes || strcmp (dirent->d_name, "..") == 0); 18003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes char *end; 18103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = 0; 18203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes long tidl = strtol (dirent->d_name, &end, 10); 18303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (errno != 0) 18403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 18503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 18603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return -1; 18703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 18803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_t tid = tidl; 18903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (tidl <= 0 || (end && *end) || tid != tidl) 19003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 19103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_PARSE_PROC); 19203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return -1; 19303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 19403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes *thread_argp = dwfl_arg; 19503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return tid; 19603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 19703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 19803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes/* Just checks that the thread id exists. */ 19903333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic bool 20003333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid, 20103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void *dwfl_arg, void **thread_argp) 20203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 20303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes *thread_argp = dwfl_arg; 20403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (kill (tid, 0) < 0) 20503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 20603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 20703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 20803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 20903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return true; 21003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 21103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 21203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes/* Implement the ebl_set_initial_registers_tid setfunc callback. */ 21303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 21403333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic bool 21503333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_thread_state_registers_cb (int firstreg, unsigned nregs, 21603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes const Dwarf_Word *regs, void *arg) 21703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 21803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Dwfl_Thread *thread = (Dwfl_Thread *) arg; 21903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (firstreg < 0) 22003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 22103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes assert (firstreg == -1); 22203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes assert (nregs == 1); 22303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes INTUSE(dwfl_thread_state_register_pc) (thread, *regs); 22403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return true; 22503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 22603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes assert (nregs > 0); 22703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return INTUSE(dwfl_thread_state_registers) (thread, firstreg, nregs, regs); 22803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 22903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 23003333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic bool 23103333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg) 23203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 23303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes struct __libdwfl_pid_arg *pid_arg = thread_arg; 23403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes assert (pid_arg->tid_attached == 0); 23503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_t tid = INTUSE(dwfl_thread_tid) (thread); 23603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (! pid_arg->assume_ptrace_stopped 23703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes && ! __libdwfl_ptrace_attach (tid, &pid_arg->tid_was_stopped)) 23803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 23903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_arg->tid_attached = tid; 24003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Dwfl_Process *process = thread->process; 24103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Ebl *ebl = process->ebl; 24203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return ebl_set_initial_registers_tid (ebl, tid, 24303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_thread_state_registers_cb, thread); 24403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 24503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 24603333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic void 24703333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg) 24803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 24903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes struct __libdwfl_pid_arg *pid_arg = dwfl_arg; 25003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes closedir (pid_arg->dir); 25103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes free (pid_arg); 25203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 25303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 25403333823c75a1c1887e923828113a1b0fd12020cElliott Hughesvoid 25503333823c75a1c1887e923828113a1b0fd12020cElliott Hughesinternal_function 25603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes__libdwfl_ptrace_detach (pid_t tid, bool tid_was_stopped) 25703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 25803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes /* This handling is needed only on older Linux kernels such as 25903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 2.6.32-358.23.2.el6.ppc64. Later kernels such as 26003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 3.11.7-200.fc19.x86_64 remember the T (stopped) state 26103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes themselves and no longer need to pass SIGSTOP during 26203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes PTRACE_DETACH. */ 26303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes ptrace (PTRACE_DETACH, tid, NULL, 26403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes (void *) (intptr_t) (tid_was_stopped ? SIGSTOP : 0)); 26503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 26603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 26703333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic void 26803333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_thread_detach (Dwfl_Thread *thread, void *thread_arg) 26903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 27003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes struct __libdwfl_pid_arg *pid_arg = thread_arg; 27103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_t tid = INTUSE(dwfl_thread_tid) (thread); 27203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes assert (pid_arg->tid_attached == tid); 27303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_arg->tid_attached = 0; 27403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (! pid_arg->assume_ptrace_stopped) 27503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_ptrace_detach (tid, pid_arg->tid_was_stopped); 27603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 27703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 27803333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic const Dwfl_Thread_Callbacks pid_thread_callbacks = 27903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 28003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_next_thread, 28103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_getthread, 28203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_memory_read, 28303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_set_initial_registers, 28403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_detach, 28503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_thread_detach, 28603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes}; 28703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 28803333823c75a1c1887e923828113a1b0fd12020cElliott Hughesint 28903333823c75a1c1887e923828113a1b0fd12020cElliott Hughesdwfl_linux_proc_attach (Dwfl *dwfl, pid_t pid, bool assume_ptrace_stopped) 29003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 29103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes char buffer[36]; 29203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes FILE *procfile; 29303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes int err = 0; /* The errno to return and set for dwfl->attcherr. */ 29403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 29503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes /* Make sure to report the actual PID (thread group leader) to 29603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes dwfl_attach_state. */ 29703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) pid); 29803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes procfile = fopen (buffer, "r"); 29903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (procfile == NULL) 30003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 30103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes err = errno; 30203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes fail: 30303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR) 30403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 30503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = err; 30603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes dwfl->attacherr = __libdwfl_canon_error (DWFL_E_ERRNO); 30703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 30803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return err; 30903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 31003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 31103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes char *line = NULL; 31203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes size_t linelen = 0; 31303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes while (getline (&line, &linelen, procfile) >= 0) 31403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (strncmp (line, "Tgid:", 5) == 0) 31503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 31603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = 0; 31703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes char *endptr; 31803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes long val = strtol (&line[5], &endptr, 10); 31903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if ((errno == ERANGE && val == LONG_MAX) 32003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes || *endptr != '\n' || val < 0 || val != (pid_t) val) 32103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid = 0; 32203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes else 32303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid = (pid_t) val; 32403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes break; 32503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 32603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes free (line); 32703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes fclose (procfile); 32803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 32903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (pid == 0) 33003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 33103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes err = ESRCH; 33203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes goto fail; 33303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 33403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 33503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes char dirname[64]; 33603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes int i = snprintf (dirname, sizeof (dirname), "/proc/%ld/task", (long) pid); 33703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes assert (i > 0 && i < (ssize_t) sizeof (dirname) - 1); 33803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes DIR *dir = opendir (dirname); 33903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (dir == NULL) 34003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 34103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes err = errno; 34203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes goto fail; 34303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 34403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes struct __libdwfl_pid_arg *pid_arg = malloc (sizeof *pid_arg); 34503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (pid_arg == NULL) 34603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 34703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes closedir (dir); 34803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes err = ENOMEM; 34903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes goto fail; 35003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 35103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_arg->dir = dir; 35203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_arg->tid_attached = 0; 35303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_arg->assume_ptrace_stopped = assume_ptrace_stopped; 35403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (! INTUSE(dwfl_attach_state) (dwfl, NULL, pid, &pid_thread_callbacks, 35503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_arg)) 35603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes { 35703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes closedir (dir); 35803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes free (pid_arg); 35903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return -1; 36003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes } 36103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return 0; 36203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 36303333823c75a1c1887e923828113a1b0fd12020cElliott HughesINTDEF (dwfl_linux_proc_attach) 36403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 36503333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstruct __libdwfl_pid_arg * 36603333823c75a1c1887e923828113a1b0fd12020cElliott Hughesinternal_function 36703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes__libdwfl_get_pid_arg (Dwfl *dwfl) 36803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 36903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes if (dwfl != NULL && dwfl->process != NULL 37003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes && dwfl->process->callbacks == &pid_thread_callbacks) 37103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return (struct __libdwfl_pid_arg *) dwfl->process->callbacks_arg; 37203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 37303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return NULL; 37403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 37503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 37603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#else /* __linux__ */ 37703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 37803333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic pid_t 37903333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_next_thread (Dwfl *dwfl __attribute__ ((unused)), 38003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void *dwfl_arg __attribute__ ((unused)), 38103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void **thread_argp __attribute__ ((unused))) 38203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 38303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = ENOSYS; 38403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 38503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return -1; 38603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 38703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 38803333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic bool 38903333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_getthread (Dwfl *dwfl __attribute__ ((unused)), 39003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_t tid __attribute__ ((unused)), 39103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void *dwfl_arg __attribute__ ((unused)), 39203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void **thread_argp __attribute__ ((unused))) 39303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 39403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = ENOSYS; 39503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 39603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 39703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 39803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 39903333823c75a1c1887e923828113a1b0fd12020cElliott Hughesbool 40003333823c75a1c1887e923828113a1b0fd12020cElliott Hughesinternal_function 40103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes__libdwfl_ptrace_attach (pid_t tid __attribute__ ((unused)), 40203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes bool *tid_was_stoppedp __attribute__ ((unused))) 40303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 40403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = ENOSYS; 40503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 40603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 40703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 40803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 40903333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic bool 41003333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_memory_read (Dwfl *dwfl __attribute__ ((unused)), 41103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Dwarf_Addr addr __attribute__ ((unused)), 41203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes Dwarf_Word *result __attribute__ ((unused)), 41303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void *arg __attribute__ ((unused))) 41403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 41503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = ENOSYS; 41603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 41703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 41803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 41903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 42003333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic bool 42103333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_set_initial_registers (Dwfl_Thread *thread __attribute__ ((unused)), 42203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void *thread_arg __attribute__ ((unused))) 42303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 42403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes errno = ENOSYS; 42503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes __libdwfl_seterrno (DWFL_E_ERRNO); 42603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return false; 42703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 42803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 42903333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic void 43003333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_detach (Dwfl *dwfl __attribute__ ((unused)), 43103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void *dwfl_arg __attribute__ ((unused))) 43203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 43303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 43403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 43503333823c75a1c1887e923828113a1b0fd12020cElliott Hughesvoid 43603333823c75a1c1887e923828113a1b0fd12020cElliott Hughesinternal_function 43703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes__libdwfl_ptrace_detach (pid_t tid __attribute__ ((unused)), 43803333823c75a1c1887e923828113a1b0fd12020cElliott Hughes bool tid_was_stopped __attribute__ ((unused))) 43903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 44003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 44103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 44203333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic void 44303333823c75a1c1887e923828113a1b0fd12020cElliott Hughespid_thread_detach (Dwfl_Thread *thread __attribute__ ((unused)), 44403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes void *thread_arg __attribute__ ((unused))) 44503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 44603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 44703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 44803333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstatic const Dwfl_Thread_Callbacks pid_thread_callbacks = 44903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 45003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_next_thread, 45103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_getthread, 45203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_memory_read, 45303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_set_initial_registers, 45403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_detach, 45503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_thread_detach, 45603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes}; 45703333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 45803333823c75a1c1887e923828113a1b0fd12020cElliott Hughesint 45903333823c75a1c1887e923828113a1b0fd12020cElliott Hughesdwfl_linux_proc_attach (Dwfl *dwfl __attribute__ ((unused)), 46003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes pid_t pid __attribute__ ((unused)), 46103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes bool assume_ptrace_stopped __attribute__ ((unused))) 46203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 46303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return ENOSYS; 46403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 46503333823c75a1c1887e923828113a1b0fd12020cElliott HughesINTDEF (dwfl_linux_proc_attach) 46603333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 46703333823c75a1c1887e923828113a1b0fd12020cElliott Hughesstruct __libdwfl_pid_arg * 46803333823c75a1c1887e923828113a1b0fd12020cElliott Hughesinternal_function 46903333823c75a1c1887e923828113a1b0fd12020cElliott Hughes__libdwfl_get_pid_arg (Dwfl *dwfl __attribute__ ((unused))) 47003333823c75a1c1887e923828113a1b0fd12020cElliott Hughes{ 47103333823c75a1c1887e923828113a1b0fd12020cElliott Hughes return NULL; 47203333823c75a1c1887e923828113a1b0fd12020cElliott Hughes} 47303333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 47403333823c75a1c1887e923828113a1b0fd12020cElliott Hughes#endif /* ! __linux __ */ 47503333823c75a1c1887e923828113a1b0fd12020cElliott Hughes 476