1999-02-19 03:21:36 +03:00
/*
* Copyright ( c ) 1991 , 1992 Paul Kranenburg < pk @ cs . few . eur . nl >
* Copyright ( c ) 1993 Branko Lankester < branko @ hacktic . nl >
* Copyright ( c ) 1993 , 1994 , 1995 , 1996 Rick Sladkey < jrs @ world . std . com >
1999-12-23 17:20:14 +03:00
* Copyright ( c ) 1996 - 1999 Wichert Akkerman < wichert @ cistron . nl >
* Copyright ( c ) 1999 IBM Deutschland Entwicklung GmbH , IBM Corporation
* Linux for s390 port by D . J . Barrow
* < barrow_dj @ mail . yahoo . com , djbarrow @ de . ibm . com >
1999-02-19 03:21:36 +03:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* $ Id $
*/
# include "defs.h"
2003-01-09 09:53:34 +03:00
# include <signal.h>
# include <sys/syscall.h>
1999-02-19 03:21:36 +03:00
# include <sys/user.h>
# include <sys/param.h>
# include <fcntl.h>
2001-07-10 17:48:44 +04:00
# if HAVE_SYS_UIO_H
# include <sys/uio.h>
# endif
1999-02-19 03:21:36 +03:00
# ifdef SUNOS4
# include <machine/reg.h>
# include <a.out.h>
# include <link.h>
# endif /* SUNOS4 */
1999-07-13 19:45:02 +04:00
2000-06-27 21:33:32 +04:00
# if defined(linux) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 1))
1999-02-19 03:21:36 +03:00
# include <linux/ptrace.h>
2002-12-16 23:40:54 +03:00
# endif
1999-07-13 19:45:02 +04:00
2000-02-04 00:58:30 +03:00
# if defined(LINUX) && defined(IA64)
2003-01-09 09:53:34 +03:00
# include <asm / ptrace_offsets.h>
# include <asm / rse.h>
2000-02-04 00:58:30 +03:00
# endif
1999-07-13 19:45:02 +04:00
# ifdef HAVE_SYS_REG_H
# include <sys/reg.h>
# define PTRACE_PEEKUSR PTRACE_PEEKUSER
2000-02-20 02:59:03 +03:00
# elif defined(HAVE_LINUX_PTRACE_H)
# undef PTRACE_SYSCALL
2004-03-02 00:29:22 +03:00
# ifdef HAVE_STRUCT_IA64_FPREG
# define ia64_fpreg XXX_ia64_fpreg
# endif
# ifdef HAVE_STRUCT_PT_ALL_USER_REGS
# define pt_all_user_regs XXX_pt_all_user_regs
# endif
2000-02-20 02:59:03 +03:00
# include <linux/ptrace.h>
2004-03-02 00:29:22 +03:00
# undef ia64_fpreg
# undef pt_all_user_regs
1999-05-09 04:29:58 +04:00
# endif
1999-02-19 03:21:36 +03:00
# ifdef SUNOS4_KERNEL_ARCH_KLUDGE
# include <sys/utsname.h>
# endif /* SUNOS4_KERNEL_ARCH_KLUDGE */
sparc/linux: Rewrite to use asm/ptrace.h
The current sparc/linux code uses asm/reg.h, but recent Linux kernels
dropped that header completely. So switch over to the ptrace headers
as those should stick around indefinitely as part of the ABI.
* defs.h [LINUXSPARC] (U_REG_G1, U_REG_O0, U_REG_O1): Define.
* process.c: Drop asm/regs.h include.
[SPARC || SPARC64] (change_syscall): Change struct regs to struct pt_regs.
* signal.c: Drop asm/regs.h include.
(m_siginfo_t): Unify [SPARC || SPARC64] and [MIPS].
[SPARC || SPARC64] (sys_sigreturn): Change struct regs to struct pt_regs.
* syscall.c: Drop asm/regs.h include.
[SPARC || SPARC64] (internal_syscall, get_scno, get_error, force_result,
syscall_enter): Change struct regs to struct pt_regs.
* util.c: Drop asm/regs.h include.
(_hack_syscall5, _ptrace): Delete.
[SPARC || SPARC64] (getpc, printcall, arg_setup_state): Change
struct regs to struct pt_regs.
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2009-10-12 19:05:14 +04:00
# if defined(LINUXSPARC) && defined (SPARC64)
2004-07-07 David S. Miller <davem@nuts.davemloft.net>
* linux/sparc/syscallent.h: Sync with reality.
* linux/sparc/syscall.h (sys_sendfile64, sys_futex, sys_gettid,
sys_sched_setaffinity, sys_sched_getaffinity, sys_setxattr,
sys_lsetxattr, sys_fsetxattr, sys_getxattr, sys_lgetxattr,
sys_fgetxattr, sys_listxattr, sys_llistxattr, sys_flistxattr,
sys_removexattr, sys_lremovexattr, sys_fremovexattr,
sys_remap_file_pages, sys_readahead, sys_tgkill, sys_statfs64,
sys_fstatfs64, sys_clock_settime, sys_clock_gettime,
sys_clock_getres, sys_clock_nanosleep, sys_timer_create,
sys_timer_settime, sys_timer_gettime): New declarations.
* linux/sparc64/dummy2.h, linux/sparc64/syscallent2.h,
linux/sparc64/syscall.h, linux/sparc64/errnoent.h,
linux/sparc64/errnoent1.h, linux/sparc64/errnoent2.h,
linux/sparc64/ioctlent.h, linux/sparc64/ioctlent1.h,
linux/sparc64/ioctlent2.h, linux/sparc64/signalent.h,
linux/sparc64/signalent.h, linux/sparc64/signalent.h,
linux/sparc64/signalent1.h, linux/sparc64/signalent2.h,
linux/sparc64/syscall1.h, linux/sparc64/syscallent.h,
linux/sparc64/syscallent1.h: New files.
* defs.h (LINUXSPARC): Define also when SPARC64.
(LINUX && SPARC64): Set SUPPORTED_PERSONALITIES to 3.
Ignore SIGTRAP after execve by defining TCB_WAITEXECVE.
Define possibly missing __NR_exit_group. Declare getrval2.
* configure.ac (sparc64): New architecture case.
* config.h.in (SPARC64): New define.
* file.c (stat_sparc64): New structure.
(printstat_sparc64): New output routine for that.
(printstat): Call it, if personality is 2.
(printstat64): Likewise.
* util.c: Conditionalize ptrace defines on LINUXSPARC
not LINUX && SPARC.
(SPARC64 && LINUX): Define r_pc to r_tpc, and PTRACE_FOOREGS
to PTRACE_FOOREGS64 so that more sparc code can be shared
between 64-bit and 32-bit.
(_hack_syscall5): Correct trap number when SPARC64.
(PTRACE_WRITE{TEXT,DATA}): Add SPARC64 to ifdef guard.
(getpc): Handle SPARC64 && LINUX.
(printcall): Likewise.
(arg fetching/setting): Use same code for SPARC64 LINUX
as for SPARC.
(setbpt): Handle SPARC64 && LINUX.
(clearbpt): Likewise.
* signal.c: Conditionalize ptrace defines on SPARC and
SPARC64.
(SPARC64 && LINUX): Define r_pc to r_tpc, and PTRACE_FOOREGS
to PTRACE_FOOREGS64 so that more sparc code can be shared
between 64-bit and 32-bit.
(m_siginfo): Use same definition on SPARC64 as SPARC.
(sys_sigreturn): Handle LINUX && SPARC64.
* syscall.c: Conditionalize ptrace defines on SPARC and
SPARC64.
(SPARC64 && LINUX): Define r_pc to r_tpc, and PTRACE_FOOREGS
to PTRACE_FOOREGS64 so that more sparc code can be shared
between 64-bit and 32-bit.
(getscno): Use same static state on SPARC64 as SPARC,
and add SPARC64 handling.
(get_error): Handle LINUX && SPARC64.
(force_result): Likewise.
(syscall_enter): Likewise.
(trace_syscall): Handle sys_socketcall and sys_ipc on SPARC64
just like SPARC.
(getrval2): Handle LINUX && SPARC64.
* process.c: Conditionalize ptrace defines on SPARC and
SPARC64.
(SPARC64 && LINUX): Define r_pc to r_tpc, and PTRACE_FOOREGS
to PTRACE_FOOREGS64 so that more sparc code can be shared
between 64-bit and 32-bit.
(change_syscall): Handle LINUX && SPARC64.
(struct_user_offsets): Ifdef out those which do not exist
on SPARC64.
* net.c (sys_pipe): Handle LINUX && SPARC64.
* ioctl.c: Fix initializer typo for nioctlents2, was
nioctlents1 by accident.
2004-07-12 11:44:08 +04:00
# undef PTRACE_GETREGS
# define PTRACE_GETREGS PTRACE_GETREGS64
# undef PTRACE_SETREGS
# define PTRACE_SETREGS PTRACE_SETREGS64
1999-08-30 03:15:07 +04:00
# endif
1999-02-19 03:21:36 +03:00
/* macros */
# ifndef MAX
# define MAX(a,b) (((a) > (b)) ? (a) : (b))
# endif
# ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
# endif
int
tv_nz ( a )
struct timeval * a ;
{
return a - > tv_sec | | a - > tv_usec ;
}
int
tv_cmp ( a , b )
struct timeval * a , * b ;
{
if ( a - > tv_sec < b - > tv_sec
| | ( a - > tv_sec = = b - > tv_sec & & a - > tv_usec < b - > tv_usec ) )
return - 1 ;
if ( a - > tv_sec > b - > tv_sec
| | ( a - > tv_sec = = b - > tv_sec & & a - > tv_usec > b - > tv_usec ) )
return 1 ;
return 0 ;
}
double
tv_float ( tv )
struct timeval * tv ;
{
return tv - > tv_sec + tv - > tv_usec / 1000000.0 ;
}
void
tv_add ( tv , a , b )
struct timeval * tv , * a , * b ;
{
tv - > tv_sec = a - > tv_sec + b - > tv_sec ;
tv - > tv_usec = a - > tv_usec + b - > tv_usec ;
2007-07-24 05:38:22 +04:00
if ( tv - > tv_usec > = 1000000 ) {
1999-02-19 03:21:36 +03:00
tv - > tv_sec + + ;
tv - > tv_usec - = 1000000 ;
}
}
void
tv_sub ( tv , a , b )
struct timeval * tv , * a , * b ;
{
tv - > tv_sec = a - > tv_sec - b - > tv_sec ;
tv - > tv_usec = a - > tv_usec - b - > tv_usec ;
if ( ( ( long ) tv - > tv_usec ) < 0 ) {
tv - > tv_sec - - ;
tv - > tv_usec + = 1000000 ;
}
}
void
tv_div ( tv , a , n )
struct timeval * tv , * a ;
int n ;
{
tv - > tv_usec = ( a - > tv_sec % n * 1000000 + a - > tv_usec + n / 2 ) / n ;
tv - > tv_sec = a - > tv_sec / n + tv - > tv_usec / 1000000 ;
tv - > tv_usec % = 1000000 ;
}
void
tv_mul ( tv , a , n )
struct timeval * tv , * a ;
int n ;
{
tv - > tv_usec = a - > tv_usec * n ;
2007-06-30 01:25:56 +04:00
tv - > tv_sec = a - > tv_sec * n + tv - > tv_usec / 1000000 ;
1999-02-19 03:21:36 +03:00
tv - > tv_usec % = 1000000 ;
}
2007-01-12 01:05:04 +03:00
const char *
xlookup ( const struct xlat * xlat , int val )
1999-02-19 03:21:36 +03:00
{
for ( ; xlat - > str ! = NULL ; xlat + + )
if ( xlat - > val = = val )
return xlat - > str ;
return NULL ;
}
2008-12-17 22:21:59 +03:00
/*
2009-06-03 03:49:22 +04:00
* Generic ptrace wrapper which tracks ESRCH errors
* by setting tcp - > ptrace_errno to ESRCH .
2008-12-17 22:21:59 +03:00
*
* We assume that ESRCH indicates likely process death ( SIGKILL ? ) ,
* modulo bugs where process somehow ended up not stopped .
* Unfortunately kernel uses ESRCH for that case too . Oh well .
2009-06-03 03:49:22 +04:00
*
* Currently used by upeek ( ) only .
* TODO : use this in all other ptrace ( ) calls while decoding .
2008-12-17 22:21:59 +03:00
*/
long
2009-06-03 03:49:22 +04:00
do_ptrace ( int request , struct tcb * tcp , void * addr , void * data )
2008-12-17 22:21:59 +03:00
{
long l ;
errno = 0 ;
2009-10-07 09:13:39 +04:00
l = ptrace ( request , tcp - > pid , addr , ( long ) data ) ;
2009-06-03 03:49:22 +04:00
/* Non-ESRCH errors might be our invalid reg/mem accesses,
* we do not record them . */
if ( errno = = ESRCH )
tcp - > ptrace_errno = ESRCH ;
2008-12-17 22:21:59 +03:00
return l ;
}
/*
* Used when we want to unblock stopped traced process .
* Should be only used with PTRACE_CONT , PTRACE_DETACH and PTRACE_SYSCALL .
* Returns 0 on success or if error was ESRCH
* ( presumably process was killed while we talk to it ) .
* Otherwise prints error message and returns - 1.
*/
int
2009-06-03 03:49:22 +04:00
ptrace_restart ( int op , struct tcb * tcp , int sig )
2008-12-17 22:21:59 +03:00
{
int err ;
2009-06-03 03:49:22 +04:00
const char * msg ;
2008-12-17 22:21:59 +03:00
errno = 0 ;
2009-10-07 09:13:39 +04:00
ptrace ( op , tcp - > pid , ( void * ) 1 , ( long ) sig ) ;
2008-12-17 22:21:59 +03:00
err = errno ;
if ( ! err | | err = = ESRCH )
return 0 ;
tcp - > ptrace_errno = err ;
2009-06-03 03:49:22 +04:00
msg = " SYSCALL " ;
if ( op = = PTRACE_CONT )
msg = " CONT " ;
if ( op = = PTRACE_DETACH )
msg = " DETACH " ;
fprintf ( stderr , " strace: ptrace(PTRACE_%s,1,%d): %s \n " ,
msg , sig , strerror ( err ) ) ;
2008-12-17 22:21:59 +03:00
return - 1 ;
}
1999-02-19 03:21:36 +03:00
/*
* Print entry in struct xlat table , if there .
*/
void
2007-01-12 01:05:04 +03:00
printxval ( const struct xlat * xlat , int val , const char * dflt )
1999-02-19 03:21:36 +03:00
{
2007-01-12 01:05:04 +03:00
const char * str = xlookup ( xlat , val ) ;
1999-02-19 03:21:36 +03:00
if ( str )
tprintf ( " %s " , str ) ;
else
tprintf ( " %#x /* %s */ " , val , dflt ) ;
}
2009-11-04 19:08:34 +03:00
# if HAVE_LONG_LONG
/*
* Print 64 bit argument at position llarg and return the index of the next
* argument .
*/
int
printllval ( struct tcb * tcp , const char * format , int llarg )
{
# if defined(FREEBSD) \
2010-07-12 23:39:57 +04:00
| | ( defined ( LINUX ) & & defined ( POWERPC ) & & ! defined ( POWERPC64 ) ) \
2009-11-04 19:08:34 +03:00
| | defined ( LINUX_MIPSO32 )
/* Align 64bit argument to 64bit boundary. */
if ( llarg % 2 ) llarg + + ;
# endif
2010-07-12 23:39:57 +04:00
# if defined LINUX && (defined X86_64 || defined POWERPC64)
2009-11-04 19:08:34 +03:00
if ( current_personality = = 0 ) {
tprintf ( format , tcp - > u_arg [ llarg ] ) ;
llarg + + ;
} else {
2010-07-12 23:39:57 +04:00
# ifdef POWERPC64
/* Align 64bit argument to 64bit boundary. */
if ( llarg % 2 ) llarg + + ;
# endif
2009-11-04 19:08:34 +03:00
tprintf ( format , LONG_LONG ( tcp - > u_arg [ llarg ] , tcp - > u_arg [ llarg + 1 ] ) ) ;
llarg + = 2 ;
}
2010-07-12 23:39:57 +04:00
# elif defined IA64 || defined ALPHA
2009-11-04 19:08:34 +03:00
tprintf ( format , tcp - > u_arg [ llarg ] ) ;
llarg + + ;
# elif defined LINUX_MIPSN32
tprintf ( format , tcp - > ext_arg [ llarg ] ) ;
llarg + + ;
# else
tprintf ( format , LONG_LONG ( tcp - > u_arg [ llarg ] , tcp - > u_arg [ llarg + 1 ] ) ) ;
llarg + = 2 ;
# endif
return llarg ;
}
# endif
1999-02-19 03:21:36 +03:00
/*
* Interpret ` xlat ' as an array of flags
* print the entries whose bits are on in ` flags '
* return # of flags printed .
*/
int
addflags ( xlat , flags )
2004-09-04 07:39:20 +04:00
const struct xlat * xlat ;
1999-02-19 03:21:36 +03:00
int flags ;
{
int n ;
for ( n = 0 ; xlat - > str ; xlat + + ) {
if ( xlat - > val & & ( flags & xlat - > val ) = = xlat - > val ) {
tprintf ( " |%s " , xlat - > str ) ;
flags & = ~ xlat - > val ;
n + + ;
}
}
if ( flags ) {
tprintf ( " |%#x " , flags ) ;
n + + ;
}
return n ;
}
2007-11-02 00:46:22 +03:00
/*
* Interpret ` xlat ' as an array of flags /
* Print to static string the entries whose bits are on in ` flags '
* Return static string .
*/
const char *
sprintflags ( const char * prefix , const struct xlat * xlat , int flags )
{
static char outstr [ 1024 ] ;
int found = 0 ;
strcpy ( outstr , prefix ) ;
for ( ; xlat - > str ; xlat + + ) {
if ( ( flags & xlat - > val ) = = xlat - > val ) {
if ( found )
strcat ( outstr , " | " ) ;
strcat ( outstr , xlat - > str ) ;
flags & = ~ xlat - > val ;
found = 1 ;
}
}
if ( flags ) {
if ( found )
strcat ( outstr , " | " ) ;
sprintf ( outstr + strlen ( outstr ) , " %#x " , flags ) ;
}
return outstr ;
}
1999-02-19 03:21:36 +03:00
int
2010-09-07 02:08:24 +04:00
printflags ( const struct xlat * xlat , int flags , const char * dflt )
1999-02-19 03:21:36 +03:00
{
int n ;
2010-09-07 02:08:24 +04:00
const char * sep ;
1999-02-19 03:21:36 +03:00
if ( flags = = 0 & & xlat - > val = = 0 ) {
tprintf ( " %s " , xlat - > str ) ;
return 1 ;
}
sep = " " ;
for ( n = 0 ; xlat - > str ; xlat + + ) {
if ( xlat - > val & & ( flags & xlat - > val ) = = xlat - > val ) {
tprintf ( " %s%s " , sep , xlat - > str ) ;
flags & = ~ xlat - > val ;
sep = " | " ;
n + + ;
}
}
2005-05-31 Dmitry V. Levin <ldv@altlinux.org>
* util.c (printxval): Change third argument from "char *" to
"const char *".
(printflags): Add third argument, "const char *", with similar
meaning to the third argument of printxval().
* defs.h (printxval): Change third argument from "char *" to
"const char *".
(printflags): Add third argument.
* bjm.c (sys_query_module) [LINUX]: Pass third argument to
printflags().
* desc.c (sys_fcntl): Likewise.
(sys_flock) [LOCK_SH]: Likewise.
(print_epoll_event) [HAVE_SYS_EPOLL_H]: Likewise.
* file.c (sys_open): Likewise.
(solaris_open) [LINUXSPARC]: Likewise.
(sys_access): Likewise.
(sys_chflags, sys_fchflags) [FREEBSD]: Likewise.
(realprintstat) [HAVE_LONG_LONG_OFF_T &&
HAVE_STRUCT_STAT_ST_FLAGS]: Likewise.
(printstat64) [HAVE_STAT64 &&
HAVE_STRUCT_STAT_ST_FLAGS]: Likewise.
(sys_setxattr, sys_fsetxattr): Likewise.
* ipc.c (sys_msgget, sys_msgsnd, sys_msgrcv, sys_semget,
sys_shmget, sys_shmat) [LINUX || SUNOS4 || FREEBSD]: Likewise.
(sys_mq_open) [LINUX]: Likewise.
(printmqattr) [HAVE_MQUEUE_H]: Likewise.
* mem.c (print_mmap) [!HAVE_LONG_LONG_OFF_T]: Likewise.
(sys_mmap64) [_LFS64_LARGEFILE || HAVE_LONG_LONG_OFF_T]: Likewise.
(sys_mprotect): Likewise.
(sys_mremap, sys_madvise, sys_mlockall) [LINUX]: Likewise.
(sys_msync) [MS_ASYNC]: Likewise.
(sys_mctl) [MC_SYNC]: Likewise.
(sys_remap_file_pages, sys_mbind, sys_get_mempolicy) [LINUX]:
Likewise.
* net.c (printmsghdr) [HAVE_STRUCT_MSGHDR_MSG_CONTROL]: Likewise.
(sys_send, sys_sendto): Likewise.
(sys_sendmsg) [HAVE_SENDMSG]: Likewise.
(sys_recv, sys_recvfrom): Likewise.
(sys_recvmsg) [HAVE_SENDMSG]: Likewise.
(printicmpfilter) [ICMP_FILTER]: Likewise.
* proc.c (proc_ioctl) [SVR4 && !HAVE_MP_PROCFS || FREEBSD]: Likewise.
* process.c (sys_clone) [LINUX]: Likewise.
(printwaitn): Likewise.
(sys_waitid) [SVR4 || LINUX]: Likewise.
* signal.c (sys_sigvec) [SUNOS4 || FREEBSD]: Likewise.
(sys_sigaction): Likewise.
(printcontext) [SVR4]: Likewise.
(print_stack_t) [LINUX) || FREEBSD]: Likewise.
(sys_rt_sigaction) [LINUX]: Likewise.
* sock.c (sock_ioctl) [LINUX]: Likewise.
* stream.c (sys_putmsg, sys_getmsg): Likewise.
(sys_putpmsg) [SYS_putpmsg]: Likewise.
(sys_getpmsg) [SYS_getpmsg]: Likewise.
(sys_poll): Likewise.
(print_transport_message) [TI_BIND]: Likewise.
(stream_ioctl): Likewise.
* system.c (sys_mount, sys_reboot): Likewise.
(sys_cacheflush) [LINUX && M68K]: Likewise.
(sys_capget, sys_capset) [SYS_capget]: Likewise.
* term.c (term_ioctl) [TIOCMGET]: Likewise.
* time.c (sys_clock_nanosleep, sys_timer_settime) [LINUX]:
Likewise.
Fixes RH#159310.
2005-06-01 23:02:36 +04:00
if ( n ) {
if ( flags ) {
tprintf ( " %s%#x " , sep , flags ) ;
n + + ;
}
} else {
if ( flags ) {
tprintf ( " %#x " , flags ) ;
if ( dflt )
tprintf ( " /* %s */ " , dflt ) ;
} else {
if ( dflt )
tprintf ( " 0 " ) ;
}
1999-02-19 03:21:36 +03:00
}
2005-05-31 Dmitry V. Levin <ldv@altlinux.org>
* util.c (printxval): Change third argument from "char *" to
"const char *".
(printflags): Add third argument, "const char *", with similar
meaning to the third argument of printxval().
* defs.h (printxval): Change third argument from "char *" to
"const char *".
(printflags): Add third argument.
* bjm.c (sys_query_module) [LINUX]: Pass third argument to
printflags().
* desc.c (sys_fcntl): Likewise.
(sys_flock) [LOCK_SH]: Likewise.
(print_epoll_event) [HAVE_SYS_EPOLL_H]: Likewise.
* file.c (sys_open): Likewise.
(solaris_open) [LINUXSPARC]: Likewise.
(sys_access): Likewise.
(sys_chflags, sys_fchflags) [FREEBSD]: Likewise.
(realprintstat) [HAVE_LONG_LONG_OFF_T &&
HAVE_STRUCT_STAT_ST_FLAGS]: Likewise.
(printstat64) [HAVE_STAT64 &&
HAVE_STRUCT_STAT_ST_FLAGS]: Likewise.
(sys_setxattr, sys_fsetxattr): Likewise.
* ipc.c (sys_msgget, sys_msgsnd, sys_msgrcv, sys_semget,
sys_shmget, sys_shmat) [LINUX || SUNOS4 || FREEBSD]: Likewise.
(sys_mq_open) [LINUX]: Likewise.
(printmqattr) [HAVE_MQUEUE_H]: Likewise.
* mem.c (print_mmap) [!HAVE_LONG_LONG_OFF_T]: Likewise.
(sys_mmap64) [_LFS64_LARGEFILE || HAVE_LONG_LONG_OFF_T]: Likewise.
(sys_mprotect): Likewise.
(sys_mremap, sys_madvise, sys_mlockall) [LINUX]: Likewise.
(sys_msync) [MS_ASYNC]: Likewise.
(sys_mctl) [MC_SYNC]: Likewise.
(sys_remap_file_pages, sys_mbind, sys_get_mempolicy) [LINUX]:
Likewise.
* net.c (printmsghdr) [HAVE_STRUCT_MSGHDR_MSG_CONTROL]: Likewise.
(sys_send, sys_sendto): Likewise.
(sys_sendmsg) [HAVE_SENDMSG]: Likewise.
(sys_recv, sys_recvfrom): Likewise.
(sys_recvmsg) [HAVE_SENDMSG]: Likewise.
(printicmpfilter) [ICMP_FILTER]: Likewise.
* proc.c (proc_ioctl) [SVR4 && !HAVE_MP_PROCFS || FREEBSD]: Likewise.
* process.c (sys_clone) [LINUX]: Likewise.
(printwaitn): Likewise.
(sys_waitid) [SVR4 || LINUX]: Likewise.
* signal.c (sys_sigvec) [SUNOS4 || FREEBSD]: Likewise.
(sys_sigaction): Likewise.
(printcontext) [SVR4]: Likewise.
(print_stack_t) [LINUX) || FREEBSD]: Likewise.
(sys_rt_sigaction) [LINUX]: Likewise.
* sock.c (sock_ioctl) [LINUX]: Likewise.
* stream.c (sys_putmsg, sys_getmsg): Likewise.
(sys_putpmsg) [SYS_putpmsg]: Likewise.
(sys_getpmsg) [SYS_getpmsg]: Likewise.
(sys_poll): Likewise.
(print_transport_message) [TI_BIND]: Likewise.
(stream_ioctl): Likewise.
* system.c (sys_mount, sys_reboot): Likewise.
(sys_cacheflush) [LINUX && M68K]: Likewise.
(sys_capget, sys_capset) [SYS_capget]: Likewise.
* term.c (term_ioctl) [TIOCMGET]: Likewise.
* time.c (sys_clock_nanosleep, sys_timer_settime) [LINUX]:
Likewise.
Fixes RH#159310.
2005-06-01 23:02:36 +04:00
1999-02-19 03:21:36 +03:00
return n ;
}
void
2010-09-07 02:08:24 +04:00
printnum ( struct tcb * tcp , long addr , const char * fmt )
1999-02-19 03:21:36 +03:00
{
2003-01-14 12:59:00 +03:00
long num ;
1999-02-19 03:21:36 +03:00
if ( ! addr ) {
tprintf ( " NULL " ) ;
return ;
}
if ( umove ( tcp , addr , & num ) < 0 ) {
tprintf ( " %#lx " , addr ) ;
return ;
}
tprintf ( " [ " ) ;
tprintf ( fmt , num ) ;
tprintf ( " ] " ) ;
}
2005-07-05 03:28:10 +04:00
void
2010-09-07 02:08:24 +04:00
printnum_int ( struct tcb * tcp , long addr , const char * fmt )
2005-07-05 03:28:10 +04:00
{
int num ;
if ( ! addr ) {
tprintf ( " NULL " ) ;
return ;
}
if ( umove ( tcp , addr , & num ) < 0 ) {
tprintf ( " %#lx " , addr ) ;
return ;
}
tprintf ( " [ " ) ;
tprintf ( fmt , num ) ;
tprintf ( " ] " ) ;
}
2003-11-14 01:32:27 +03:00
void
printuid ( text , uid )
const char * text ;
unsigned long uid ;
{
tprintf ( " %s " , text ) ;
tprintf ( ( uid = = - 1 ) ? " %ld " : " %lu " , uid ) ;
}
1999-02-19 03:21:36 +03:00
static char path [ MAXPATHLEN + 1 ] ;
2008-11-11 02:19:13 +03:00
/*
* Quote string ` instr ' of length ` size '
* Write up to ( 3 + ` size ' * 4 ) bytes to ` outstr ' buffer .
* If ` len ' < 0 , treat ` instr ' as a NUL - terminated string
* and quote at most ( ` size ' - 1 ) bytes .
*/
2007-11-02 02:53:59 +03:00
static int
2007-10-09 01:48:01 +04:00
string_quote ( const char * instr , char * outstr , int len , int size )
1999-02-19 03:21:36 +03:00
{
2007-10-09 01:48:01 +04:00
const unsigned char * ustr = ( const unsigned char * ) instr ;
char * s = outstr ;
int usehex = 0 , c , i ;
1999-02-19 03:21:36 +03:00
2007-10-09 01:48:01 +04:00
if ( xflag > 1 )
usehex = 1 ;
else if ( xflag ) {
2008-11-11 02:19:13 +03:00
/* Check for presence of symbol which require
to hex - quote the whole string . */
2007-10-09 01:48:01 +04:00
for ( i = 0 ; i < size ; + + i ) {
c = ustr [ i ] ;
2008-11-11 02:19:13 +03:00
/* Check for NUL-terminated string. */
if ( len < 0 ) {
if ( c = = ' \0 ' )
break ;
/* Quote at most size - 1 bytes. */
if ( i = = size - 1 )
continue ;
}
2007-10-09 01:48:01 +04:00
if ( ! isprint ( c ) & & ! isspace ( c ) ) {
usehex = 1 ;
break ;
}
}
1999-02-19 03:21:36 +03:00
}
2007-10-09 01:48:01 +04:00
* s + + = ' \" ' ;
if ( usehex ) {
2008-11-11 02:19:13 +03:00
/* Hex-quote the whole string. */
2007-10-09 01:48:01 +04:00
for ( i = 0 ; i < size ; + + i ) {
c = ustr [ i ] ;
2008-11-11 02:19:13 +03:00
/* Check for NUL-terminated string. */
if ( len < 0 ) {
if ( c = = ' \0 ' )
break ;
/* Quote at most size - 1 bytes. */
if ( i = = size - 1 )
continue ;
}
2007-10-09 01:48:01 +04:00
sprintf ( s , " \\ x%02x " , c ) ;
s + = 4 ;
}
} else {
for ( i = 0 ; i < size ; + + i ) {
c = ustr [ i ] ;
2008-11-11 02:19:13 +03:00
/* Check for NUL-terminated string. */
if ( len < 0 ) {
if ( c = = ' \0 ' )
break ;
/* Quote at most size - 1 bytes. */
if ( i = = size - 1 )
continue ;
}
2007-10-09 01:48:01 +04:00
switch ( c ) {
case ' \" ' : case ' \\ ' :
* s + + = ' \\ ' ;
* s + + = c ;
break ;
case ' \f ' :
* s + + = ' \\ ' ;
* s + + = ' f ' ;
break ;
case ' \n ' :
* s + + = ' \\ ' ;
* s + + = ' n ' ;
break ;
case ' \r ' :
* s + + = ' \\ ' ;
* s + + = ' r ' ;
break ;
case ' \t ' :
* s + + = ' \\ ' ;
* s + + = ' t ' ;
break ;
case ' \v ' :
* s + + = ' \\ ' ;
* s + + = ' v ' ;
break ;
default :
if ( isprint ( c ) )
* s + + = c ;
else if ( i + 1 < size
& & isdigit ( ustr [ i + 1 ] ) ) {
sprintf ( s , " \\ %03o " , c ) ;
s + = 4 ;
} else {
sprintf ( s , " \\ %o " , c ) ;
s + = strlen ( s ) ;
}
break ;
}
1999-02-19 03:21:36 +03:00
}
}
2007-10-09 01:48:01 +04:00
* s + + = ' \" ' ;
* s = ' \0 ' ;
2007-11-02 02:53:59 +03:00
/* Return nonzero if the string was unterminated. */
return i = = size ;
1999-02-19 03:21:36 +03:00
}
2008-11-11 02:19:13 +03:00
/*
* Print path string specified by address ` addr ' and length ` n ' .
* If path length exceeds ` n ' , append ` . . . ' to the output .
*/
1999-02-19 03:21:36 +03:00
void
2007-10-09 01:48:01 +04:00
printpathn ( struct tcb * tcp , long addr , int n )
1999-02-19 03:21:36 +03:00
{
2008-11-11 02:19:13 +03:00
if ( ! addr ) {
2005-02-06 04:55:07 +03:00
tprintf ( " NULL " ) ;
2007-10-09 01:48:01 +04:00
return ;
}
2008-11-11 02:19:13 +03:00
/* Cap path length to the path buffer size,
and NUL - terminate the buffer . */
if ( n > sizeof path - 1 )
n = sizeof path - 1 ;
2007-10-09 01:48:01 +04:00
path [ n ] = ' \0 ' ;
2008-11-11 02:19:13 +03:00
/* Fetch one byte more to find out whether path length > n. */
2007-10-09 01:48:01 +04:00
if ( umovestr ( tcp , addr , n + 1 , path ) < 0 )
1999-02-19 03:21:36 +03:00
tprintf ( " %#lx " , addr ) ;
else {
2007-10-09 01:48:01 +04:00
static char outstr [ 4 * ( sizeof path - 1 ) + sizeof " \" ... \" " ] ;
int trunc = ( path [ n ] ! = ' \0 ' ) ;
if ( trunc )
path [ n ] = ' \0 ' ;
2008-11-11 02:19:13 +03:00
( void ) string_quote ( path , outstr , - 1 , n + 1 ) ;
if ( trunc )
2007-10-09 01:48:01 +04:00
strcat ( outstr , " ... " ) ;
tprintf ( " %s " , outstr ) ;
1999-02-19 03:21:36 +03:00
}
}
void
2007-10-09 01:48:01 +04:00
printpath ( struct tcb * tcp , long addr )
{
printpathn ( tcp , addr , sizeof path - 1 ) ;
}
2008-11-11 02:19:13 +03:00
/*
* Print string specified by address ` addr ' and length ` len ' .
* If ` len ' < 0 , treat the string as a NUL - terminated string .
* If string length exceeds ` max_strlen ' , append ` . . . ' to the output .
*/
2007-10-09 01:48:01 +04:00
void
printstr ( struct tcb * tcp , long addr , int len )
1999-02-19 03:21:36 +03:00
{
2007-10-09 01:48:01 +04:00
static char * str = NULL ;
1999-02-19 03:21:36 +03:00
static char * outstr ;
2007-11-02 02:53:59 +03:00
int size ;
1999-02-19 03:21:36 +03:00
if ( ! addr ) {
tprintf ( " NULL " ) ;
return ;
}
2008-11-11 02:19:13 +03:00
/* Allocate static buffers if they are not allocated yet. */
if ( ! str )
str = malloc ( max_strlen + 1 ) ;
if ( ! outstr )
outstr = malloc ( 4 * max_strlen + sizeof " \" ... \" " ) ;
if ( ! str | | ! outstr ) {
fprintf ( stderr , " out of memory \n " ) ;
tprintf ( " %#lx " , addr ) ;
return ;
1999-02-19 03:21:36 +03:00
}
2007-10-09 01:48:01 +04:00
1999-02-19 03:21:36 +03:00
if ( len < 0 ) {
2008-11-11 02:19:13 +03:00
/*
* Treat as a NUL - terminated string : fetch one byte more
* because string_quote ( ) quotes one byte less .
*/
2007-10-09 01:48:01 +04:00
size = max_strlen + 1 ;
2008-11-11 02:19:13 +03:00
str [ max_strlen ] = ' \0 ' ;
2007-10-09 01:48:01 +04:00
if ( umovestr ( tcp , addr , size , str ) < 0 ) {
1999-02-19 03:21:36 +03:00
tprintf ( " %#lx " , addr ) ;
return ;
}
}
else {
2008-11-11 02:19:13 +03:00
size = MIN ( len , max_strlen ) ;
2007-10-09 01:48:01 +04:00
if ( umoven ( tcp , addr , size , str ) < 0 ) {
1999-02-19 03:21:36 +03:00
tprintf ( " %#lx " , addr ) ;
return ;
}
}
2008-11-11 02:19:13 +03:00
if ( string_quote ( str , outstr , len , size ) & &
( len < 0 | | len > max_strlen ) )
2007-10-09 01:48:01 +04:00
strcat ( outstr , " ... " ) ;
1999-02-19 03:21:36 +03:00
tprintf ( " %s " , outstr ) ;
}
2001-07-10 17:48:44 +04:00
# if HAVE_SYS_UIO_H
void
dumpiov ( tcp , len , addr )
struct tcb * tcp ;
int len ;
long addr ;
{
2006-12-13 20:08:08 +03:00
# if defined(LINUX) && SUPPORTED_PERSONALITIES > 1
union {
struct { u_int32_t base ; u_int32_t len ; } * iov32 ;
struct { u_int64_t base ; u_int64_t len ; } * iov64 ;
} iovu ;
# define iov iovu.iov64
# define sizeof_iov \
( personality_wordsize [ current_personality ] = = 4 \
? sizeof ( * iovu . iov32 ) : sizeof ( * iovu . iov64 ) )
# define iov_iov_base(i) \
( personality_wordsize [ current_personality ] = = 4 \
? ( u_int64_t ) iovu . iov32 [ i ] . base : iovu . iov64 [ i ] . base )
# define iov_iov_len(i) \
( personality_wordsize [ current_personality ] = = 4 \
? ( u_int64_t ) iovu . iov32 [ i ] . len : iovu . iov64 [ i ] . len )
# else
2001-07-10 17:48:44 +04:00
struct iovec * iov ;
2006-12-13 20:08:08 +03:00
# define sizeof_iov sizeof(*iov)
# define iov_iov_base(i) iov[i].iov_base
# define iov_iov_len(i) iov[i].iov_len
# endif
2001-07-10 17:48:44 +04:00
int i ;
2005-06-01 23:22:06 +04:00
unsigned long size ;
2001-07-10 17:48:44 +04:00
2006-12-13 20:08:08 +03:00
size = sizeof_iov * ( unsigned long ) len ;
if ( size / sizeof_iov ! = len
| | ( iov = malloc ( size ) ) = = NULL ) {
2005-06-01 23:22:06 +04:00
fprintf ( stderr , " out of memory \n " ) ;
2001-07-10 17:48:44 +04:00
return ;
}
2005-06-01 23:22:06 +04:00
if ( umoven ( tcp , addr , size , ( char * ) iov ) > = 0 ) {
2001-07-10 17:48:44 +04:00
for ( i = 0 ; i < len ; i + + ) {
2008-12-30 21:47:55 +03:00
/* include the buffer number to make it easy to
* match up the trace with the source */
tprintf ( " * %lu bytes in buffer %d \n " ,
( unsigned long ) iov_iov_len ( i ) , i ) ;
dumpstr ( tcp , ( long ) iov_iov_base ( i ) ,
iov_iov_len ( i ) ) ;
}
2001-07-10 17:48:44 +04:00
}
free ( ( char * ) iov ) ;
2006-12-13 20:08:08 +03:00
# undef sizeof_iov
# undef iov_iov_base
# undef iov_iov_len
# undef iov
2001-07-10 17:48:44 +04:00
}
# endif
1999-02-19 03:21:36 +03:00
void
dumpstr ( tcp , addr , len )
struct tcb * tcp ;
long addr ;
int len ;
{
static int strsize = - 1 ;
static unsigned char * str ;
static char outstr [ 80 ] ;
char * s ;
int i , j ;
if ( strsize < len ) {
if ( str )
free ( str ) ;
if ( ( str = malloc ( len ) ) = = NULL ) {
2005-06-01 22:55:42 +04:00
fprintf ( stderr , " out of memory \n " ) ;
1999-02-19 03:21:36 +03:00
return ;
}
strsize = len ;
}
if ( umoven ( tcp , addr , len , ( char * ) str ) < 0 )
return ;
for ( i = 0 ; i < len ; i + = 16 ) {
s = outstr ;
sprintf ( s , " | %05x " , i ) ;
s + = 9 ;
for ( j = 0 ; j < 16 ; j + + ) {
if ( j = = 8 )
* s + + = ' ' ;
if ( i + j < len ) {
sprintf ( s , " %02x " , str [ i + j ] ) ;
s + = 3 ;
}
else {
* s + + = ' ' ; * s + + = ' ' ; * s + + = ' ' ;
}
}
* s + + = ' ' ; * s + + = ' ' ;
for ( j = 0 ; j < 16 ; j + + ) {
if ( j = = 8 )
* s + + = ' ' ;
if ( i + j < len ) {
if ( isprint ( str [ i + j ] ) )
* s + + = str [ i + j ] ;
else
* s + + = ' . ' ;
}
else
* s + + = ' ' ;
}
tprintf ( " %s | \n " , outstr ) ;
}
}
# define PAGMASK (~(PAGSIZ - 1))
/*
* move ` len ' bytes of data from process ` pid '
* at address ` addr ' to our space at ` laddr '
*/
int
2009-01-07 00:45:06 +03:00
umoven ( struct tcb * tcp , long addr , int len , char * laddr )
1999-02-19 03:21:36 +03:00
{
# ifdef LINUX
2009-06-03 03:49:22 +04:00
int pid = tcp - > pid ;
1999-02-19 03:21:36 +03:00
int n , m ;
1999-03-15 22:49:42 +03:00
int started = 0 ;
1999-02-19 03:21:36 +03:00
union {
long val ;
char x [ sizeof ( long ) ] ;
} u ;
if ( addr & ( sizeof ( long ) - 1 ) ) {
/* addr not a multiple of sizeof(long) */
n = addr - ( addr & - sizeof ( long ) ) ; /* residue */
addr & = - sizeof ( long ) ; /* residue */
2009-06-03 03:49:22 +04:00
errno = 0 ;
u . val = ptrace ( PTRACE_PEEKDATA , pid , ( char * ) addr , 0 ) ;
if ( errno ) {
if ( started & & ( errno = = EPERM | | errno = = EIO ) ) {
/* Ran into 'end of memory' - stupid "printpath" */
return 0 ;
}
/* But if not started, we had a bogus address. */
if ( addr ! = 0 & & errno ! = EIO & & errno ! = ESRCH )
perror ( " ptrace: umoven " ) ;
return - 1 ;
}
1999-03-15 22:49:42 +03:00
started = 1 ;
1999-02-19 03:21:36 +03:00
memcpy ( laddr , & u . x [ n ] , m = MIN ( sizeof ( long ) - n , len ) ) ;
addr + = sizeof ( long ) , laddr + = m , len - = m ;
}
while ( len ) {
2009-06-03 03:49:22 +04:00
errno = 0 ;
u . val = ptrace ( PTRACE_PEEKDATA , pid , ( char * ) addr , 0 ) ;
if ( errno ) {
if ( started & & ( errno = = EPERM | | errno = = EIO ) ) {
/* Ran into 'end of memory' - stupid "printpath" */
return 0 ;
}
if ( addr ! = 0 & & errno ! = EIO & & errno ! = ESRCH )
perror ( " ptrace: umoven " ) ;
return - 1 ;
}
1999-03-15 22:49:42 +03:00
started = 1 ;
1999-02-19 03:21:36 +03:00
memcpy ( laddr , u . x , m = MIN ( sizeof ( long ) , len ) ) ;
addr + = sizeof ( long ) , laddr + = m , len - = m ;
}
# endif /* LINUX */
# ifdef SUNOS4
int pid = tcp - > pid ;
int n ;
while ( len ) {
n = MIN ( len , PAGSIZ ) ;
n = MIN ( n , ( ( addr + PAGSIZ ) & PAGMASK ) - addr ) ;
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_READDATA , pid ,
( char * ) addr , len , laddr ) < 0 ) {
if ( errno ! = ESRCH ) {
perror ( " umoven: ptrace(PTRACE_READDATA, ...) " ) ;
abort ( ) ;
}
1999-02-19 03:21:36 +03:00
return - 1 ;
}
len - = n ;
addr + = n ;
laddr + = n ;
}
# endif /* SUNOS4 */
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-11-29 18:34:02 +03:00
# ifdef HAVE_MP_PROCFS
2001-05-15 18:53:43 +04:00
int fd = tcp - > pfd_as ;
1999-08-30 03:15:07 +04:00
# else
2001-05-15 18:53:43 +04:00
int fd = tcp - > pfd ;
1999-02-19 03:21:36 +03:00
# endif
2001-05-15 18:53:43 +04:00
lseek ( fd , addr , SEEK_SET ) ;
if ( read ( fd , laddr , len ) = = - 1 )
1999-02-19 03:21:36 +03:00
return - 1 ;
2000-09-02 01:03:06 +04:00
# endif /* USE_PROCFS */
1999-02-19 03:21:36 +03:00
return 0 ;
}
/*
* like ` umove ' but make the additional effort of looking
* for a terminating zero byte .
*/
int
2009-01-07 00:45:06 +03:00
umovestr ( struct tcb * tcp , long addr , int len , char * laddr )
1999-02-19 03:21:36 +03:00
{
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
2001-05-15 18:53:43 +04:00
# ifdef HAVE_MP_PROCFS
int fd = tcp - > pfd_as ;
# else
int fd = tcp - > pfd ;
# endif
/* Some systems (e.g. FreeBSD) can be upset if we read off the
end of valid memory , avoid this by trying to read up
to page boundaries . But we don ' t know what a page is ( and
getpagesize ( 2 ) ( if it exists ) doesn ' t necessarily return
hardware page size ) . Assume all pages > = 1024 ( a - historical
I know ) */
int page = 1024 ; /* How to find this? */
int move = page - ( addr & ( page - 1 ) ) ;
int left = len ;
lseek ( fd , addr , SEEK_SET ) ;
while ( left ) {
if ( move > left ) move = left ;
2001-10-16 14:20:22 +04:00
if ( ( move = read ( fd , laddr , move ) ) < = 0 )
2001-05-15 18:53:43 +04:00
return left ! = len ? 0 : - 1 ;
if ( memchr ( laddr , 0 , move ) ) break ;
left - = move ;
laddr + = move ;
addr + = move ;
move = page ;
}
2000-09-02 01:03:06 +04:00
# else /* !USE_PROCFS */
1999-03-15 22:49:42 +03:00
int started = 0 ;
2009-06-03 03:49:22 +04:00
int pid = tcp - > pid ;
1999-02-19 03:21:36 +03:00
int i , n , m ;
union {
long val ;
char x [ sizeof ( long ) ] ;
} u ;
if ( addr & ( sizeof ( long ) - 1 ) ) {
/* addr not a multiple of sizeof(long) */
n = addr - ( addr & - sizeof ( long ) ) ; /* residue */
addr & = - sizeof ( long ) ; /* residue */
2009-06-03 03:49:22 +04:00
errno = 0 ;
u . val = ptrace ( PTRACE_PEEKDATA , pid , ( char * ) addr , 0 ) ;
if ( errno ) {
if ( started & & ( errno = = EPERM | | errno = = EIO ) ) {
/* Ran into 'end of memory' - stupid "printpath" */
return 0 ;
}
if ( addr ! = 0 & & errno ! = EIO & & errno ! = ESRCH )
perror ( " umovestr " ) ;
return - 1 ;
}
1999-03-15 22:49:42 +03:00
started = 1 ;
1999-02-19 03:21:36 +03:00
memcpy ( laddr , & u . x [ n ] , m = MIN ( sizeof ( long ) - n , len ) ) ;
while ( n & ( sizeof ( long ) - 1 ) )
if ( u . x [ n + + ] = = ' \0 ' )
return 0 ;
addr + = sizeof ( long ) , laddr + = m , len - = m ;
}
while ( len ) {
2009-06-03 03:49:22 +04:00
errno = 0 ;
u . val = ptrace ( PTRACE_PEEKDATA , pid , ( char * ) addr , 0 ) ;
if ( errno ) {
if ( started & & ( errno = = EPERM | | errno = = EIO ) ) {
/* Ran into 'end of memory' - stupid "printpath" */
return 0 ;
}
if ( addr ! = 0 & & errno ! = EIO & & errno ! = ESRCH )
perror ( " umovestr " ) ;
return - 1 ;
}
1999-03-15 22:49:42 +03:00
started = 1 ;
1999-02-19 03:21:36 +03:00
memcpy ( laddr , u . x , m = MIN ( sizeof ( long ) , len ) ) ;
for ( i = 0 ; i < sizeof ( long ) ; i + + )
if ( u . x [ i ] = = ' \0 ' )
return 0 ;
addr + = sizeof ( long ) , laddr + = m , len - = m ;
}
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
2001-05-15 18:53:43 +04:00
return 0 ;
1999-02-19 03:21:36 +03:00
}
# ifdef LINUX
2009-02-09 21:55:59 +03:00
# if !defined (SPARC) && !defined(SPARC64)
# define PTRACE_WRITETEXT 101
# define PTRACE_WRITEDATA 102
# endif /* !SPARC && !SPARC64 */
1999-02-19 03:21:36 +03:00
# endif /* LINUX */
# ifdef SUNOS4
static int
2009-06-03 03:49:22 +04:00
uload ( cmd , pid , addr , len , laddr )
int cmd ;
int pid ;
long addr ;
int len ;
char * laddr ;
1999-02-19 03:21:36 +03:00
{
int peek , poke ;
int n , m ;
union {
long val ;
char x [ sizeof ( long ) ] ;
} u ;
if ( cmd = = PTRACE_WRITETEXT ) {
peek = PTRACE_PEEKTEXT ;
poke = PTRACE_POKETEXT ;
}
else {
peek = PTRACE_PEEKDATA ;
poke = PTRACE_POKEDATA ;
}
if ( addr & ( sizeof ( long ) - 1 ) ) {
/* addr not a multiple of sizeof(long) */
n = addr - ( addr & - sizeof ( long ) ) ; /* residue */
addr & = - sizeof ( long ) ;
2009-06-03 03:49:22 +04:00
errno = 0 ;
u . val = ptrace ( peek , pid , ( char * ) addr , 0 ) ;
if ( errno ) {
perror ( " uload: POKE " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
2009-06-03 03:49:22 +04:00
}
memcpy ( & u . x [ n ] , laddr , m = MIN ( sizeof ( long ) - n , len ) ) ;
if ( ptrace ( poke , pid , ( char * ) addr , u . val ) < 0 ) {
perror ( " uload: POKE " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
2009-06-03 03:49:22 +04:00
addr + = sizeof ( long ) , laddr + = m , len - = m ;
1999-02-19 03:21:36 +03:00
}
while ( len ) {
if ( len < sizeof ( long ) )
2009-06-03 03:49:22 +04:00
u . val = ptrace ( peek , pid , ( char * ) addr , 0 ) ;
memcpy ( u . x , laddr , m = MIN ( sizeof ( long ) , len ) ) ;
if ( ptrace ( poke , pid , ( char * ) addr , u . val ) < 0 ) {
perror ( " uload: POKE " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
2009-06-03 03:49:22 +04:00
addr + = sizeof ( long ) , laddr + = m , len - = m ;
1999-02-19 03:21:36 +03:00
}
return 0 ;
}
2009-06-03 03:49:22 +04:00
int
tload ( pid , addr , len , laddr )
int pid ;
int addr , len ;
char * laddr ;
{
return uload ( PTRACE_WRITETEXT , pid , addr , len , laddr ) ;
}
int
dload ( pid , addr , len , laddr )
int pid ;
int addr ;
int len ;
char * laddr ;
1999-02-19 03:21:36 +03:00
{
2009-06-03 03:49:22 +04:00
return uload ( PTRACE_WRITEDATA , pid , addr , len , laddr ) ;
1999-02-19 03:21:36 +03:00
}
# endif /* SUNOS4 */
2000-09-02 01:03:06 +04:00
# ifndef USE_PROCFS
1999-02-19 03:21:36 +03:00
int
2009-06-03 03:49:22 +04:00
upeek ( tcp , off , res )
struct tcb * tcp ;
long off ;
long * res ;
1999-02-19 03:21:36 +03:00
{
long val ;
2009-02-09 21:55:59 +03:00
# ifdef SUNOS4_KERNEL_ARCH_KLUDGE
1999-02-19 03:21:36 +03:00
{
static int is_sun4m = - 1 ;
struct utsname name ;
/* Round up the usual suspects. */
if ( is_sun4m = = - 1 ) {
if ( uname ( & name ) < 0 ) {
perror ( " upeek: uname? " ) ;
exit ( 1 ) ;
}
is_sun4m = strcmp ( name . machine , " sun4m " ) = = 0 ;
if ( is_sun4m ) {
2004-09-04 07:39:20 +04:00
const struct xlat * x ;
1999-02-19 03:21:36 +03:00
for ( x = struct_user_offsets ; x - > str ; x + + )
x - > val + = 1024 ;
}
}
if ( is_sun4m )
off + = 1024 ;
}
2009-02-09 21:55:59 +03:00
# endif /* SUNOS4_KERNEL_ARCH_KLUDGE */
2009-06-03 03:49:22 +04:00
errno = 0 ;
2008-12-17 22:21:59 +03:00
val = do_ptrace ( PTRACE_PEEKUSER , tcp , ( char * ) off , 0 ) ;
2009-06-03 03:49:22 +04:00
if ( val = = - 1 & & errno ) {
if ( errno ! = ESRCH ) {
char buf [ 60 ] ;
sprintf ( buf , " upeek: ptrace(PTRACE_PEEKUSER,%d,%lu,0) " , tcp - > pid , off ) ;
perror ( buf ) ;
}
1999-02-19 03:21:36 +03:00
return - 1 ;
2009-06-03 03:49:22 +04:00
}
1999-02-19 03:21:36 +03:00
* res = val ;
return 0 ;
}
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
void
2009-02-25 20:08:40 +03:00
printcall ( struct tcb * tcp )
1999-02-19 03:21:36 +03:00
{
2005-02-02 23:55:23 +03:00
# define PRINTBADPC tprintf(sizeof(long) == 4 ? "[????????] " : \
sizeof ( long ) = = 8 ? " [????????????????] " : \
NULL /* crash */ )
1999-02-19 03:21:36 +03:00
# ifdef LINUX
2009-02-09 21:55:59 +03:00
# ifdef I386
1999-02-19 03:21:36 +03:00
long eip ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , 4 * EIP , & eip ) < 0 ) {
2005-02-02 23:55:23 +03:00
PRINTBADPC ;
1999-02-19 03:21:36 +03:00
return ;
}
tprintf ( " [%08lx] " , eip ) ;
2005-02-02 05:48:53 +03:00
2009-02-09 21:55:59 +03:00
# elif defined(S390) || defined(S390X)
2008-12-30 21:47:55 +03:00
long psw ;
2009-06-03 03:49:22 +04:00
if ( upeek ( tcp , PT_PSWADDR , & psw ) < 0 ) {
2008-12-30 21:47:55 +03:00
PRINTBADPC ;
return ;
}
2009-02-09 21:55:59 +03:00
# ifdef S390
2008-12-30 21:47:55 +03:00
tprintf ( " [%08lx] " , psw ) ;
2009-02-09 21:55:59 +03:00
# elif S390X
2008-12-30 21:47:55 +03:00
tprintf ( " [%16lx] " , psw ) ;
2009-02-09 21:55:59 +03:00
# endif
2005-02-02 05:48:53 +03:00
2009-02-09 21:55:59 +03:00
# elif defined(X86_64)
2002-09-23 19:41:01 +04:00
long rip ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , 8 * RIP , & rip ) < 0 ) {
2005-02-02 23:55:23 +03:00
PRINTBADPC ;
2002-09-23 19:41:01 +04:00
return ;
}
tprintf ( " [%16lx] " , rip ) ;
2009-02-09 21:55:59 +03:00
# elif defined(IA64)
2000-02-04 00:58:30 +03:00
long ip ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , PT_B0 , & ip ) < 0 ) {
2005-02-02 23:55:23 +03:00
PRINTBADPC ;
2000-02-04 00:58:30 +03:00
return ;
}
tprintf ( " [%08lx] " , ip ) ;
2009-02-09 21:55:59 +03:00
# elif defined(POWERPC)
1999-02-19 03:21:36 +03:00
long pc ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , sizeof ( unsigned long ) * PT_NIP , & pc ) < 0 ) {
2010-07-12 23:39:57 +04:00
PRINTBADPC ;
1999-02-19 03:21:36 +03:00
return ;
}
2010-07-12 23:39:57 +04:00
# ifdef POWERPC64
tprintf ( " [%016lx] " , pc ) ;
# else
1999-02-19 03:21:36 +03:00
tprintf ( " [%08lx] " , pc ) ;
2010-07-12 23:39:57 +04:00
# endif
2009-02-09 21:55:59 +03:00
# elif defined(M68K)
1999-02-19 03:21:36 +03:00
long pc ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , 4 * PT_PC , & pc ) < 0 ) {
1999-02-19 03:21:36 +03:00
tprintf ( " [????????] " ) ;
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-09 21:55:59 +03:00
# elif defined(ALPHA)
1999-02-19 03:21:36 +03:00
long pc ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , REG_PC , & pc ) < 0 ) {
2005-02-02 23:55:23 +03:00
tprintf ( " [????????????????] " ) ;
1999-02-19 03:21:36 +03:00
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-09 21:55:59 +03:00
# elif defined(SPARC) || defined(SPARC64)
sparc/linux: Rewrite to use asm/ptrace.h
The current sparc/linux code uses asm/reg.h, but recent Linux kernels
dropped that header completely. So switch over to the ptrace headers
as those should stick around indefinitely as part of the ABI.
* defs.h [LINUXSPARC] (U_REG_G1, U_REG_O0, U_REG_O1): Define.
* process.c: Drop asm/regs.h include.
[SPARC || SPARC64] (change_syscall): Change struct regs to struct pt_regs.
* signal.c: Drop asm/regs.h include.
(m_siginfo_t): Unify [SPARC || SPARC64] and [MIPS].
[SPARC || SPARC64] (sys_sigreturn): Change struct regs to struct pt_regs.
* syscall.c: Drop asm/regs.h include.
[SPARC || SPARC64] (internal_syscall, get_scno, get_error, force_result,
syscall_enter): Change struct regs to struct pt_regs.
* util.c: Drop asm/regs.h include.
(_hack_syscall5, _ptrace): Delete.
[SPARC || SPARC64] (getpc, printcall, arg_setup_state): Change
struct regs to struct pt_regs.
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2009-10-12 19:05:14 +04:00
struct pt_regs regs ;
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_GETREGS , tcp - > pid , ( char * ) & regs , 0 ) < 0 ) {
2005-02-02 23:55:23 +03:00
PRINTBADPC ;
1999-02-19 03:21:36 +03:00
return ;
}
sparc/linux: Rewrite to use asm/ptrace.h
The current sparc/linux code uses asm/reg.h, but recent Linux kernels
dropped that header completely. So switch over to the ptrace headers
as those should stick around indefinitely as part of the ABI.
* defs.h [LINUXSPARC] (U_REG_G1, U_REG_O0, U_REG_O1): Define.
* process.c: Drop asm/regs.h include.
[SPARC || SPARC64] (change_syscall): Change struct regs to struct pt_regs.
* signal.c: Drop asm/regs.h include.
(m_siginfo_t): Unify [SPARC || SPARC64] and [MIPS].
[SPARC || SPARC64] (sys_sigreturn): Change struct regs to struct pt_regs.
* syscall.c: Drop asm/regs.h include.
[SPARC || SPARC64] (internal_syscall, get_scno, get_error, force_result,
syscall_enter): Change struct regs to struct pt_regs.
* util.c: Drop asm/regs.h include.
(_hack_syscall5, _ptrace): Delete.
[SPARC || SPARC64] (getpc, printcall, arg_setup_state): Change
struct regs to struct pt_regs.
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2009-10-12 19:05:14 +04:00
# if defined(SPARC64)
tprintf ( " [%08lx] " , regs . tpc ) ;
# else
tprintf ( " [%08lx] " , regs . pc ) ;
# endif
2009-02-09 21:55:59 +03:00
# elif defined(HPPA)
2001-03-27 16:17:16 +04:00
long pc ;
2009-06-03 03:49:22 +04:00
if ( upeek ( tcp , PT_IAOQ0 , & pc ) < 0 ) {
2001-03-27 16:17:16 +04:00
tprintf ( " [????????] " ) ;
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-09 21:55:59 +03:00
# elif defined(MIPS)
2001-04-10 14:22:50 +04:00
long pc ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , REG_EPC , & pc ) < 0 ) {
2001-04-10 14:22:50 +04:00
tprintf ( " [????????] " ) ;
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-09 21:55:59 +03:00
# elif defined(SH)
2008-12-30 21:47:55 +03:00
long pc ;
2002-05-01 20:39:22 +04:00
2008-12-30 21:47:55 +03:00
if ( upeek ( tcp , 4 * REG_PC , & pc ) < 0 ) {
tprintf ( " [????????] " ) ;
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-09 21:55:59 +03:00
# elif defined(SH64)
2003-06-02 23:18:58 +04:00
long pc ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , REG_PC , & pc ) < 0 ) {
2005-02-02 23:55:23 +03:00
tprintf ( " [????????????????] " ) ;
2003-06-02 23:18:58 +04:00
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-09 21:55:59 +03:00
# elif defined(ARM)
2003-06-04 03:28:59 +04:00
long pc ;
2003-06-02 23:18:58 +04:00
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , 4 * 15 , & pc ) < 0 ) {
2005-02-02 23:55:23 +03:00
PRINTBADPC ;
2003-06-04 03:28:59 +04:00
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-27 23:32:52 +03:00
# elif defined(AVR32)
long pc ;
if ( upeek ( tcp , REG_PC , & pc ) < 0 ) {
tprintf ( " [????????] " ) ;
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-09 21:55:59 +03:00
# elif defined(BFIN)
2008-11-11 01:21:41 +03:00
long pc ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , PT_PC , & pc ) < 0 ) {
2008-11-11 01:21:41 +03:00
PRINTBADPC ;
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-25 20:08:40 +03:00
# elif defined(CRISV10)
long pc ;
2009-10-05 18:41:02 +04:00
if ( upeek ( tcp , 4 * PT_IRP , & pc ) < 0 ) {
2009-02-25 20:08:40 +03:00
PRINTBADPC ;
return ;
}
tprintf ( " [%08lx] " , pc ) ;
# elif defined(CRISV32)
long pc ;
2009-10-05 18:41:02 +04:00
if ( upeek ( tcp , 4 * PT_ERP , & pc ) < 0 ) {
2009-02-25 20:08:40 +03:00
PRINTBADPC ;
return ;
}
tprintf ( " [%08lx] " , pc ) ;
2009-02-09 21:55:59 +03:00
# endif /* architecture */
1999-02-19 03:21:36 +03:00
# endif /* LINUX */
# ifdef SUNOS4
struct regs regs ;
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_GETREGS , tcp - > pid , ( char * ) & regs , 0 ) < 0 ) {
perror ( " printcall: ptrace(PTRACE_GETREGS, ...) " ) ;
2005-02-02 23:55:23 +03:00
PRINTBADPC ;
1999-02-19 03:21:36 +03:00
return ;
}
tprintf ( " [%08x] " , regs . r_o7 ) ;
# endif /* SUNOS4 */
# ifdef SVR4
/* XXX */
2005-02-02 23:55:23 +03:00
PRINTBADPC ;
1999-02-19 03:21:36 +03:00
# endif
2000-09-02 01:03:06 +04:00
# ifdef FREEBSD
struct reg regs ;
pread ( tcp - > pfd_reg , & regs , sizeof ( regs ) , 0 ) ;
tprintf ( " [%08x] " , regs . r_eip ) ;
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
}
2009-02-09 21:55:59 +03:00
/*
* These # if ' s are huge , please indent them correctly .
* It ' s easy to get confused otherwise .
*/
2000-09-02 01:03:06 +04:00
# ifndef USE_PROCFS
1999-02-19 03:21:36 +03:00
Remove dead code
* defs.h (tv_tv): Remove.
* net.c (sys_xsetsockaddr): Remove commented out dead code.
* process.c (setarg, sys_execv, sys_execve, struct_user_offsets):
Likewise.
* signal.c (sys_sigsuspend): Likewise.
* strace.c (reaper, trace): Likewise.
* stream.c (internal_stream_ioctl): Likewise.
* syscall.c (trace_syscall): Likewise.
* term.c (term_ioctl): Likewise.
* util.c (tv_tv, umoven, uload, getpc, fixvfork, setbpt, clearbpt):
Likewise.
2010-01-01 01:50:49 +03:00
# ifdef LINUX
2003-01-09 09:53:34 +03:00
2009-02-09 21:55:59 +03:00
# include "syscall.h"
2008-05-20 04:34:34 +04:00
2009-02-09 21:55:59 +03:00
# include <sys / syscall.h>
# ifndef CLONE_PTRACE
# define CLONE_PTRACE 0x00002000
# endif
# ifndef CLONE_VFORK
# define CLONE_VFORK 0x00004000
# endif
# ifndef CLONE_VM
# define CLONE_VM 0x00000100
# endif
# ifndef CLONE_STOPPED
# define CLONE_STOPPED 0x02000000
# endif
2003-01-09 09:53:34 +03:00
2009-02-09 21:55:59 +03:00
# ifdef IA64
2003-01-09 09:53:34 +03:00
2004-02-21 01:56:43 +03:00
/* We don't have fork()/vfork() syscalls on ia64 itself, but the ia32
subsystem has them for x86 . . . */
2009-02-09 21:55:59 +03:00
# define SYS_fork 2
# define SYS_vfork 190
2004-02-21 01:56:43 +03:00
2003-01-09 09:53:34 +03:00
typedef unsigned long * arg_setup_state ;
static int
arg_setup ( struct tcb * tcp , arg_setup_state * state )
{
2008-08-07 01:38:52 +04:00
unsigned long cfm , sof , sol ;
long bsp ;
2003-01-09 09:53:34 +03:00
2008-08-07 01:38:52 +04:00
if ( ia32 ) {
/* Satisfy a false GCC warning. */
* state = NULL ;
2004-02-21 01:56:43 +03:00
return 0 ;
2008-08-07 01:38:52 +04:00
}
2004-02-21 01:56:43 +03:00
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , PT_AR_BSP , & bsp ) < 0 )
2003-01-09 09:53:34 +03:00
return - 1 ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , PT_CFM , ( long * ) & cfm ) < 0 )
2003-01-09 09:53:34 +03:00
return - 1 ;
sof = ( cfm > > 0 ) & 0x7f ;
sol = ( cfm > > 7 ) & 0x7f ;
2008-08-07 01:38:52 +04:00
bsp = ( long ) ia64_rse_skip_regs ( ( unsigned long * ) bsp , - sof + sol ) ;
2003-01-09 09:53:34 +03:00
2008-08-07 01:38:52 +04:00
* state = ( unsigned long * ) bsp ;
2003-01-09 09:53:34 +03:00
return 0 ;
}
2009-02-09 21:55:59 +03:00
# define arg_finish_change(tcp, state) 0
2003-01-09 09:53:34 +03:00
2009-02-09 21:55:59 +03:00
# ifdef SYS_fork
2003-01-09 09:53:34 +03:00
static int
get_arg0 ( struct tcb * tcp , arg_setup_state * state , long * valp )
{
2004-02-21 01:56:43 +03:00
int ret ;
if ( ia32 )
2008-12-16 21:18:40 +03:00
ret = upeek ( tcp , PT_R11 , valp ) ;
2004-02-21 01:56:43 +03:00
else
ret = umoven ( tcp ,
( unsigned long ) ia64_rse_skip_regs ( * state , 0 ) ,
sizeof ( long ) , ( void * ) valp ) ;
return ret ;
2003-01-09 09:53:34 +03:00
}
static int
get_arg1 ( struct tcb * tcp , arg_setup_state * state , long * valp )
{
2004-02-21 01:56:43 +03:00
int ret ;
if ( ia32 )
2008-12-16 21:18:40 +03:00
ret = upeek ( tcp , PT_R9 , valp ) ;
2004-02-21 01:56:43 +03:00
else
ret = umoven ( tcp ,
( unsigned long ) ia64_rse_skip_regs ( * state , 1 ) ,
sizeof ( long ) , ( void * ) valp ) ;
return ret ;
2003-01-09 09:53:34 +03:00
}
2009-02-09 21:55:59 +03:00
# endif
2003-01-09 09:53:34 +03:00
static int
set_arg0 ( struct tcb * tcp , arg_setup_state * state , long val )
{
2004-02-21 01:56:43 +03:00
int req = PTRACE_POKEDATA ;
void * ap ;
if ( ia32 ) {
ap = ( void * ) ( intptr_t ) PT_R11 ; /* r11 == EBX */
req = PTRACE_POKEUSER ;
} else
ap = ia64_rse_skip_regs ( * state , 0 ) ;
2009-06-03 03:49:22 +04:00
errno = 0 ;
ptrace ( req , tcp - > pid , ap , val ) ;
return errno ? - 1 : 0 ;
2003-01-09 09:53:34 +03:00
}
static int
set_arg1 ( struct tcb * tcp , arg_setup_state * state , long val )
{
2004-02-21 01:56:43 +03:00
int req = PTRACE_POKEDATA ;
void * ap ;
if ( ia32 ) {
ap = ( void * ) ( intptr_t ) PT_R9 ; /* r9 == ECX */
req = PTRACE_POKEUSER ;
} else
ap = ia64_rse_skip_regs ( * state , 1 ) ;
2009-06-03 03:49:22 +04:00
errno = 0 ;
ptrace ( req , tcp - > pid , ap , val ) ;
return errno ? - 1 : 0 ;
2003-01-09 09:53:34 +03:00
}
2008-07-18 05:19:36 +04:00
/* ia64 does not return the input arguments from functions (and syscalls)
according to ia64 RSE ( Register Stack Engine ) behavior . */
2009-02-09 21:55:59 +03:00
# define restore_arg0(tcp, state, val) ((void) (state), 0)
# define restore_arg1(tcp, state, val) ((void) (state), 0)
2008-07-18 05:19:36 +04:00
2009-02-09 21:55:59 +03:00
# elif defined (SPARC) || defined (SPARC64)
2003-01-09 09:53:34 +03:00
sparc/linux: Rewrite to use asm/ptrace.h
The current sparc/linux code uses asm/reg.h, but recent Linux kernels
dropped that header completely. So switch over to the ptrace headers
as those should stick around indefinitely as part of the ABI.
* defs.h [LINUXSPARC] (U_REG_G1, U_REG_O0, U_REG_O1): Define.
* process.c: Drop asm/regs.h include.
[SPARC || SPARC64] (change_syscall): Change struct regs to struct pt_regs.
* signal.c: Drop asm/regs.h include.
(m_siginfo_t): Unify [SPARC || SPARC64] and [MIPS].
[SPARC || SPARC64] (sys_sigreturn): Change struct regs to struct pt_regs.
* syscall.c: Drop asm/regs.h include.
[SPARC || SPARC64] (internal_syscall, get_scno, get_error, force_result,
syscall_enter): Change struct regs to struct pt_regs.
* util.c: Drop asm/regs.h include.
(_hack_syscall5, _ptrace): Delete.
[SPARC || SPARC64] (getpc, printcall, arg_setup_state): Change
struct regs to struct pt_regs.
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2009-10-12 19:05:14 +04:00
typedef struct pt_regs arg_setup_state ;
2003-01-09 09:53:34 +03:00
2009-02-09 21:55:59 +03:00
# define arg_setup(tcp, state) \
2009-06-03 03:49:22 +04:00
( ptrace ( PTRACE_GETREGS , tcp - > pid , ( char * ) ( state ) , 0 ) )
2009-02-09 21:55:59 +03:00
# define arg_finish_change(tcp, state) \
2009-06-03 03:49:22 +04:00
( ptrace ( PTRACE_SETREGS , tcp - > pid , ( char * ) ( state ) , 0 ) )
2003-01-09 09:53:34 +03:00
sparc/linux: Rewrite to use asm/ptrace.h
The current sparc/linux code uses asm/reg.h, but recent Linux kernels
dropped that header completely. So switch over to the ptrace headers
as those should stick around indefinitely as part of the ABI.
* defs.h [LINUXSPARC] (U_REG_G1, U_REG_O0, U_REG_O1): Define.
* process.c: Drop asm/regs.h include.
[SPARC || SPARC64] (change_syscall): Change struct regs to struct pt_regs.
* signal.c: Drop asm/regs.h include.
(m_siginfo_t): Unify [SPARC || SPARC64] and [MIPS].
[SPARC || SPARC64] (sys_sigreturn): Change struct regs to struct pt_regs.
* syscall.c: Drop asm/regs.h include.
[SPARC || SPARC64] (internal_syscall, get_scno, get_error, force_result,
syscall_enter): Change struct regs to struct pt_regs.
* util.c: Drop asm/regs.h include.
(_hack_syscall5, _ptrace): Delete.
[SPARC || SPARC64] (getpc, printcall, arg_setup_state): Change
struct regs to struct pt_regs.
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2009-10-12 19:05:14 +04:00
# define get_arg0(tcp, state, valp) (*(valp) = (state)->u_regs[U_REG_O0], 0)
# define get_arg1(tcp, state, valp) (*(valp) = (state)->u_regs[U_REG_O1], 0)
# define set_arg0(tcp, state, val) ((state)->u_regs[U_REG_O0] = (val), 0)
# define set_arg1(tcp, state, val) ((state)->u_regs[U_REG_O1] = (val), 0)
2009-02-09 21:55:59 +03:00
# define restore_arg0(tcp, state, val) 0
2003-01-09 09:53:34 +03:00
2009-02-09 21:55:59 +03:00
# else /* other architectures */
2003-01-09 09:53:34 +03:00
2009-02-09 21:55:59 +03:00
# if defined S390 || defined S390X
2003-01-20 12:04:36 +03:00
/* Note: this is only true for the `clone' system call, which handles
arguments specially . We could as well say that its first two arguments
are swapped relative to other architectures , but that would just be
another # ifdef in the calls . */
2009-02-09 21:55:59 +03:00
# define arg0_offset PT_GPR3
# define arg1_offset PT_ORIGGPR2
# define restore_arg0(tcp, state, val) ((void) (state), 0)
# define restore_arg1(tcp, state, val) ((void) (state), 0)
# define arg0_index 1
# define arg1_index 0
# elif defined (ALPHA) || defined (MIPS)
# define arg0_offset REG_A0
# define arg1_offset (REG_A0+1)
2009-02-27 23:32:52 +03:00
# elif defined (AVR32)
# define arg0_offset (REG_R12)
# define arg1_offset (REG_R11)
2009-02-09 21:55:59 +03:00
# elif defined (POWERPC)
# define arg0_offset (sizeof(unsigned long)*PT_R3)
# define arg1_offset (sizeof(unsigned long)*PT_R4)
# define restore_arg0(tcp, state, val) ((void) (state), 0)
# elif defined (HPPA)
# define arg0_offset PT_GR26
# define arg1_offset (PT_GR26-4)
# elif defined (X86_64)
# define arg0_offset ((long)(8*(current_personality ? RBX : RDI)))
# define arg1_offset ((long)(8*(current_personality ? RCX : RSI)))
# elif defined (SH)
# define arg0_offset (4*(REG_REG0+4))
# define arg1_offset (4*(REG_REG0+5))
# elif defined (SH64)
/* ABI defines arg0 & 1 in r2 & r3 */
# define arg0_offset (REG_OFFSET+16)
# define arg1_offset (REG_OFFSET+24)
# define restore_arg0(tcp, state, val) 0
2009-02-25 20:08:40 +03:00
# elif defined CRISV10 || defined CRISV32
# define arg0_offset (4*PT_R11)
# define arg1_offset (4*PT_ORIG_R10)
# define restore_arg0(tcp, state, val) 0
# define restore_arg1(tcp, state, val) 0
# define arg0_index 1
# define arg1_index 0
2009-02-09 21:55:59 +03:00
# else
# define arg0_offset 0
# define arg1_offset 4
# if defined ARM
# define restore_arg0(tcp, state, val) 0
# endif
# endif
2003-01-09 09:53:34 +03:00
typedef int arg_setup_state ;
2009-02-09 21:55:59 +03:00
# define arg_setup(tcp, state) (0)
# define arg_finish_change(tcp, state) 0
# define get_arg0(tcp, cookie, valp) \
( upeek ( ( tcp ) , arg0_offset , ( valp ) ) )
# define get_arg1(tcp, cookie, valp) \
( upeek ( ( tcp ) , arg1_offset , ( valp ) ) )
2003-01-09 09:53:34 +03:00
static int
2009-06-03 03:49:22 +04:00
set_arg0 ( struct tcb * tcp , void * cookie , long val )
2003-01-09 09:53:34 +03:00
{
2009-06-03 03:49:22 +04:00
return ptrace ( PTRACE_POKEUSER , tcp - > pid , ( char * ) arg0_offset , val ) ;
2003-01-09 09:53:34 +03:00
}
static int
2009-06-03 03:49:22 +04:00
set_arg1 ( struct tcb * tcp , void * cookie , long val )
2003-01-09 09:53:34 +03:00
{
2009-06-03 03:49:22 +04:00
return ptrace ( PTRACE_POKEUSER , tcp - > pid , ( char * ) arg1_offset , val ) ;
2003-01-09 09:53:34 +03:00
}
2009-02-09 21:55:59 +03:00
# endif /* architectures */
2003-01-09 09:53:34 +03:00
2009-02-09 21:55:59 +03:00
# ifndef restore_arg0
# define restore_arg0(tcp, state, val) set_arg0((tcp), (state), (val))
# endif
# ifndef restore_arg1
# define restore_arg1(tcp, state, val) set_arg1((tcp), (state), (val))
# endif
2003-01-09 09:53:34 +03:00
2009-02-09 21:55:59 +03:00
# ifndef arg0_index
# define arg0_index 0
# define arg1_index 1
# endif
2004-03-02 00:05:16 +03:00
2003-01-09 09:53:34 +03:00
int
Two cleanups: tcb table expansion failure is not really a survivable
event, we do not have any viable way to continue. No wonder most
places where that is detected have FIXMEs.
It's way simpler to treat as fatal failure, and handle it inside
tcb table expansion finctions.
Second cleanup: tidy up haphazard locations of a few externs.
* defs.h: Change return type of expand_tcbtab() to void.
Declare change_syscall().
* process.c: Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(fork_tcb): Change return type to void - it can't fail now.
* strace.c: Move extern declarations out of function bodies.
Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(expand_tcbtab): Change return type to void - it can't fail now.
On failure to expand, print a message, clean up, and exit.
(alloc_tcb): On failure to expand, print a message, clean up, and exit.
* util.c (setbpt): Remove extern declaration from function body.
2009-01-17 04:52:54 +03:00
setbpt ( struct tcb * tcp )
2003-01-09 09:53:34 +03:00
{
2008-05-20 04:34:34 +04:00
static int clone_scno [ SUPPORTED_PERSONALITIES ] = { SYS_clone } ;
2003-01-09 09:53:34 +03:00
arg_setup_state state ;
if ( tcp - > flags & TCB_BPTSET ) {
fprintf ( stderr , " PANIC: TCB already set in pid %u \n " , tcp - > pid ) ;
return - 1 ;
}
2008-05-20 04:34:34 +04:00
/*
* It ' s a silly kludge to initialize this with a search at runtime .
* But it ' s better than maintaining another magic thing in the
* godforsaken tables .
*/
if ( clone_scno [ current_personality ] = = 0 ) {
int i ;
for ( i = 0 ; i < nsyscalls ; + + i )
if ( sysent [ i ] . sys_func = = sys_clone ) {
clone_scno [ current_personality ] = i ;
break ;
}
}
2005-06-08 03:21:31 +04:00
switch ( known_scno ( tcp ) ) {
2009-02-09 21:55:59 +03:00
# ifdef SYS_vfork
2003-01-18 03:19:31 +03:00
case SYS_vfork :
2009-02-09 21:55:59 +03:00
# endif
# ifdef SYS_fork
2003-01-09 09:53:34 +03:00
case SYS_fork :
2009-02-09 21:55:59 +03:00
# endif
# if defined SYS_fork || defined SYS_vfork
2003-01-09 09:53:34 +03:00
if ( arg_setup ( tcp , & state ) < 0
| | get_arg0 ( tcp , & state , & tcp - > inst [ 0 ] ) < 0
| | get_arg1 ( tcp , & state , & tcp - > inst [ 1 ] ) < 0
2008-05-20 04:34:34 +04:00
| | change_syscall ( tcp , clone_scno [ current_personality ] ) < 0
2003-01-09 09:53:34 +03:00
| | set_arg0 ( tcp , & state , CLONE_PTRACE | SIGCHLD ) < 0
| | set_arg1 ( tcp , & state , 0 ) < 0
| | arg_finish_change ( tcp , & state ) < 0 )
return - 1 ;
2004-03-01 23:57:09 +03:00
tcp - > u_arg [ arg0_index ] = CLONE_PTRACE | SIGCHLD ;
tcp - > u_arg [ arg1_index ] = 0 ;
2003-01-09 09:53:34 +03:00
tcp - > flags | = TCB_BPTSET ;
return 0 ;
2009-02-09 21:55:59 +03:00
# endif
2003-01-09 09:53:34 +03:00
case SYS_clone :
2009-02-09 21:55:59 +03:00
# ifdef SYS_clone2
2003-01-09 09:53:34 +03:00
case SYS_clone2 :
2009-02-09 21:55:59 +03:00
# endif
2008-08-07 01:43:35 +04:00
/* ia64 calls directly `clone (CLONE_VFORK | CLONE_VM)'
contrary to x86 SYS_vfork above . Even on x86 we turn the
vfork semantics into plain fork - each application must not
depend on the vfork specifics according to POSIX . We would
hang waiting for the parent resume otherwise . We need to
clear also CLONE_VM but only in the CLONE_VFORK case as
otherwise we would break pthread_create . */
2008-07-18 05:09:44 +04:00
if ( ( arg_setup ( tcp , & state ) < 0
| | set_arg0 ( tcp , & state ,
( tcp - > u_arg [ arg0_index ] | CLONE_PTRACE )
2008-08-07 01:43:35 +04:00
& ~ ( tcp - > u_arg [ arg0_index ] & CLONE_VFORK
? CLONE_VFORK | CLONE_VM : 0 ) ) < 0
2008-07-18 05:09:44 +04:00
| | arg_finish_change ( tcp , & state ) < 0 ) )
2009-02-27 23:32:52 +03:00
return - 1 ;
2003-01-09 09:53:34 +03:00
tcp - > flags | = TCB_BPTSET ;
2004-03-01 23:57:09 +03:00
tcp - > inst [ 0 ] = tcp - > u_arg [ arg0_index ] ;
tcp - > inst [ 1 ] = tcp - > u_arg [ arg1_index ] ;
2003-01-09 09:53:34 +03:00
return 0 ;
default :
fprintf ( stderr , " PANIC: setbpt for syscall %ld on %u??? \n " ,
tcp - > scno , tcp - > pid ) ;
break ;
}
return - 1 ;
}
int
clearbpt ( tcp )
struct tcb * tcp ;
{
arg_setup_state state ;
if ( arg_setup ( tcp , & state ) < 0
2003-01-14 12:46:15 +03:00
| | restore_arg0 ( tcp , & state , tcp - > inst [ 0 ] ) < 0
| | restore_arg1 ( tcp , & state , tcp - > inst [ 1 ] ) < 0
2003-01-09 09:53:34 +03:00
| | arg_finish_change ( tcp , & state ) )
2009-11-11 15:54:04 +03:00
if ( errno ! = ESRCH ) return - 1 ;
2003-01-09 09:53:34 +03:00
tcp - > flags & = ~ TCB_BPTSET ;
return 0 ;
}
2009-02-09 21:55:59 +03:00
# else /* !defined LINUX */
2003-01-09 09:53:34 +03:00
1999-02-19 03:21:36 +03:00
int
setbpt ( tcp )
struct tcb * tcp ;
{
2009-02-09 21:55:59 +03:00
# ifdef SUNOS4
# ifdef SPARC /* This code is slightly sparc specific */
1999-02-19 03:21:36 +03:00
1999-06-22 19:28:30 +04:00
struct regs regs ;
2009-02-09 21:55:59 +03:00
# define BPT 0x91d02001 /* ta 1 */
# define LOOP 0x10800000 /* ba 0 */
# define LOOPA 0x30800000 /* ba,a 0 */
# define NOP 0x01000000
# if LOOPA
1999-02-19 03:21:36 +03:00
static int loopdeloop [ 1 ] = { LOOPA } ;
2009-02-09 21:55:59 +03:00
# else
1999-02-19 03:21:36 +03:00
static int loopdeloop [ 2 ] = { LOOP , NOP } ;
2009-02-09 21:55:59 +03:00
# endif
1999-02-19 03:21:36 +03:00
if ( tcp - > flags & TCB_BPTSET ) {
fprintf ( stderr , " PANIC: TCB already set in pid %u \n " , tcp - > pid ) ;
return - 1 ;
}
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_GETREGS , tcp - > pid , ( char * ) & regs , 0 ) < 0 ) {
perror ( " setbpt: ptrace(PTRACE_GETREGS, ...) " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
tcp - > baddr = regs . r_o7 + 8 ;
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_READTEXT , tcp - > pid , ( char * ) tcp - > baddr ,
sizeof tcp - > inst , ( char * ) tcp - > inst ) < 0 ) {
perror ( " setbpt: ptrace(PTRACE_READTEXT, ...) " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
/*
* XXX - BRUTAL MODE ON
* We cannot set a real BPT in the child , since it will not be
* traced at the moment it will reach the trap and would probably
* die with a core dump .
* Thus , we are force our way in by taking out two instructions
* and insert an eternal loop in stead , in expectance of the SIGSTOP
* generated by out PTRACE_ATTACH .
* Of cause , if we evaporate ourselves in the middle of all this . . .
*/
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_WRITETEXT , tcp - > pid , ( char * ) tcp - > baddr ,
1999-02-19 03:21:36 +03:00
sizeof loopdeloop , ( char * ) loopdeloop ) < 0 ) {
2009-06-03 03:49:22 +04:00
perror ( " setbpt: ptrace(PTRACE_WRITETEXT, ...) " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
tcp - > flags | = TCB_BPTSET ;
2009-02-09 21:55:59 +03:00
# endif /* SPARC */
# endif /* SUNOS4 */
1999-02-19 03:21:36 +03:00
return 0 ;
}
int
clearbpt ( tcp )
struct tcb * tcp ;
{
2009-02-09 21:55:59 +03:00
# ifdef SUNOS4
# ifdef SPARC
1999-02-19 03:21:36 +03:00
2009-02-09 21:55:59 +03:00
# if !LOOPA
1999-06-22 19:28:30 +04:00
struct regs regs ;
2009-02-09 21:55:59 +03:00
# endif
1999-02-19 03:21:36 +03:00
if ( ! ( tcp - > flags & TCB_BPTSET ) ) {
fprintf ( stderr , " PANIC: TCB not set in pid %u \n " , tcp - > pid ) ;
return - 1 ;
}
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_WRITETEXT , tcp - > pid , ( char * ) tcp - > baddr ,
1999-02-19 03:21:36 +03:00
sizeof tcp - > inst , ( char * ) tcp - > inst ) < 0 ) {
2009-06-03 03:49:22 +04:00
perror ( " clearbtp: ptrace(PTRACE_WRITETEXT, ...) " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
tcp - > flags & = ~ TCB_BPTSET ;
2009-02-09 21:55:59 +03:00
# if !LOOPA
1999-02-19 03:21:36 +03:00
/*
* Since we don ' t have a single instruction breakpoint , we may have
2009-02-09 21:55:59 +03:00
* to adjust the program counter after removing our ` breakpoint ' .
1999-02-19 03:21:36 +03:00
*/
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_GETREGS , tcp - > pid , ( char * ) & regs , 0 ) < 0 ) {
perror ( " clearbpt: ptrace(PTRACE_GETREGS, ...) " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
2009-06-03 03:49:22 +04:00
if ( ( regs . r_pc < tcp - > baddr ) | |
( regs . r_pc > tcp - > baddr + 4 ) ) {
1999-02-19 03:21:36 +03:00
/* The breakpoint has not been reached yet */
if ( debug )
fprintf ( stderr ,
" NOTE: PC not at bpt (pc %#x baddr %#x) \n " ,
2009-02-09 21:55:59 +03:00
regs . r_pc , tcp - > baddr ) ;
1999-02-19 03:21:36 +03:00
return 0 ;
}
if ( regs . r_pc ! = tcp - > baddr )
if ( debug )
fprintf ( stderr , " NOTE: PC adjusted (%#x -> %#x \n " ,
regs . r_pc , tcp - > baddr ) ;
regs . r_pc = tcp - > baddr ;
2009-06-03 03:49:22 +04:00
if ( ptrace ( PTRACE_SETREGS , tcp - > pid , ( char * ) & regs , 0 ) < 0 ) {
perror ( " clearbpt: ptrace(PTRACE_SETREGS, ...) " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
2009-02-09 21:55:59 +03:00
# endif /* LOOPA */
# endif /* SPARC */
# endif /* SUNOS4 */
1999-02-19 03:21:36 +03:00
return 0 ;
}
2009-02-09 21:55:59 +03:00
# endif /* !defined LINUX */
2003-01-09 09:53:34 +03:00
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
2009-02-09 21:55:59 +03:00
1999-02-19 03:21:36 +03:00
# ifdef SUNOS4
static int
2008-12-16 21:18:40 +03:00
getex ( tcp , hdr )
struct tcb * tcp ;
1999-02-19 03:21:36 +03:00
struct exec * hdr ;
{
int n ;
for ( n = 0 ; n < sizeof * hdr ; n + = 4 ) {
long res ;
2008-12-16 21:18:40 +03:00
if ( upeek ( tcp , uoff ( u_exdata ) + n , & res ) < 0 )
1999-02-19 03:21:36 +03:00
return - 1 ;
memcpy ( ( ( char * ) hdr ) + n , & res , 4 ) ;
}
if ( debug ) {
fprintf ( stderr , " [struct exec: magic: %o version %u Mach %o \n " ,
hdr - > a_magic , hdr - > a_toolversion , hdr - > a_machtype ) ;
fprintf ( stderr , " Text %lu Data %lu Bss %lu Syms %lu Entry %#lx] \n " ,
hdr - > a_text , hdr - > a_data , hdr - > a_bss , hdr - > a_syms , hdr - > a_entry ) ;
}
return 0 ;
}
int
fixvfork ( tcp )
struct tcb * tcp ;
{
int pid = tcp - > pid ;
/*
* Change ` vfork ' in a freshly exec ' ed dynamically linked
* executable ' s ( internal ) symbol table to plain old ` fork '
*/
struct exec hdr ;
struct link_dynamic dyn ;
struct link_dynamic_2 ld ;
char * strtab , * cp ;
2008-12-16 21:18:40 +03:00
if ( getex ( tcp , & hdr ) < 0 )
1999-02-19 03:21:36 +03:00
return - 1 ;
if ( ! hdr . a_dynamic )
return - 1 ;
if ( umove ( tcp , ( int ) N_DATADDR ( hdr ) , & dyn ) < 0 ) {
fprintf ( stderr , " Cannot read DYNAMIC \n " ) ;
return - 1 ;
}
if ( umove ( tcp , ( int ) dyn . ld_un . ld_2 , & ld ) < 0 ) {
fprintf ( stderr , " Cannot read link_dynamic_2 \n " ) ;
return - 1 ;
}
if ( ( strtab = malloc ( ( unsigned ) ld . ld_symb_size ) ) = = NULL ) {
2005-06-01 22:55:42 +04:00
fprintf ( stderr , " out of memory \n " ) ;
1999-02-19 03:21:36 +03:00
return - 1 ;
}
2009-06-03 03:49:22 +04:00
if ( umoven ( tcp , ( int ) ld . ld_symbols + ( int ) N_TXTADDR ( hdr ) ,
1999-02-19 03:21:36 +03:00
( int ) ld . ld_symb_size , strtab ) < 0 )
goto err ;
for ( cp = strtab ; cp < strtab + ld . ld_symb_size ; ) {
if ( strcmp ( cp , " _vfork " ) = = 0 ) {
if ( debug )
fprintf ( stderr , " fixvfork: FOUND _vfork \n " ) ;
strcpy ( cp , " _fork " ) ;
break ;
}
cp + = strlen ( cp ) + 1 ;
}
if ( cp < strtab + ld . ld_symb_size )
/*
* Write entire symbol table back to avoid
* memory alignment bugs in ptrace
*/
2009-06-03 03:49:22 +04:00
if ( tload ( pid , ( int ) ld . ld_symbols + ( int ) N_TXTADDR ( hdr ) ,
1999-02-19 03:21:36 +03:00
( int ) ld . ld_symb_size , strtab ) < 0 )
goto err ;
free ( strtab ) ;
return 0 ;
err :
free ( strtab ) ;
return - 1 ;
}
# endif /* SUNOS4 */