--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- AIX5-specific syscalls stuff. priv_syswrap-aix5.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2006-2006 OpenWorks LLP
+ info@open-works.co.uk
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#ifndef __PRIV_SYSWRAP_AIX5_H
+#define __PRIV_SYSWRAP_AIX5_H
+
+/* requires #include "priv_types_n_macros.h" */
+
+
+/* Allocate a stack for this thread, if it doesn't already have one.
+ They're allocated lazily, and never freed. Returns the initial stack
+ pointer value to use, or 0 if allocation failed. */
+extern Addr ML_(allocstack)(ThreadId tid);
+
+/* Re-read /proc/../map and update everything that depends on it. */
+extern void ML_(aix5_rescan_procmap_after_load_or_unload) ( void );
+
+/* Mess with the given thread's pc/toc so that it is entering
+ pthread_exit() with argument PTHREAD_CANCELED. Returns True if ok,
+ False if it failed to do so, due to not being able to find
+ pthread_exit() by searching symbol tables. */
+extern Bool ML_(aix5_force_thread_into_pthread_exit)( ThreadId );
+
+/* For various reasons, on AIX we may have to just give up if
+ continuing is too difficult (eg, risk of future deadlock). This
+ sets up the process state to exit straight away, but does not
+ actually itself exit. */
+extern
+void ML_(aix5_set_threadstate_for_emergency_exit)(ThreadId tid, HChar* why);
+
+/* Debugging stuff, for making sense of AIX5 threading syscalls. */
+extern void ML_(aix5debugstuff_show_tstate) ( Addr, HChar* who );
+extern void ML_(aix5debugstuff_show_tstate_flags) ( UWord w );
+extern HChar* ML_(aix5debugstuff_pc_to_fnname) ( Addr pc );
+
+
+// Syscalls which can be handled by a common wrapper for
+// both ppc32-aix5 and ppc64-aix5
+
+DECL_TEMPLATE(aix5, sys___libc_sbrk);
+DECL_TEMPLATE(aix5, sys___msleep);
+DECL_TEMPLATE(aix5, sys__clock_gettime);
+DECL_TEMPLATE(aix5, sys__clock_settime);
+DECL_TEMPLATE(aix5, sys__exit);
+DECL_TEMPLATE(aix5, sys__fp_fpscrx_sc);
+DECL_TEMPLATE(aix5, sys__getpgrp);
+DECL_TEMPLATE(aix5, sys__getpid);
+DECL_TEMPLATE(aix5, sys__getppid);
+DECL_TEMPLATE(aix5, sys__getpriority);
+DECL_TEMPLATE(aix5, sys__nsleep);
+DECL_TEMPLATE(aix5, sys__pause);
+DECL_TEMPLATE(aix5, sys__poll);
+DECL_TEMPLATE(aix5, sys__select);
+DECL_TEMPLATE(aix5, sys__sem_wait);
+DECL_TEMPLATE(aix5, sys__setpgid);
+DECL_TEMPLATE(aix5, sys__setsid);
+DECL_TEMPLATE(aix5, sys__sigaction);
+DECL_TEMPLATE(aix5, sys__thread_self);
+DECL_TEMPLATE(aix5, sys__thread_setsched);
+DECL_TEMPLATE(aix5, sys_access);
+DECL_TEMPLATE(aix5, sys_accessx);
+DECL_TEMPLATE(aix5, sys_appgetrlimit);
+DECL_TEMPLATE(aix5, sys_appgetrusage);
+DECL_TEMPLATE(aix5, sys_apprestimer);
+DECL_TEMPLATE(aix5, sys_appsetrlimit);
+DECL_TEMPLATE(aix5, sys_appulimit);
+DECL_TEMPLATE(aix5, sys_bind);
+DECL_TEMPLATE(aix5, sys_chdir);
+DECL_TEMPLATE(aix5, sys_chmod);
+DECL_TEMPLATE(aix5, sys_chown);
+DECL_TEMPLATE(aix5, sys_close);
+DECL_TEMPLATE(aix5, sys_connext);
+DECL_TEMPLATE(aix5, sys_execve);
+DECL_TEMPLATE(aix5, sys_finfo);
+DECL_TEMPLATE(aix5, sys_fstatx);
+DECL_TEMPLATE(aix5, sys_fsync);
+DECL_TEMPLATE(aix5, sys_getdirent);
+DECL_TEMPLATE(aix5, sys_getdirent64);
+DECL_TEMPLATE(aix5, sys_getdomainname);
+DECL_TEMPLATE(aix5, sys_getgidx);
+DECL_TEMPLATE(aix5, sys_getgroups);
+DECL_TEMPLATE(aix5, sys_gethostname);
+DECL_TEMPLATE(aix5, sys_getpriv);
+DECL_TEMPLATE(aix5, sys_getprocs);
+DECL_TEMPLATE(aix5, sys_getrpid);
+DECL_TEMPLATE(aix5, sys_getsockopt);
+DECL_TEMPLATE(aix5, sys_gettimerid);
+DECL_TEMPLATE(aix5, sys_getuidx);
+DECL_TEMPLATE(aix5, sys_incinterval);
+DECL_TEMPLATE(aix5, sys_kfcntl);
+DECL_TEMPLATE(aix5, sys_kfork);
+DECL_TEMPLATE(aix5, sys_kftruncate);
+DECL_TEMPLATE(aix5, sys_kgetsidx);
+DECL_TEMPLATE(aix5, sys_kill);
+DECL_TEMPLATE(aix5, sys_kioctl);
+DECL_TEMPLATE(aix5, sys_klseek);
+DECL_TEMPLATE(aix5, sys_knlist);
+DECL_TEMPLATE(aix5, sys_kpread);
+DECL_TEMPLATE(aix5, sys_kread);
+DECL_TEMPLATE(aix5, sys_kreadv);
+DECL_TEMPLATE(aix5, sys_kthread_ctl);
+DECL_TEMPLATE(aix5, sys_kwaitpid);
+DECL_TEMPLATE(aix5, sys_kwrite);
+DECL_TEMPLATE(aix5, sys_kwritev);
+DECL_TEMPLATE(aix5, sys_listen);
+DECL_TEMPLATE(aix5, sys_loadbind);
+DECL_TEMPLATE(aix5, sys_loadquery);
+DECL_TEMPLATE(aix5, sys_lseek);
+DECL_TEMPLATE(aix5, sys_mkdir);
+DECL_TEMPLATE(aix5, sys_mmap);
+DECL_TEMPLATE(aix5, sys_mprotect);
+DECL_TEMPLATE(aix5, sys_munmap);
+DECL_TEMPLATE(aix5, sys_naccept);
+DECL_TEMPLATE(aix5, sys_ngetpeername);
+DECL_TEMPLATE(aix5, sys_ngetsockname);
+DECL_TEMPLATE(aix5, sys_nrecvfrom);
+DECL_TEMPLATE(aix5, sys_nrecvmsg);
+DECL_TEMPLATE(aix5, sys_nsendmsg);
+DECL_TEMPLATE(aix5, sys_open);
+DECL_TEMPLATE(aix5, sys_pipe);
+DECL_TEMPLATE(aix5, sys_privcheck);
+DECL_TEMPLATE(aix5, sys_readlink);
+DECL_TEMPLATE(aix5, sys_recv);
+DECL_TEMPLATE(aix5, sys_rename);
+DECL_TEMPLATE(aix5, sys_sbrk);
+DECL_TEMPLATE(aix5, sys_sched_get_priority_max);
+DECL_TEMPLATE(aix5, sys_sem_destroy);
+DECL_TEMPLATE(aix5, sys_sem_init);
+DECL_TEMPLATE(aix5, sys_sem_post);
+DECL_TEMPLATE(aix5, sys_send);
+DECL_TEMPLATE(aix5, sys_setgid);
+DECL_TEMPLATE(aix5, sys_setsockopt);
+DECL_TEMPLATE(aix5, sys_setuid);
+DECL_TEMPLATE(aix5, sys_shmat);
+DECL_TEMPLATE(aix5, sys_shmctl);
+DECL_TEMPLATE(aix5, sys_shmdt);
+DECL_TEMPLATE(aix5, sys_shmget);
+DECL_TEMPLATE(aix5, sys_shutdown);
+DECL_TEMPLATE(aix5, sys_sigcleanup);
+DECL_TEMPLATE(aix5, sys_sigprocmask);
+DECL_TEMPLATE(aix5, sys_socket);
+DECL_TEMPLATE(aix5, sys_statfs);
+DECL_TEMPLATE(aix5, sys_statx);
+DECL_TEMPLATE(aix5, sys_symlink);
+DECL_TEMPLATE(aix5, sys_sys_parm);
+DECL_TEMPLATE(aix5, sys_sysconfig);
+DECL_TEMPLATE(aix5, sys_thread_create);
+DECL_TEMPLATE(aix5, sys_thread_init);
+DECL_TEMPLATE(aix5, sys_thread_kill);
+/* thread_setmymask_fast is platform specific */
+DECL_TEMPLATE(aix5, sys_thread_setmystate);
+DECL_TEMPLATE(aix5, sys_thread_setmystate_fast);
+/* thread_setstate is platform specific */
+DECL_TEMPLATE(aix5, sys_thread_terminate_unlock);
+DECL_TEMPLATE(aix5, sys_thread_tsleep);
+DECL_TEMPLATE(aix5, sys_thread_tsleep_event);
+DECL_TEMPLATE(aix5, sys_thread_twakeup);
+DECL_TEMPLATE(aix5, sys_thread_twakeup_event);
+DECL_TEMPLATE(aix5, sys_thread_unlock);
+DECL_TEMPLATE(aix5, sys_thread_waitlock);
+DECL_TEMPLATE(aix5, sys_thread_waitlock_);
+DECL_TEMPLATE(aix5, sys_times);
+DECL_TEMPLATE(aix5, sys_umask);
+DECL_TEMPLATE(aix5, sys_uname);
+DECL_TEMPLATE(aix5, sys_unlink);
+DECL_TEMPLATE(aix5, sys_utimes);
+DECL_TEMPLATE(aix5, sys_vmgetinfo);
+DECL_TEMPLATE(aix5, sys_yield);
+
+
+#endif // __PRIV_SYSWRAP_AIX5_H
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- Support for doing system calls. syscall-ppc32-aix5.S ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2006-2006 OpenWorks LLP
+ info@open-works.co.uk
+
+ Derived from Paul Mackerras' implementation of same for ppc32-linux
+ in syscall-ppc32-linux.S.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics_asm.h"
+#include "libvex_guest_offsets.h"
+
+/* kludge: from include/vki/vki-ppc32-aix5.h */
+#define VKI_SIG_SETMASK 2
+
+
+/*----------------------------------------------------------------*/
+/*
+ Perform a syscall for the client. This will run a syscall
+ with the client's specific per-thread signal mask.
+
+ The structure of this function is such that, if the syscall is
+ interrupted by a signal, we can determine exactly what
+ execution state we were in with respect to the execution of
+ the syscall by examining the value of NIP in the signal
+ handler. This means that we can always do the appropriate
+ thing to precisely emulate the kernel's signal/syscall
+ interactions.
+
+ The syscall number is taken from the argument, even though it
+ should also be in R2 in guest_state. The syscall result is written
+ back to R3 and R4 in the guest state on completion.
+
+ Returns 0 if the syscall was successfully called (even if the
+ syscall itself failed), or a nonzero error code in the lowest
+ 8 bits if one of the sigprocmasks failed (there's no way to
+ determine which one failed). And there's no obvious way to
+ recover from that either, but nevertheless we want to know.
+
+ VG_(fixup_guest_state_after_syscall_interrupted) does the
+ thread state fixup in the case where we were interrupted by a
+ signal.
+
+ Prototype:
+
+ UWord ML_(do_syscall_for_client_WRK)(
+ Int syscallno, // r3
+ void* guest_state, // r4
+ const vki_sigset_t *sysmask, // r5
+ const vki_sigset_t *postmask, // r6
+ Int nsigwords, // r7
+ Int __nr_sigprocmask) // r8
+*/
+ .file "syscall-ppc32-aix6.S"
+
+ .toc
+ .csect .text[PR]
+ .align 2
+ .globl ML_(do_syscall_for_client_WRK)
+ .globl .ML_(do_syscall_for_client_WRK)
+ .csect ML_(do_syscall_for_client_WRK)[DS]
+ML_(do_syscall_for_client_WRK):
+ .long .ML_(do_syscall_for_client_WRK), TOC[tc0], 0
+ .csect .text[PR]
+.ML_(do_syscall_for_client_WRK):
+ /* make a stack frame */
+ stwu 1,-512(1)
+ stw 31,256(1)
+ stw 30,260(1)
+ stw 29,264(1)
+ stw 28,268(1)
+ stw 27,272(1)
+ stw 26,276(1)
+ mflr 26
+ stw 26,280(1)
+ stw 2,284(1)
+ mr 31,3 /* syscall number */
+ mr 30,4 /* guest_state */
+ mr 29,6 /* postmask */
+ mr 28,7 /* nsigwords */
+ mr 27,8 /* __nr_sigprocmask */
+
+Lvg1: /* Even though we can't take a signal until the sigprocmask
+ completes, start the range early. If PC is in the range [1,2),
+ the syscall hasn't been started yet */
+
+ /* set the signal mask for doing the system call */
+ /* set up for sigprocmask(SIG_SETMASK, sysmask, postmask) */
+ mr 2,8
+ li 3,VKI_SIG_SETMASK
+ mr 4,5
+ mr 5,6
+ mr 6,7 /* nsigwords -- needed on AIX ? */
+
+ /* actually do the sigprocmask */
+ .long 0x48000005 /* bl here+4 */
+ mflr 26
+ addi 26,26,16
+ mtlr 26
+ sc
+
+ /* did it fail? (assuming r3 == 0 for success) */
+ cmpwi 0,3,0
+ bne 0,Lvg7
+
+ /* load up syscall args from the threadstate */
+ lwz 3,OFFSET_ppc32_GPR3(30)
+ lwz 4,OFFSET_ppc32_GPR4(30)
+ lwz 5,OFFSET_ppc32_GPR5(30)
+ lwz 6,OFFSET_ppc32_GPR6(30)
+ lwz 7,OFFSET_ppc32_GPR7(30)
+ lwz 8,OFFSET_ppc32_GPR8(30)
+ lwz 9,OFFSET_ppc32_GPR9(30)
+ lwz 10,OFFSET_ppc32_GPR10(30)
+ mr 2,31 /* syscall number */
+
+ .long 0x48000005 /* bl here+4 */
+ mflr 26
+ addi 26,26,16
+ mtlr 26
+
+ /* If PC is in the range [2,2], then the syscall was either
+ just about to start, or was interrupted and the kernel was
+ restarting it. */
+Lvg2: sc /* do the syscall */
+
+ /* In the range [3, 4), the syscall result is in r3/r4, but
+ hasn't been committed to R3/R4. */
+ /* put the result back in the threadstate */
+
+Lvg3: stw 3,OFFSET_ppc32_GPR3(30) /* gst->GPR3 = res */
+ stw 4,OFFSET_ppc32_GPR4(30) /* gst->GPR4 = err */
+
+ /* Block signals again. If PC is in [4,5), then the syscall
+ is complete and we needn't worry about it. */
+ /* set up for sigprocmask(SIG_SETMASK, postmask, NULL) */
+Lvg4: mr 2,27
+ li 3,VKI_SIG_SETMASK
+ mr 4,29
+ li 5,0
+ mr 6,28 /* nsigwords -- needed on AIX ? */
+
+ /* actually do the sigprocmask */
+ .long 0x48000005 /* bl here+4 */
+ mflr 26
+ addi 26,26,16
+ mtlr 26
+ sc
+
+ /* did it fail? (assuming r3 == 0 for success) */
+ cmpwi 0,3,0
+ bne 0,Lvg7
+
+ /* now safe from signals */
+ li 3,0 /* SUCCESS */
+
+ /* pop off stack frame */
+Lvg5: lwz 2,284(1)
+ lwz 26,280(1)
+ mtlr 26
+ lwz 26,276(1)
+ lwz 27,272(1)
+ lwz 28,268(1)
+ lwz 29,264(1)
+ lwz 30,260(1)
+ lwz 31,256(1)
+ addi 1,1,512
+ blr
+
+ /* failure: return 0x8000 | error code */
+Lvg7: mr 3,4
+ ori 3,3,0x8000 /* FAILURE -- ensure return value is nonzero */
+ b Lvg5
+
+/* export the ranges so that
+ VG_(fixup_guest_state_after_syscall_interrupted) can do the
+ right thing */
+
+ .csect .data[RW],3
+ .align 2
+.globl ML_(blksys_setup)
+.globl ML_(blksys_restart)
+.globl ML_(blksys_complete)
+.globl ML_(blksys_committed)
+.globl ML_(blksys_finished)
+ML_(blksys_setup): .long Lvg1
+ML_(blksys_restart): .long Lvg2
+ML_(blksys_complete): .long Lvg3
+ML_(blksys_committed): .long Lvg4
+ML_(blksys_finished): .long Lvg5
+
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- Support for doing system calls. syscall-ppc64-aix5.S ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2006-2006 OpenWorks LLP
+ info@open-works.co.uk
+
+ Derived from Paul Mackerras' implementation of same for ppc32-linux
+ in syscall-ppc32-linux.S.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics_asm.h"
+#include "libvex_guest_offsets.h"
+
+/* kludge: from include/vki/vki-ppc64-aix5.h */
+#define VKI_SIG_SETMASK 2
+
+
+/*----------------------------------------------------------------*/
+/*
+ Perform a syscall for the client. This will run a syscall
+ with the client's specific per-thread signal mask.
+
+ The structure of this function is such that, if the syscall is
+ interrupted by a signal, we can determine exactly what
+ execution state we were in with respect to the execution of
+ the syscall by examining the value of NIP in the signal
+ handler. This means that we can always do the appropriate
+ thing to precisely emulate the kernel's signal/syscall
+ interactions.
+
+ The syscall number is taken from the argument, even though it
+ should also be in R2 in guest_state. The syscall result is written
+ back to R3 and R4 in the guest state on completion.
+
+ Returns 0 if the syscall was successfully called (even if the
+ syscall itself failed), or a nonzero error code in the lowest
+ 8 bits if one of the sigprocmasks failed (there's no way to
+ determine which one failed). And there's no obvious way to
+ recover from that either, but nevertheless we want to know.
+
+ VG_(fixup_guest_state_after_syscall_interrupted) does the
+ thread state fixup in the case where we were interrupted by a
+ signal.
+
+ Prototype:
+
+ UWord ML_(do_syscall_for_client_WRK)(
+ Int syscallno, // r3
+ void* guest_state, // r4
+ const vki_sigset_t *sysmask, // r5
+ const vki_sigset_t *postmask, // r6
+ Int nsigwords, // r7
+ Int __nr_sigprocmask) // r8
+*/
+ .file "syscall-ppc64-aix6.S"
+
+ .toc
+ .csect .text[PR]
+ .align 2
+ .globl ML_(do_syscall_for_client_WRK)
+ .globl .ML_(do_syscall_for_client_WRK)
+ .csect ML_(do_syscall_for_client_WRK)[DS]
+ML_(do_syscall_for_client_WRK):
+ .llong .ML_(do_syscall_for_client_WRK), TOC[tc0], 0
+ .csect .text[PR]
+.ML_(do_syscall_for_client_WRK):
+ /* make a stack frame */
+ stdu 1,-1024(1)
+ std 31,512(1)
+ std 30,520(1)
+ std 29,528(1)
+ std 28,536(1)
+ std 27,544(1)
+ std 26,552(1)
+ mflr 26
+ std 26,560(1)
+ std 2,568(1)
+ mr 31,3 /* syscall number */
+ mr 30,4 /* guest_state */
+ mr 29,6 /* postmask */
+ mr 28,7 /* nsigwords */
+ mr 27,8 /* __nr_sigprocmask */
+
+Lvg1: /* Even though we can't take a signal until the sigprocmask
+ completes, start the range early. If PC is in the range [1,2),
+ the syscall hasn't been started yet */
+
+ /* set the signal mask for doing the system call */
+ /* set up for sigprocmask(SIG_SETMASK, sysmask, postmask) */
+ mr 2,8
+ li 3,VKI_SIG_SETMASK
+ mr 4,5
+ mr 5,6
+ mr 6,7 /* nsigwords -- needed on AIX ? */
+
+ /* actually do the sigprocmask */
+ .long 0x48000005 /* bl here+4 */
+ mflr 26
+ addi 26,26,16
+ mtlr 26
+ sc
+
+ /* did it fail? (assuming r3 == 0 for success) */
+ cmpdi 0,3,0
+ bne 0,Lvg7
+
+ /* load up syscall args from the threadstate */
+ ld 3,OFFSET_ppc64_GPR3(30)
+ ld 4,OFFSET_ppc64_GPR4(30)
+ ld 5,OFFSET_ppc64_GPR5(30)
+ ld 6,OFFSET_ppc64_GPR6(30)
+ ld 7,OFFSET_ppc64_GPR7(30)
+ ld 8,OFFSET_ppc64_GPR8(30)
+ ld 9,OFFSET_ppc64_GPR9(30)
+ ld 10,OFFSET_ppc64_GPR10(30)
+ mr 2,31 /* syscall number */
+
+ .long 0x48000005 /* bl here+4 */
+ mflr 26
+ addi 26,26,16
+ mtlr 26
+
+ /* If PC is in the range [2,2], then the syscall was either
+ just about to start, or was interrupted and the kernel was
+ restarting it. */
+Lvg2: sc /* do the syscall */
+
+ /* In the range [3, 4), the syscall result is in r3/r4, but
+ hasn't been committed to R3/R4. */
+ /* put the result back in the threadstate */
+
+Lvg3: std 3,OFFSET_ppc64_GPR3(30) /* gst->GPR3 = res */
+ std 4,OFFSET_ppc64_GPR4(30) /* gst->GPR4 = err */
+
+ /* Block signals again. If PC is in [4,5), then the syscall
+ is complete and we needn't worry about it. */
+ /* set up for sigprocmask(SIG_SETMASK, postmask, NULL) */
+Lvg4: mr 2,27
+ li 3,VKI_SIG_SETMASK
+ mr 4,29
+ li 5,0
+ mr 6,28 /* nsigwords -- needed on AIX ? */
+
+ /* actually do the sigprocmask */
+ .long 0x48000005 /* bl here+4 */
+ mflr 26
+ addi 26,26,16
+ mtlr 26
+ sc
+
+ /* did it fail? (assuming r3 == 0 for success) */
+ cmpdi 0,3,0
+ bne 0,Lvg7
+
+ /* now safe from signals */
+ li 3,0 /* SUCCESS */
+
+ /* pop off stack frame */
+Lvg5: ld 2,568(1)
+ ld 26,560(1)
+ mtlr 26
+ ld 26,552(1)
+ ld 27,544(1)
+ ld 28,536(1)
+ ld 29,528(1)
+ ld 30,520(1)
+ ld 31,512(1)
+ addi 1,1,1024
+ blr
+
+ /* failure: return 0x8000 | error code */
+Lvg7: mr 3,4
+ ori 3,3,0x8000 /* FAILURE -- ensure return value is nonzero */
+ b Lvg5
+
+/* export the ranges so that
+ VG_(fixup_guest_state_after_syscall_interrupted) can do the
+ right thing */
+
+ .csect .data[RW],3
+ .align 2
+.globl ML_(blksys_setup)
+.globl ML_(blksys_restart)
+.globl ML_(blksys_complete)
+.globl ML_(blksys_committed)
+.globl ML_(blksys_finished)
+ML_(blksys_setup): .llong Lvg1
+ML_(blksys_restart): .llong Lvg2
+ML_(blksys_complete): .llong Lvg3
+ML_(blksys_committed): .llong Lvg4
+ML_(blksys_finished): .llong Lvg5
+
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- AIX5-specific syscalls. syswrap-aix5.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2006-2006 OpenWorks LLP
+ info@open-works.co.uk
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_vkiscnums.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_aspacemgr.h"
+#include "pub_core_debuginfo.h" // VG_(di_notify_*)
+#include "pub_core_transtab.h" // VG_(discard_translations)
+#include "pub_core_clientstate.h"
+#include "pub_core_debuglog.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcfile.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_libcsignal.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_tooliface.h"
+#include "pub_core_options.h"
+#include "pub_core_scheduler.h"
+#include "pub_core_signals.h"
+#include "pub_core_syscall.h"
+#include "pub_core_sigframe.h" // VG_(sigframe_destroy)
+#include "pub_core_syswrap.h"
+#include "pub_core_stacktrace.h"
+
+#include "priv_types_n_macros.h"
+#include "priv_syswrap-aix5.h"
+
+
+
+/* ---------------------------------------------------------------------
+ Misc helpers
+ ------------------------------------------------------------------ */
+
+/* Allocate a stack for this thread, if it doesn't already have one.
+ They're allocated lazily, and never freed. Returns the initial stack
+ pointer value to use, or 0 if allocation failed. */
+Addr ML_(allocstack)(ThreadId tid)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ VgStack* stack;
+ Addr initial_SP;
+
+ /* Either the stack_base and stack_init_SP are both zero (in which
+ case a stack hasn't been allocated) or they are both non-zero,
+ in which case it has. */
+
+ if (tst->os_state.valgrind_stack_base == 0)
+ vg_assert(tst->os_state.valgrind_stack_init_SP == 0);
+
+ if (tst->os_state.valgrind_stack_base != 0)
+ vg_assert(tst->os_state.valgrind_stack_init_SP != 0);
+
+ /* If no stack is present, allocate one. */
+ if (tst->os_state.valgrind_stack_base == 0) {
+ stack = VG_(am_alloc_VgStack)( &initial_SP );
+ if (stack) {
+ /* Leave some space above SP because AIX's ABI stores
+ stuff there. */
+ initial_SP -= 256;
+ vg_assert(initial_SP > (Addr)stack);
+ tst->os_state.valgrind_stack_base = (Addr)stack;
+ tst->os_state.valgrind_stack_init_SP = initial_SP;
+ } else {
+ return 0; /* allocation of stack failed */
+ }
+ }
+
+ if (0)
+ VG_(printf)( "stack for tid %d at %p; init_SP=%p\n",
+ tid,
+ (void*)tst->os_state.valgrind_stack_base,
+ (void*)tst->os_state.valgrind_stack_init_SP );
+
+ return tst->os_state.valgrind_stack_init_SP;
+}
+
+
+/* If we know or believe a module load/unload event has happened, get
+ aspacem to re-read /proc/../map to update its picture of what text
+ and data segments are present. This also notifies all the usual
+ parties that need to know about address space changes. */
+
+void ML_(aix5_rescan_procmap_after_load_or_unload) ( void )
+{
+ AixCodeSegChange* changes;
+ Int changes_size, changes_used, i;
+
+ /* Find out how many AixCodeSegChange records we will need, and
+ acquire them. */
+ changes_size = VG_(am_aix5_reread_procmap_howmany_directives)();
+ changes = VG_(arena_malloc)(VG_AR_CORE,
+ changes_size * sizeof(AixCodeSegChange));
+ vg_assert(changes);
+
+ /* Now re-read /proc/<pid>/map and acquire a change set */
+ VG_(am_aix5_reread_procmap)( changes, &changes_used );
+ vg_assert(changes_used >= 0 && changes_used <= changes_size);
+
+ /* And notify all parties of the changes. */
+ for (i = 0; i < changes_used; i++) {
+ VG_(di_aix5_notify_segchange)(
+ changes[i].code_start,
+ changes[i].code_len,
+ changes[i].data_start,
+ changes[i].data_len,
+ changes[i].file_name,
+ changes[i].mem_name,
+ changes[i].is_mainexe,
+ changes[i].acquire
+ );
+
+ if (changes[i].acquire) {
+ VG_TRACK( new_mem_mmap,
+ changes[i].code_start, changes[i].code_len,
+ /*r*/True, /*w*/False, /*x*/True );
+ VG_TRACK( new_mem_mmap,
+ changes[i].data_start, changes[i].data_len,
+ /*r*/True, /*w*/True, /*x*/False );
+ } else {
+ VG_TRACK( die_mem_munmap,
+ changes[i].code_start, changes[i].code_len );
+ VG_TRACK( die_mem_munmap,
+ changes[i].data_start, changes[i].data_len );
+ VG_(discard_translations)(
+ changes[i].code_start, changes[i].code_len,
+ "POST(sys___loadx/sys__kload)(code)" );
+ VG_(discard_translations)(
+ changes[i].data_start, changes[i].data_len,
+ "POST(sys___loadx/sys__kload)(data)" );
+ }
+ }
+
+ VG_(arena_free)(VG_AR_CORE, changes);
+}
+
+
+/* Mess with the given thread's pc/toc so that it is entering
+ pthread_exit() with argument PTHREAD_CANCELED. Returns True if ok,
+ False if it failed to do so, due to not being able to find
+ pthread_exit() by searching symbol tables. */
+Bool ML_(aix5_force_thread_into_pthread_exit)( ThreadId tid )
+{
+ Addr ent = 0, toc = 0;
+ Bool found;
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ found = VG_(lookup_symbol_SLOW)("libpthread*.a(*.o)", "pthread_exit",
+ &ent, &toc);
+ if (found) {
+ if (0)
+ VG_(printf)("THREAD CANCELED, new cia,toc = %p,%p\n", ent, toc);
+ tst->arch.vex.guest_CIA = ent;
+ tst->arch.vex.guest_GPR2 = toc;
+ tst->arch.vex.guest_GPR3 = (Word)(-1); /* == PTHREAD_CANCELED */
+ /* If the thread is blocked in a syscall, we better bop it on
+ the head with SIGVGKILL in order to get it out of said
+ syscall. */
+ if (tst->status == VgTs_WaitSys) {
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(sending SIGVGKILL to tid %d)", (Int)tid);
+ VG_(get_thread_out_of_syscall)( tid );
+ }
+ return True; /* ok */
+ } else {
+ // urk. Now we're hosed. Let the caller figure out what to do.
+ return False; /* failed */
+ }
+}
+
+
+/* For various reasons, on AIX we may have to just give up if
+ continuing is too difficult (eg, risk of future deadlock). This
+ sets up the process state to exit straight away, but does not
+ actually itself exit. */
+void ML_(aix5_set_threadstate_for_emergency_exit)(ThreadId tid, HChar* why)
+{
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ /* Set the thread's status to be exiting and taking out the
+ entire process, then claim that the syscall succeeded. */
+ tst->exitreason = VgSrc_ExitProcess;
+ tst->os_state.exitcode = 1;
+ if (!VG_(clo_xml)) {
+ VG_(message)(Vg_UserMsg,
+ "WARNING: AIX: %s", why);
+ VG_(message)(Vg_UserMsg,
+ "WARNING: (too difficult to continue past this point).");
+ VG_(get_and_pp_StackTrace)(tid, 10);
+ }
+}
+
+
+/* Update aspacem etc on conclusion of a successful sbrk/__libc_sbrk
+ call. 2006-08-24: this was not completed because I don't
+ understand what sbrk/__libc_sbrk are doing. */
+
+static void handle_sbrk ( Word delta )
+{
+ return;
+ /*NOTREACHED*/
+ if (delta > 0) {
+ /* Map in VG_(brk_limit) for delta */
+ /* using notify_mmap ? */
+ VG_(brk_limit) += delta;
+ }
+ if (delta < 0) {
+ Addr tmp = VG_(brk_limit);
+ VG_(brk_limit) += delta;
+ /* Can't move below original starting point */
+ if (VG_(brk_limit) < VG_(brk_base))
+ VG_(brk_limit) = VG_(brk_base);
+ if (VG_(brk_limit) < tmp)
+ /* Unmap VG_(brk_limit) for tmp - VG_(brk_limit) */
+ /* using notify_munmap ? */
+ ;
+ }
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("new brk: 0x%010llx-0x%010llx (size %lld)\n",
+ (ULong)VG_(brk_base),
+ (ULong)VG_(brk_limit),
+ (ULong)VG_(brk_limit) - (ULong)VG_(brk_base));
+}
+
+
+/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
+#include <sys/thread.h>
+#include <sys/poll.h>
+#include <sys/times.h>
+#include <sys/shm.h>
+#include <semaphore.h>
+#include <sys/statfs.h>
+/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
+
+HChar* ML_(aix5debugstuff_pc_to_fnname) ( Addr pc )
+{
+ Bool ok;
+ static HChar name[100];
+ ok = VG_(get_fnname_w_offset)(pc, name, 100);
+ if (!ok) VG_(strcpy)(name, "???");
+ return &name[0];
+}
+
+static void aix5debugstuff_show_sigset ( vki_sigset_t* set )
+{
+ Int i;
+ UChar* p = (UChar*)set;
+ for (i = 0; i < sizeof(vki_sigset_t); i++)
+ VG_(printf)("%02x", (Int)p[i]);
+}
+
+static HChar* aix5debugstuff_name_of_tstate_flag ( UWord flag )
+{
+ Int i, nset;
+ nset = 0;
+ for (i = 0; i < 8*sizeof(UWord); i++)
+ if (flag & (1U << i))
+ nset++;
+ vg_assert(nset == 1);
+ switch (flag) {
+ case TSTATE_LOCAL: return "LOCAL";
+ case TSTATE_CANCEL_DEFER: return "CANCEL_DEFER";
+ case TSTATE_CANCEL_DISABLE: return "CANCEL_DISABLE";
+ case TSTATE_CANCEL_PENDING: return "CANCEL_PENDING";
+ case TSTATE_CANCEL_CHKPT: return "CANCEL_CHKPT";
+ case TSTATE_INTR: return "INTR";
+ case TSTATE_EXEMPT: return "EXEMPT";
+#ifdef TSTATE_PROFILING_OFF
+ case TSTATE_PROFILING_OFF: return "PROFILING_OFF";
+#endif
+ case TSTATE_SUSPEND: return "SUSPEND";
+ case TSTATE_CONT: return "CONT";
+#ifdef TSTATE_CREDS
+ case TSTATE_CREDS: return "CREDS";
+#endif
+#ifdef TSTATE_PROCHANDLERS
+ case TSTATE_PROCHANDLERS: return "PROCHANDLERS";
+#endif
+ case TSTATE_ADVH: return "ADVH";
+ case TSTATE_SYNCH: return "SYNCH";
+ case TSTATE_USCHED: return "USCHED";
+ case TSTATE_DEFAULT_SCHED: return "DEFAULT_SCHED";
+#ifdef TSTATE_INHERIT_SCHED
+ case TSTATE_INHERIT_SCHED: return "INHERIT_SCHED";
+#endif
+#ifdef TSTATE_LOCAL_INIT
+ case TSTATE_LOCAL_INIT: return "LOCAL_INIT";
+#endif
+#ifdef TSTATE_LOCAL_TERM
+ case TSTATE_LOCAL_TERM: return "LOCAL_TERM";
+#endif
+#ifdef TSTATE_LOCAL_MCHANGE
+ case TSTATE_LOCAL_MCHANGE: return "LOCAL_MCHANGE";
+#endif
+ case TSTATE_CHANGE_ALL: return "CHANGE_ALL";
+#ifdef TSTATE_CHANGE_PTID
+ case TSTATE_CHANGE_PTID: return "CHANGE_PTID";
+#endif
+#ifdef TSTATE_CHANGE_PROFILE
+ case TSTATE_CHANGE_PROFILE: return "CHANGE_PROFILE";
+#endif
+#ifdef TSTATE_CHANGE_SSTACK
+ case TSTATE_CHANGE_SSTACK: return "CHANGE_SSTACK";
+#endif
+ case TSTATE_CHANGE_ERRNOP: return "CHANGE_ERRNOP";
+ case TSTATE_CHANGE_SIGMASK: return "CHANGE_SIGMASK";
+ case TSTATE_CHANGE_PSIG: return "CHANGE_PSIG";
+ case TSTATE_CHANGE_SCHED: return "CHANGE_SCHED";
+ case TSTATE_CHANGE_FLAGS: return "CHANGE_FLAGS";
+ case TSTATE_CHANGE_USERDATA: return "CHANGE_USERDATA";
+ default: return "???";
+ }
+}
+
+void ML_(aix5debugstuff_show_tstate_flags) ( UWord w )
+{
+ const Int step = 5;
+ Int i, j;
+ UWord m;
+ j = 0;
+ for (i = 0; i < 8*sizeof(UWord); i++) {
+ m = 1U << i;
+ if ((w & m) == 0)
+ continue;
+ if ((j % step) == 0)
+ VG_(printf)(" ");
+ VG_(printf)("%s ", aix5debugstuff_name_of_tstate_flag(w & m));
+ if ((j % step) == step-1 && j > 0)
+ VG_(printf)("\n");
+ j++;
+ }
+ if (((j-1) % step) != step-1 && j > 0)
+ VG_(printf)("\n");
+}
+
+void ML_(aix5debugstuff_show_tstate) ( Addr tsA, HChar* who )
+{
+ Int i;
+ const Int step = sizeof(void*)==8 ? 3 : 5;
+ struct tstate* ts = (struct tstate*)tsA;
+ VG_(printf)("\n{ ========= %s =========\n", who);
+ for (i = 0; i < _NGPRS; i++) {
+ if ((i % step) == 0)
+ VG_(printf)(" [%2d] ", i);
+ if (sizeof(void*)==8)
+ VG_(printf)("%016llx ", (ULong)ts->mst.gpr[i]);
+ else
+ VG_(printf)("%08llx ", (ULong)ts->mst.gpr[i]);
+ if ((i == _NGPRS-1) || ((i % step) == step-1 && i > 0))
+ VG_(printf)("\n");
+ }
+ VG_(printf)(" [iar] %p %s\n", ts->mst.iar,
+ ML_(aix5debugstuff_pc_to_fnname)(ts->mst.iar));
+
+ VG_(printf)(" errnop_addr %p\n", ts->errnop_addr);
+
+ VG_(printf)(" sigmask ");
+ aix5debugstuff_show_sigset( (vki_sigset_t*)&ts->sigmask );
+ VG_(printf)("\n");
+
+ VG_(printf)(" psig ");
+ aix5debugstuff_show_sigset( (vki_sigset_t*)&ts->psig );
+ VG_(printf)("\n");
+
+ VG_(printf)(" policy %d\n", ts->policy);
+ VG_(printf)(" priority %d\n", ts->priority);
+ VG_(printf)(" flags 0x%x\n", ts->flags);
+ ML_(aix5debugstuff_show_tstate_flags)( (UWord)ts->flags );
+ VG_(printf)(" flagmask 0x%x\n", ts->flagmask);
+ VG_(printf)(" userdata %p\n", (void*)ts->userdata);
+ VG_(printf)(" fpinfo %d\n", ts->fpinfo);
+ VG_(printf)(" fpscrx %d\n", ts->fpscrx);
+ VG_(printf)(" sigaltstack ??\n");
+ VG_(printf)(" thread_control_p 0x%llx\n", (ULong)ts->thread_control_p);
+// AIX 5.1 does not seem to have these members
+// VG_(printf)(" prbase %p\n", (void*)ts->prbase);
+// VG_(printf)(" credp %p\n", (void*)ts->credp);
+// VG_(printf)(" ptid %d\n", (int)ts->ptid);
+// VG_(printf)(" tct_clock %d\n", (int)ts->tct_clock);
+ UInt* p = (UInt*)tsA;
+ for (i = 0; i < sizeof(struct tstate)/sizeof(UInt); i++) {
+ HChar* s = ML_(aix5debugstuff_pc_to_fnname)( (Addr)p[i] );
+ if (0==VG_(strcmp)(s,"???"))
+ continue;
+ VG_(printf)(" [%d] %p %s\n", i, p[i], s);
+ }
+ VG_(printf)("}\n");
+}
+
+/* ---------------------------------------------------------------------
+ PRE/POST wrappers for arch-generic, AIX5-specific syscalls. Note:
+ in fact AIX5 doesn't share any wrappers with Linux since it's
+ difficult to get syswrap-generic.c to compile on AIX. Hence in
+ fact this file also serves the role of syswrap-generic.c for AIX.
+ This could probably be improved at the cost of some extra effort.
+ ------------------------------------------------------------------ */
+
+// Nb: See the comment above the generic PRE/POST wrappers in
+// m_syswrap/syswrap-generic.c for notes about how they work.
+
+#define PRE(name) DEFN_PRE_TEMPLATE(aix5, name)
+#define POST(name) DEFN_POST_TEMPLATE(aix5, name)
+
+
+// How to make __libc_sbrk appear to fail, from libc's point of view:
+// SysRes r;
+// r.res = -1; /* significant to libc */
+// r.err = VKI_ENOMEM; /* not significant to libc */
+// SET_STATUS_from_SysRes( r );
+// return;
+
+PRE(sys___libc_sbrk)
+{
+ PRINT("__libc_sbrk (BOGUS HANDLER)( %p )",ARG1);
+ PRE_REG_READ1(long, "__libc_sbrk", long, arg1);
+ /* After a zero sbrk, disallow aspacem from doing sbrk, since libc
+ might rely on the value returned by this syscall. */
+ /* 1 Oct 06: not currently used (aspacemgr-aix5.c ignores it) */
+ VG_(am_aix5_sbrk_allowed) = toBool(ARG1 != 0);
+ /* Disallow libc from moving the brk backwards as that might trash
+ SkPreAlloc sections acquired by aspacem from previous uses of
+ sbrk. */
+ if (ARG1 < 0)
+ ARG1 = 0;
+ /* Do this as a sync syscall, so the sbrk_allowed flag gets turned
+ back on ASAP. Typically libc does sbrk(0) and then sbrk(x > 0)
+ in quick succession. Although surely it should hold some kind
+ of lock at that point, else it cannot safely use the result from
+ the first sbrk call to influence the second one? */
+ *flags &= ~SfMayBlock;
+}
+POST(sys___libc_sbrk)
+{
+ vg_assert(SUCCESS);
+ handle_sbrk(ARG1);
+}
+
+/* __loadx is handled in the platform-specific files. */
+
+PRE(sys___msleep)
+{
+ PRINT("__msleep (BOGUS HANDLER) ( %p )", ARG1);
+ PRE_REG_READ1(long, "msleep", void*, arg1);
+}
+
+/* __unload is handled in the platform-specific files. */
+
+PRE(sys__clock_gettime)
+{
+ PRINT("_clock_gettime (UNDOCUMENTED) ( %d, %p )", ARG1, ARG2);
+ PRE_REG_READ2(int, "_clock_gettime", int, arg1, int, arg2);
+}
+
+PRE(sys__clock_settime)
+{
+ PRINT("_clock_settime (UNDOCUMENTED) ( %d, %p )", ARG1, ARG2);
+ PRE_REG_READ2(int, "_clock_settime", int, arg1, int, arg2);
+}
+
+PRE(sys__exit)
+{
+ ThreadState* tst;
+ /* simple; just make this thread exit */
+ PRINT("_exit( %d )", ARG1);
+ PRE_REG_READ1(void, "exit", int, exitcode);
+
+ tst = VG_(get_ThreadState)(tid);
+ /* Set the thread's status to be exiting and taking out the entire
+ process, then claim that the syscall succeeded. */
+ tst->exitreason = VgSrc_ExitProcess;
+ tst->os_state.exitcode = ARG1;
+ SET_STATUS_Success(0);
+}
+
+PRE(sys__fp_fpscrx_sc)
+{
+ PRINT("_fp_fpscrx_sc (BOGUS HANDLER)");
+}
+
+PRE(sys__getpgrp)
+{
+ PRINT("_getpgrp (BOGUS HANDLER)");
+}
+
+PRE(sys__getpid)
+{
+ PRINT("_getpid ( )");
+}
+
+PRE(sys__getppid)
+{
+ PRINT("_getppid ( )");
+}
+
+PRE(sys__getpriority)
+{
+ PRINT("_getpriority (BOGUS HANDLER)");
+}
+
+PRE(sys__nsleep)
+{
+ *flags |= SfMayBlock;
+ PRINT("_nsleep( %p, %p )", ARG1, ARG2);
+ PRE_REG_READ2(void, "_nsleep", struct timestruc_t*, arg1,
+ struct timestruc_t*, arg2);
+ /* In 64-bit mode, struct ends in 4 padding bytes. Hence: */
+ if (ARG1)
+ PRE_MEM_READ("_nsleep(arg1)",
+ ARG1,
+ sizeof(void*)==4 ? sizeof(struct timestruc_t)
+ : sizeof(struct timestruc_t)-4 );
+ if (ARG2)
+ PRE_MEM_WRITE("_nsleep(arg2)", ARG2, sizeof(struct timestruc_t));
+}
+POST(sys__nsleep)
+{
+ if (ARG2)
+ POST_MEM_WRITE(ARG2, sizeof(struct timestruc_t));
+}
+
+PRE(sys__pause)
+{
+ *flags |= SfMayBlock;
+ PRINT("_pause ( )");
+ PRE_REG_READ0(long, "pause");
+}
+
+PRE(sys__poll)
+{
+ UInt i;
+ struct pollfd* ufds = (struct pollfd *)ARG1;
+ *flags |= SfMayBlock;
+ PRINT("_poll ( %p, %ld, %ld )\n", ARG1,ARG2,ARG3);
+ PRE_REG_READ3(long, "_poll",
+ struct pollfd *, ufds, unsigned int, nfds, long, timeout);
+
+ for (i = 0; i < ARG2; i++) {
+ PRE_MEM_READ( "poll(ufds.fd)",
+ (Addr)(&ufds[i].fd), sizeof(ufds[i].fd) );
+ PRE_MEM_READ( "poll(ufds.events)",
+ (Addr)(&ufds[i].events), sizeof(ufds[i].events) );
+ PRE_MEM_WRITE( "poll(ufds.reventss)",
+ (Addr)(&ufds[i].revents), sizeof(ufds[i].revents) );
+ }
+}
+POST(sys__poll)
+{
+ if (RES > 0) {
+ UInt i;
+ struct pollfd* ufds = (struct pollfd *)ARG1;
+ for (i = 0; i < ARG2; i++)
+ POST_MEM_WRITE( (Addr)(&ufds[i].revents), sizeof(ufds[i].revents) );
+ }
+}
+
+PRE(sys__select)
+{
+ UInt nfds, nmqids;
+ *flags |= SfMayBlock;
+ /* XXX: copy of generic; I don't know if this is right or not. */
+ PRINT("_select ( %ld, %p, %p, %p, %p )", ARG1,ARG2,ARG3,ARG4,ARG5);
+ PRE_REG_READ5(long, "_select",
+ int, n, struct sellist *, readfds,
+ struct sellist *, writefds,
+ struct sellist *, exceptfds,
+ struct timeval *, timeout);
+ nfds = ((UInt)ARG1) & 0xFFFF;
+ nmqids = (((UInt)ARG1) >> 16) & 0xFFFF;
+
+ // XXX: this possibly understates how much memory is read.
+ if (ARG2 != 0)
+ PRE_MEM_READ( "select(readfds)",
+ ARG2, nfds/8 /* __FD_SETSIZE/8 */ );
+ if (ARG3 != 0)
+ PRE_MEM_READ( "select(writefds)",
+ ARG3, nfds/8 /* __FD_SETSIZE/8 */ );
+ if (ARG4 != 0)
+ PRE_MEM_READ( "select(exceptfds)",
+ ARG4, nfds/8 /* __FD_SETSIZE/8 */ );
+ if (ARG5 != 0)
+ PRE_MEM_READ( "select(timeout)", ARG5,
+ /* in 64-bit mode, struct timeval has 4 bytes of
+ padding at the end, which tend to not be
+ initialised. */
+ sizeof(void*)==4 ? sizeof(struct timeval)
+ : sizeof(struct timeval)-4
+ );
+}
+
+PRE(sys__sem_wait)
+{
+ *flags |= SfMayBlock;
+ PRINT("_sem_wait (BOGUS HANDLER) ( %p, %p, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(long, "_sem_wait", void*, arg1, void*, arg2, long, arg3 );
+ /* Not sure what the two pointer args are. Hence no proper handler.*/
+}
+
+PRE(sys__setpgid)
+{
+ PRINT("setpgid ( %ld, %ld )", ARG1, ARG2);
+ PRE_REG_READ2(int, "setpgid", int, pid, int, pgid);
+}
+
+PRE(sys__setsid)
+{
+ PRINT("setsid ( )");
+}
+
+PRE(sys__sigaction) /* COL, more or less */
+{
+ PRINT("_sigaction ( %ld, %p, %p )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(long, "_sigaction",
+ int, signum, const struct sigaction *, act,
+ struct sigaction *, oldact);
+
+ if (ARG2 != 0) {
+ struct vki_sigaction *sa = (struct vki_sigaction *)ARG2;
+ PRE_MEM_READ( "_sigaction(act->sa_handler)",
+ (Addr)&sa->ksa_handler, sizeof(sa->ksa_handler));
+ PRE_MEM_READ( "_sigaction(act->sa_mask)",
+ (Addr)&sa->sa_mask, sizeof(sa->sa_mask));
+ PRE_MEM_READ( "rt_sigaction(act->sa_flags)",
+ (Addr)&sa->sa_flags, sizeof(sa->sa_flags));
+ }
+ if (ARG3 != 0)
+ PRE_MEM_WRITE( "rt_sigaction(oldact)", ARG3, sizeof(struct vki_sigaction));
+
+ SET_STATUS_from_SysRes(
+ VG_(do_sys_sigaction)(ARG1, (const struct vki_sigaction *)ARG2,
+ (struct vki_sigaction *)ARG3)
+ );
+}
+POST(sys__sigaction)
+{
+ vg_assert(SUCCESS);
+ if (RES == 0 && ARG3 != 0)
+ POST_MEM_WRITE( ARG3, sizeof(struct vki_sigaction));
+}
+
+PRE(sys__thread_self)
+{
+ PRINT("_thread_self ( )");
+}
+
+PRE(sys__thread_setsched)
+{
+ PRINT("_thread_setsched ( %ld, %ld, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(long, "_thread_setsched", long, arg1, long, arg2, long, arg3);
+}
+
+PRE(sys_access)
+{
+ PRINT("access ( %p(%s), %ld )", ARG1,ARG1, ARG2);
+ PRE_REG_READ2(int, "access", char*, pathname, int, mode);
+ PRE_MEM_RASCIIZ( "access(pathname)", ARG1 );
+}
+
+PRE(sys_accessx)
+{
+ PRINT("accessx ( %p(%s), %ld, %ld )", ARG1,ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "accessx", char*, pathname, int, mode, int, who);
+ PRE_MEM_RASCIIZ( "accessx(pathname)", ARG1 );
+}
+
+PRE(sys_appgetrlimit)
+{
+ /* Note: assumes kernel struct == libc struct */
+ PRINT("appgetrlimit ( %ld, %p )", ARG1, ARG2);
+ PRE_REG_READ2(int, "appgetrlimit", int, arg1, struct rlimit*, arg2);
+ PRE_MEM_WRITE( "appgetrlimit(buf)", ARG2, sizeof(struct rlimit) );
+}
+POST(sys_appgetrlimit)
+{
+ POST_MEM_WRITE( ARG2, sizeof(struct rlimit) );
+}
+
+PRE(sys_appgetrusage)
+{
+ /* Note: assumes kernel struct == libc struct */
+ PRINT("appgetrusage ( %d, %p )", ARG1, ARG2);
+ PRE_REG_READ2(int, "appgetrusage", int, arg1, struct rusage*, arg2);
+ PRE_MEM_WRITE( "appgetrusage(buf)", ARG2, sizeof(struct rusage) );
+}
+POST(sys_appgetrusage)
+{
+ POST_MEM_WRITE( ARG2, sizeof(struct rusage) );
+}
+
+PRE(sys_apprestimer)
+{
+ PRINT("apprestimer (BOGUS HANDLER)");
+}
+
+PRE(sys_appsetrlimit)
+{
+ PRINT("appsetrlimit (BOGUS HANDLER)");
+}
+
+PRE(sys_appulimit)
+{
+ PRINT("appulimit ( %ld, %ld )", ARG1, ARG2);
+ PRE_REG_READ2(long, "appulimit", long, arg1, long, arg2);
+}
+
+PRE(sys_bind)
+{
+ PRINT("bind ( %ld, %p, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "bind", int, socket,
+ void*, address, int, addresslen);
+ /* Hmm. This isn't really right - see pre_mem_read_sockaddr. */
+ PRE_MEM_READ( "bind(address)", ARG2, ARG3 );
+}
+
+PRE(sys_chdir)
+{
+ PRINT("chdir ( %p(%s) )", ARG1,ARG1);
+ PRE_REG_READ1(long, "chdir", const char *, path);
+ PRE_MEM_RASCIIZ( "chdir(path)", ARG1 );
+}
+
+PRE(sys_chmod)
+{
+ PRINT("chmod ( %p(%s), 0x%lx )", ARG1,ARG1, ARG2 );
+ PRE_REG_READ2(int, "chmod", char*, path, int, mode);
+ PRE_MEM_RASCIIZ( "chmod(path)", ARG1 );
+}
+
+PRE(sys_chown)
+{
+ PRINT("chown ( %p(%s), %ld, %ld )", ARG1,ARG1, ARG2, ARG3 );
+ PRE_REG_READ3(int, "chown", char*, path, int, owner, int, group);
+ PRE_MEM_RASCIIZ( "chown(path)", ARG1 );
+}
+
+PRE(sys_close)
+{
+ PRINT("close ( %ld )", ARG1);
+ PRE_REG_READ1(void, "close", UInt, fd);
+}
+
+PRE(sys_connext)
+{
+ /* apparently undocumented. I don't know what it does. */
+ /* Although /usr/include/net/proto_uipc.h does mention it.
+ Args are apparently (int, caddr_t, int). I suspect the
+ first arg is a fd and the third a flags value. */
+ PRINT("connext (UNDOCUMENTED)( %ld, %p, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "connext", int, arg1, caddr_t*, arg2, int, arg3);
+}
+
+//--- PRE(sys_execve) ---//
+// Pre_read a char** argument.
+static void pre_argv_envp(Addr a, ThreadId tid, Char* s1, Char* s2)
+{
+ while (True) {
+ Addr a_deref;
+ Addr* a_p = (Addr*)a;
+ PRE_MEM_READ( s1, (Addr)a_p, sizeof(Addr) );
+ a_deref = *a_p;
+ if (0 == a_deref)
+ break;
+ PRE_MEM_RASCIIZ( s2, a_deref );
+ a += sizeof(char*);
+ }
+}
+static SysRes simple_pre_exec_check(const HChar* exe_name)
+{
+ Int fd, ret;
+ SysRes res;
+
+ // Check it's readable
+ res = VG_(open)(exe_name, VKI_O_RDONLY, 0);
+ if (res.isError) {
+ return res;
+ }
+ fd = res.res;
+ VG_(close)(fd);
+
+ // Check we have execute permissions
+ ret = VG_(check_executable)((HChar*)exe_name);
+
+ if (0 != ret) {
+ return VG_(mk_SysRes_Error)(ret);
+ }
+ return VG_(mk_SysRes_Success)(0);
+}
+PRE(sys_execve)
+{
+ Char* path = NULL; /* path to executable */
+ Char** envp = NULL;
+ Char** argv = NULL;
+ Char** arg2copy;
+ Char* launcher_basename = NULL;
+ ThreadState* tst;
+ Int i, j, tot_args;
+ SysRes res;
+
+ PRINT("sys_execve ( %p(%s), %p, %p )", ARG1, ARG1, ARG2, ARG3);
+ PRE_REG_READ3(vki_off_t, "execve",
+ char *, filename, char **, argv, char **, envp);
+ PRE_MEM_RASCIIZ( "execve(filename)", ARG1 );
+ if (ARG2 != 0)
+ pre_argv_envp( ARG2, tid, "execve(argv)", "execve(argv[i])" );
+ if (ARG3 != 0)
+ pre_argv_envp( ARG3, tid, "execve(envp)", "execve(envp[i])" );
+
+ vg_assert(VG_(is_valid_tid)(tid));
+ tst = VG_(get_ThreadState)(tid);
+
+ /* Erk. If the exec fails, then the following will have made a
+ mess of things which makes it hard for us to continue. The
+ right thing to do is piece everything together again in
+ POST(execve), but that's close to impossible. Instead, we make
+ an effort to check that the execve will work before actually
+ doing it. */
+
+ /* Check that the name at least begins in client-accessible storage. */
+ /* XXX: causes execve to fail for non-memcheck tools, presumably
+ because ARG1 is thought to not to being in client-accessible
+ storage due to inadequate address space tracking. May or may
+ not be due to non-tracking of brk. */
+ //if (!VG_(am_is_valid_for_client)( ARG1, 1, VKI_PROT_READ )) {
+ // SET_STATUS_Failure( VKI_EFAULT );
+ // return;
+ //}
+
+ // Do the important checks: it is a file, is executable, permissions are
+ // ok, etc.
+ res = simple_pre_exec_check((const Char*)ARG1);
+ if (res.isError) {
+ SET_STATUS_Failure( res.err );
+ return;
+ }
+
+ /* If we're tracing the child, and the launcher name looks bogus
+ (possibly because launcher.c couldn't figure it out, see
+ comments therein) then we have no option but to fail. */
+ if (VG_(clo_trace_children)
+ && (VG_(name_of_launcher) == NULL
+ || VG_(name_of_launcher)[0] != '/')) {
+ SET_STATUS_Failure( VKI_ECHILD ); /* "No child processes" */
+ return;
+ }
+
+ /* After this point, we can't recover if the execve fails. */
+ VG_(debugLog)(1, "syswrap", "Exec of %s\n", (Char*)ARG1);
+
+ /* Resistance is futile. Nuke all other threads. POSIX mandates
+ this. (Really, nuke them all, since the new process will make
+ its own new thread.) */
+ VG_(nuke_all_threads_except)( tid, VgSrc_ExitThread );
+ VG_(reap_threads)(tid);
+
+ // Set up the child's exe path.
+ //
+ if (VG_(clo_trace_children)) {
+
+ // We want to exec the launcher. Get its pre-remembered path.
+ path = VG_(name_of_launcher);
+ // VG_(name_of_launcher) should have been acquired by m_main at
+ // startup.
+ vg_assert(path);
+
+ launcher_basename = VG_(strrchr)(path, '/');
+ if (launcher_basename == NULL || launcher_basename[1] == 0) {
+ launcher_basename = path; // hmm, tres dubious
+ } else {
+ launcher_basename++;
+ }
+
+ } else {
+ path = (Char*)ARG1;
+ }
+
+ // Set up the child's environment.
+ //
+ // Remove the valgrind-specific stuff from the environment so the
+ // child doesn't get vgpreload_core.so, vgpreload_<tool>.so, etc.
+ // This is done unconditionally, since if we are tracing the child,
+ // the child valgrind will set up the appropriate client environment.
+ // Nb: we make a copy of the environment before trying to mangle it
+ // as it might be in read-only memory (this was bug #101881).
+ //
+ // Then, if tracing the child, set VALGRIND_LIB for it.
+ //
+ if (ARG3 == 0) {
+ envp = NULL;
+ } else {
+ envp = VG_(env_clone)( (Char**)ARG3 );
+ if (envp == NULL) goto hosed;
+ VG_(env_remove_valgrind_env_stuff)( envp );
+ }
+
+ if (VG_(clo_trace_children)) {
+ // Set VALGRIND_LIB in ARG3 (the environment)
+ VG_(env_setenv)( &envp, VALGRIND_LIB, VG_(libdir));
+ }
+
+ // Set up the child's args. If not tracing it, they are
+ // simply ARG2. Otherwise, they are
+ //
+ // [launcher_basename] ++ VG_(args_for_valgrind) ++ [ARG1] ++ ARG2[1..]
+ //
+ // except that the first VG_(args_for_valgrind_noexecpass) args
+ // are omitted.
+ //
+ if (!VG_(clo_trace_children)) {
+ argv = (Char**)ARG2;
+ } else {
+ vg_assert( VG_(args_for_valgrind_noexecpass) >= 0 );
+ vg_assert( VG_(args_for_valgrind_noexecpass)
+ <= VG_(args_for_valgrind).used );
+ /* how many args in total will there be? */
+ // launcher basename
+ tot_args = 1;
+ // V's args
+ tot_args += VG_(args_for_valgrind).used;
+ tot_args -= VG_(args_for_valgrind_noexecpass);
+ // name of client exe
+ tot_args++;
+ // args for client exe, skipping [0]
+ arg2copy = (Char**)ARG2;
+ if (arg2copy && arg2copy[0]) {
+ for (i = 1; arg2copy[i]; i++)
+ tot_args++;
+ }
+ // allocate
+ argv = VG_(malloc)( (tot_args+1) * sizeof(HChar*) );
+ if (argv == 0) goto hosed;
+ // copy
+ j = 0;
+ argv[j++] = launcher_basename;
+ for (i = 0; i < VG_(args_for_valgrind).used; i++) {
+ if (i < VG_(args_for_valgrind_noexecpass))
+ continue;
+ argv[j++] = VG_(args_for_valgrind).strs[i];
+ }
+ argv[j++] = (Char*)ARG1;
+ if (arg2copy && arg2copy[0])
+ for (i = 1; arg2copy[i]; i++)
+ argv[j++] = arg2copy[i];
+ argv[j++] = NULL;
+ // check
+ vg_assert(j == tot_args+1);
+ }
+
+ /* restore the DATA rlimit for the child */
+ VG_(setrlimit)(VKI_RLIMIT_DATA, &VG_(client_rlimit_data));
+
+ /*
+ Set the signal state up for exec.
+
+ We need to set the real signal state to make sure the exec'd
+ process gets SIG_IGN properly.
+
+ Also set our real sigmask to match the client's sigmask so that
+ the exec'd child will get the right mask. First we need to
+ clear out any pending signals so they they don't get delivered,
+ which would confuse things.
+
+ XXX This is a bug - the signals should remain pending, and be
+ delivered to the new process after exec. There's also a
+ race-condition, since if someone delivers us a signal between
+ the sigprocmask and the execve, we'll still get the signal. Oh
+ well.
+ */
+ {
+ vki_sigset_t allsigs;
+ vki_siginfo_t info;
+
+ for (i = 1; i < VG_(max_signal); i++) {
+ struct vki_sigaction sa;
+ VG_(do_sys_sigaction)(i, NULL, &sa);
+ if (sa.ksa_handler == VKI_SIG_IGN)
+ VG_(sigaction)(i, &sa, NULL);
+ else {
+ sa.ksa_handler = VKI_SIG_DFL;
+ VG_(sigaction)(i, &sa, NULL);
+ }
+ }
+
+ VG_(sigfillset)(&allsigs);
+ while(VG_(sigtimedwait_zero)(&allsigs, &info) > 0)
+ ;
+
+ VG_(sigprocmask)(VKI_SIG_SETMASK, &tst->sig_mask, NULL);
+ }
+
+ if (0) {
+ Char **cpp;
+ VG_(printf)("exec: %s\n", path);
+ for (cpp = argv; cpp && *cpp; cpp++)
+ VG_(printf)("argv: %s\n", *cpp);
+ if (0)
+ for (cpp = envp; cpp && *cpp; cpp++)
+ VG_(printf)("env: %s\n", *cpp);
+ }
+
+ SET_STATUS_from_SysRes(
+ VG_(do_syscall3)(__NR_execve, (UWord)path, (UWord)argv, (UWord)envp)
+ );
+
+ /* If we got here, then the execve failed. We've already made way
+ too much of a mess to continue, so we have to abort. */
+ hosed:
+ vg_assert(FAILURE);
+ VG_(message)(Vg_UserMsg, "execve(%p(%s), %p, %p) failed, errno %d",
+ ARG1, ARG1, ARG2, ARG3, ERR);
+ VG_(message)(Vg_UserMsg, "EXEC FAILED: I can't recover from "
+ "execve() failing, so I'm dying.");
+ VG_(message)(Vg_UserMsg, "Add more stringent tests in PRE(sys_execve), "
+ "or work out how to recover.");
+ VG_(exit)(101);
+}
+
+PRE(sys_finfo)
+{
+ PRINT("finfo ( %p(%s), %ld, %p, %ld )",
+ ARG1,ARG1, ARG2, ARG3, ARG4);
+ PRE_REG_READ4(int, "finfo",
+ char*, Path1, int, cmd, void*, buffer, int, length);
+ PRE_MEM_RASCIIZ( "finfo(Path1)", ARG1 );
+ PRE_MEM_WRITE( "finfo(buffer)", ARG3, ARG4 );
+}
+POST(sys_finfo)
+{
+ POST_MEM_WRITE( ARG3, ARG4 );
+}
+
+PRE(sys_fstatx)
+{
+ PRINT("fstatx ( %ld, %p, %ld, %ld )", ARG1, ARG2, ARG3, ARG4 );
+ PRE_REG_READ4(Word, "fstatx", UWord, fd, void*, buf,
+ UWord, len, UWord, cmd);
+ PRE_MEM_WRITE( "fstatx(buf)", ARG2, ARG3 );
+}
+POST(sys_fstatx)
+{
+ POST_MEM_WRITE( ARG2, ARG3 );
+}
+
+PRE(sys_fsync)
+{
+ PRINT("fsync ( %ld )", ARG1);
+ PRE_REG_READ1(int, "fsync", int, fd);
+}
+
+PRE(sys_getdirent)
+{
+ *flags |= SfMayBlock;
+ /* this is pretty much like 'read':
+ getdirent(fd, buffer, nbytes) -> # actually read */
+ PRINT("getdirent ( %ld, %p, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(Word, "getdirent", UWord, fd, UChar*, buf, UWord, count);
+ PRE_MEM_WRITE( "getdirent(buf)", ARG2, ARG3 );
+}
+POST(sys_getdirent)
+{
+ vg_assert(SUCCESS);
+ POST_MEM_WRITE( ARG2, RES );
+}
+
+PRE(sys_getdirent64)
+{
+ /* same as getdirent, from our point of view? */
+ *flags |= SfMayBlock;
+ /* this is pretty much like 'read':
+ getdirent(fd, buffer, nbytes) -> # actually read */
+ PRINT("getdirent64 ( %ld, %p, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(Word, "getdirent64", UWord, fd, UChar*, buf, UWord, count);
+ PRE_MEM_WRITE( "getdirent64(buf)", ARG2, ARG3 );
+}
+POST(sys_getdirent64)
+{
+ vg_assert(SUCCESS);
+ POST_MEM_WRITE( ARG2, RES );
+}
+
+PRE(sys_getdomainname)
+{
+ PRINT("getdomainname ( %p, %ld )", ARG1, ARG2 );
+ PRE_MEM_WRITE( "getdomainname(buf)", ARG1, ARG2 );
+}
+POST(sys_getdomainname)
+{
+ POST_MEM_WRITE( ARG1, ARG2 );
+}
+
+PRE(sys_getgidx)
+{
+ PRINT("getgidx ( %d )", ARG1);
+ PRE_REG_READ1(UInt, "getgidx", long, arg1);
+}
+
+PRE(sys_getgroups)
+{
+ PRINT("getgroups ( %d, %p )", ARG1, ARG2);
+ PRE_REG_READ2(long, "getgroups", int, size, gid_t *, list);
+ if (ARG1 > 0)
+ PRE_MEM_WRITE( "getgroups(list)", ARG2, ARG1 * sizeof(gid_t) );
+}
+POST(sys_getgroups)
+{
+ vg_assert(SUCCESS);
+ if (ARG1 > 0 && RES > 0)
+ POST_MEM_WRITE( ARG2, RES * sizeof(gid_t) );
+}
+
+PRE(sys_gethostname)
+{
+ PRINT("gethostname ( %p, %ld )", ARG1, ARG2);
+ PRE_MEM_WRITE( "gethostname(buf)", ARG1, ARG2 );
+}
+POST(sys_gethostname)
+{
+ POST_MEM_WRITE( ARG1, ARG2 );
+}
+
+PRE(sys_getpriv)
+{
+ PRINT("getpriv (UNDOCUMENTED)(%ld, %p, %ld)", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "getpriv", int, arg1, void*, arg2, int, arg3);
+ PRE_MEM_WRITE( "getpriv(arg2)", ARG2, 8 );
+}
+POST(sys_getpriv)
+{
+ if (ARG2)
+ POST_MEM_WRITE(ARG2, 8);
+}
+
+PRE(sys_getprocs)
+{
+ PRINT("getprocs ( %p, %ld, %p, %ld, %p, %ld )",
+ ARG1, ARG2, ARG3, ARG4, ARG5, ARG6 );
+ PRE_REG_READ6(int, "getprocs",
+ void*, processbuffer, long, processize,
+ void*, filebuffer, long, filesize,
+ void*, indexpointer, long, count);
+
+ /* (processbuffer, processsize, filebuffer, filesize,
+ indexpointer, count) */
+ PRE_MEM_READ( "getprocs(IndexPointer)", ARG5, sizeof(UInt) );
+ if (ARG1)
+ PRE_MEM_WRITE( "getprocs(ProcessBuffer)", ARG1, ARG2 * ARG6 );
+ if (ARG3)
+ PRE_MEM_WRITE( "getprocs(FileBuffer)", ARG3, ARG4 * ARG6 );
+}
+POST(sys_getprocs)
+{
+ vg_assert(SUCCESS);
+ if (ARG1)
+ POST_MEM_WRITE( ARG1, ARG2 * ARG6 );
+ if (ARG3)
+ POST_MEM_WRITE( ARG3, ARG4 * ARG6 );
+}
+
+PRE(sys_getrpid)
+{
+ PRINT("getrpid ( %d, %ld, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(long, "getrpid", long, arg1, long, arg2, long, arg3);
+}
+
+PRE(sys_getsockopt)
+{
+ PRINT("getsockopt ( %ld, %ld, %ld, %p, %p )",
+ ARG1, ARG2, ARG3, ARG4, ARG5);
+ PRE_REG_READ5(int, "getsockopt", int, socket, int, level,
+ int, optionname,
+ void*, optionval, int*, optionlen);
+ if (ARG5) {
+ PRE_MEM_READ( "getsockopt(optionlen)", ARG5, sizeof(UInt) );
+ PRE_MEM_WRITE( "getsockopt(optionval)", ARG4, *(UInt*)ARG5 );
+ }
+}
+POST(sys_getsockopt)
+{
+ if (ARG5) {
+ POST_MEM_WRITE( ARG5, sizeof(UInt) );
+ POST_MEM_WRITE( ARG4, *(UInt*)ARG5 );
+ }
+}
+
+PRE(sys_gettimerid)
+{
+ PRINT("gettimerid ( %ld, %ld )", ARG1, ARG2);
+ PRE_REG_READ2(int, "gettimerid", int, timertype, int, notifytype);
+}
+
+PRE(sys_getuidx)
+{
+ PRINT("getuidx ( %ld )", ARG1);
+ PRE_REG_READ1(UInt, "getuidx", UInt, arg1);
+}
+
+PRE(sys_incinterval)
+{
+ PRINT("incinterval ( %ld, %p, %p )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "incinterval", int, timerid,
+ struct itimerstruc_t*, value,
+ struct itimerstruc_t*, ovalue);
+ if (ARG2)
+ PRE_MEM_READ( "incinterval(value)",
+ ARG2, sizeof(struct itimerstruc_t));
+ if (ARG3)
+ PRE_MEM_WRITE( "incinterval(value)",
+ ARG3, sizeof(struct itimerstruc_t));
+}
+POST(sys_incinterval)
+{
+ if (ARG3)
+ POST_MEM_WRITE( ARG3, sizeof(struct itimerstruc_t));
+}
+
+PRE(sys_kfcntl)
+{
+ *flags |= SfMayBlock;
+ switch (ARG2) {
+ // These ones ignore ARG3.
+ case F_GETFD:
+ case F_GETFL:
+ case F_GETOWN:
+ PRINT("kfcntl ( %ld, %ld )", ARG1,ARG2);
+ PRE_REG_READ2(long, "fcntl", unsigned int, fd, unsigned int, cmd);
+ break;
+
+ // These ones use ARG3 as "arg".
+ case F_DUPFD:
+ case F_SETFD:
+ case F_SETFL:
+ case F_SETOWN:
+ PRINT("kfcntl[ARG3=='arg'] ( %ld, %ld, %ld )", ARG1,ARG2,ARG3);
+ PRE_REG_READ3(long, "fcntl",
+ unsigned int, fd, unsigned int, cmd, unsigned long, arg);
+ break;
+
+ // These ones use ARG3 as "lock".
+# if !defined(VGP_ppc64_aix5)
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+# endif
+ case F_GETLK64:
+ case F_SETLK64:
+ case F_SETLKW64:
+ PRINT("kfcntl[ARG3=='lock'] ( %ld, %ld, %p )", ARG1,ARG2,ARG3);
+ PRE_REG_READ3(long, "fcntl",
+ unsigned int, fd, unsigned int, cmd,
+ struct flock64 *, lock);
+ if (ARG3 && (ARG2 == F_GETLK || ARG2 == F_GETLK64))
+ PRE_MEM_READ( "kfcntl(F_GETLK)", ARG3, sizeof(struct flock64) );
+ break;
+ }
+}
+POST(sys_kfcntl)
+{
+ // if (ARG2 == VKI_F_DUPFD) {
+ // if (!ML_(fd_allowed)(RES, "fcntl(DUPFD)", tid, True)) {
+ // VG_(close)(RES);
+ // SET_STATUS_Failure( VKI_EMFILE );
+ // } else {
+ // if (VG_(clo_track_fds))
+ // record_fd_open_named(tid, RES);
+ // }
+ // }
+ if (ARG3 && (ARG2 == F_GETLK || ARG2 == F_GETLK64))
+ POST_MEM_WRITE( ARG3, sizeof(struct flock64) );
+}
+
+/* COG; can this be moved inside the pre-handler? */
+static vki_sigset_t fork_saved_mask;
+PRE(sys_kfork) /* COPY OF GENERIC */
+{
+ vki_sigset_t mask;
+
+ PRINT("kfork ( )");
+ PRE_REG_READ0(long, "fork");
+
+ /* Block all signals during fork, so that we can fix things up in
+ the child without being interrupted. */
+ VG_(sigfillset)(&mask);
+ VG_(sigprocmask)(VKI_SIG_SETMASK, &mask, &fork_saved_mask);
+
+ SET_STATUS_from_SysRes( VG_(do_syscall0)(__NR_fork) );
+
+ if (SUCCESS && RES == 0) {
+ VG_(do_atfork_child)(tid);
+
+ /* restore signal mask */
+ VG_(sigprocmask)(VKI_SIG_SETMASK, &fork_saved_mask, NULL);
+ }
+ else
+ if (SUCCESS && RES > 0) {
+ PRINT(" fork: process %d created child %d\n", VG_(getpid)(), RES);
+
+ /* restore signal mask */
+ VG_(sigprocmask)(VKI_SIG_SETMASK, &fork_saved_mask, NULL);
+ }
+}
+
+PRE(sys_kftruncate)
+{
+ PRINT("kftruncate (BOGUS HANDLER)");
+}
+
+PRE(sys_kgetsidx)
+{
+ PRINT("kgetsidx ( %ld )", ARG1);
+ PRE_REG_READ1(Word, "kgetsidx", Word, arg1);
+}
+
+PRE(sys_kill)
+{
+ PRINT("kill ( %ld, %ld )", ARG1, ARG2);
+ PRE_REG_READ2(int, "kill", int, pid, int, signal);
+}
+
+PRE(sys_kioctl)
+{
+ *flags |= SfMayBlock;
+ PRINT("kioctl ( %ld, %p, %p, %p )", ARG1, ARG2, ARG3, ARG4);
+ PRE_REG_READ4(Word, "ioctl", Word, fd,
+ Word, command, Word, arg, Word, ext);
+ switch (ARG2 /* request */) {
+ case 0x5800/*TXISATTY*/:
+ case 0x5801/*TXTTYNAME*/:
+ break;
+ case 0x412:/*no idea what any of these are*/
+ case 0x430:
+ case 0x431:
+ case 0x432:
+ case 0x441:
+ case 0x442:
+ case 0x462:
+ case 0x480:
+ case 0x482:
+ case 0x738:
+ case 0x736:
+ case 0x73B:
+ case 0x73C:
+ case 0x73D:
+ case 0x73E:
+ case 0x5401:
+ case 0x5403:
+ case 0xFF01/*no_idea_at_all_what_this_is*/:
+ break;
+ /* We don't have any specific information on it, so
+ try to do something reasonable based on direction and
+ size bits.
+
+ According to Simon Hausmann, _IOC_READ means the kernel
+ writes a value to the ioctl value passed from the user
+ space and the other way around with _IOC_WRITE. */
+ default: {
+ UInt dir = _VKI_IOC_DIR(ARG2);
+ UInt size = _VKI_IOC_SIZE(ARG2);
+ if (VG_(strstr)(VG_(clo_sim_hints), "lax-ioctls") != NULL) {
+ /*
+ * Be very lax about ioctl handling; the only
+ * assumption is that the size is correct. Doesn't
+ * require the full buffer to be initialized when
+ * writing. Without this, using some device
+ * drivers with a large number of strange ioctl
+ * commands becomes very tiresome.
+ */
+ } else if (/* size == 0 || */ dir == _VKI_IOC_NONE) {
+ static Int moans = 5;
+ if (moans > 0 && !VG_(clo_xml)) {
+ moans--;
+ VG_(message)(Vg_UserMsg,
+ "Warning: noted but unhandled ioctl 0x%x"
+ " with no size/direction hints",
+ ARG2);
+ VG_(message)(Vg_UserMsg,
+ " This could cause spurious value errors"
+ " to appear.");
+ VG_(message)(Vg_UserMsg,
+ " See README_MISSING_SYSCALL_OR_IOCTL for "
+ "guidance on writing a proper wrapper." );
+ }
+ } else {
+ if ((dir & _VKI_IOC_WRITE) && size > 0)
+ PRE_MEM_READ( "ioctl(generic)", ARG3, size);
+ if ((dir & _VKI_IOC_READ) && size > 0)
+ PRE_MEM_WRITE( "ioctl(generic)", ARG3, size);
+ }
+ break;
+ }
+ } /* switch */
+}
+POST(sys_kioctl)
+{
+ switch (ARG2 /*request*/) {
+ case 0xFF01:
+ /* 100% kludge. I have no idea what this ioctl is. IOCINFO
+ ? But at a guess I'd say it returns some kind of info
+ from the kernel. */
+ if (ARG3) POST_MEM_WRITE(ARG3, 16);
+ break;
+ case 0x738: /* Shows up in MPI applications. */
+ if (ARG3) POST_MEM_WRITE(ARG3, 4*sizeof(Word));
+ break;
+ case 0x736: /* Shows up in MPI applications. */
+ case 0x73B: /* Shows up in MPI applications. */
+ case 0x73C: /* Shows up in MPI applications. */
+ if (ARG3) POST_MEM_WRITE(ARG3, 16);
+ /* in fact only 4 needed, but being conservative */
+ break;
+
+ case 0x5401:
+ /* some kind of tty thing */
+ if (ARG3) POST_MEM_WRITE(ARG3, 32);
+ break;
+
+ case 0x5801/*TXTTYNAME*/:
+ /* who knows if this is right. Presumably an ascii string is
+ written into the buffer specified by ARG3, but how long is
+ that buffer? */
+ if (ARG3) POST_MEM_WRITE(ARG3, 16);
+ break;
+
+ case 0x412:
+ case 0x430:
+ case 0x431:
+ case 0x432:
+ case 0x441:
+ case 0x442:
+ case 0x462:
+ case 0x480:
+ case 0x482:
+ case 0x73D:
+ case 0x73E:
+ case 0x5800/*TXISATTY*/:
+ case 0x5403:
+ break;
+ /* We don't have any specific information on it, so
+ try to do something reasonable based on direction and
+ size bits.
+
+ According to Simon Hausmann, _IOC_READ means the kernel
+ writes a value to the ioctl value passed from the user
+ space and the other way around with _IOC_WRITE. */
+ default: {
+ UInt dir = _VKI_IOC_DIR(ARG2);
+ UInt size = _VKI_IOC_SIZE(ARG2);
+ if (size > 0 && (dir & _VKI_IOC_READ)
+ && RES == 0
+ && ARG3 != (Addr)NULL)
+ POST_MEM_WRITE(ARG3, size);
+ break;
+ }
+ }
+}
+
+PRE(sys_klseek)
+{
+ PRINT("klseek ( %ld, %ld, %ld, %p )", ARG1, ARG2, ARG3, ARG4);
+ PRE_REG_READ4(long, "klseek",
+ long, fd, long, offset, long, whence, void*, arg4);
+ /* XXX: looks like 4th arg is a pointer to something. Is it
+ read or written by the kernel? */
+}
+
+PRE(sys_knlist)
+{
+ PRINT("knlist (BOGUS HANDLER)");
+}
+
+PRE(sys_kpread)
+{
+ *flags |= SfMayBlock;
+ PRINT("sys_kpread ( %d, %p, %llu, %lld )",
+ ARG1, ARG2, (ULong)ARG3, ARG4);
+ PRE_REG_READ4(ssize_t, "kpread",
+ unsigned int, fd, char *, buf,
+ vki_size_t, count, long, offset);
+ PRE_MEM_WRITE( "kpread(buf)", ARG2, ARG3 );
+}
+POST(sys_kpread)
+{
+ vg_assert(SUCCESS);
+ if (RES > 0) {
+ POST_MEM_WRITE( ARG2, RES );
+ }
+}
+
+PRE(sys_kread)
+{
+ *flags |= SfMayBlock;
+ PRINT("sys_read ( %ld, %p, %llu )", ARG1, ARG2, (ULong)ARG3);
+ PRE_REG_READ3(ssize_t, "read",
+ unsigned int, fd, char *, buf, vki_size_t, count);
+ //zz if (!ML_(fd_allowed)(ARG1, "read", tid, False))
+ //zz SET_STATUS_Failure( VKI_EBADF );
+ //zz else
+ PRE_MEM_WRITE( "read(buf)", ARG2, ARG3 );
+}
+POST(sys_kread)
+{
+ vg_assert(SUCCESS);
+ POST_MEM_WRITE( ARG2, RES );
+}
+
+PRE(sys_kreadv)
+{
+ Int i;
+ struct vki_iovec * vec;
+ *flags |= SfMayBlock;
+ /* ssize_t readvx ( int fd, struct iovec*, int iovCount, int extension ) */
+ PRINT("kreadv ( %ld, %p, %ld, %p )", ARG1, ARG2, ARG3, ARG4);
+ PRE_REG_READ4(ssize_t, "kreadv",
+ unsigned long, fd, const struct iovec *, vector,
+ unsigned long, iovCount, unsigned long, extension);
+ //zz if (!ML_(fd_allowed)(ARG1, "readv", tid, False)) {
+ //zz SET_STATUS_Failure( VKI_EBADF );
+ //zz } else {
+ PRE_MEM_READ( "kreadv(vector)", ARG2, ARG3 * sizeof(struct vki_iovec) );
+ if (ARG2 != 0) {
+ /* ToDo: don't do any of the following if the vector is invalid */
+ vec = (struct vki_iovec *)ARG2;
+ for (i = 0; i < (Int)ARG3; i++)
+ PRE_MEM_WRITE( "kreadv(vector[...])",
+ (Addr)vec[i].iov_base, vec[i].iov_len );
+ }
+ //zz }
+}
+POST(sys_kreadv)
+{
+ vg_assert(SUCCESS);
+ if (RES > 0) {
+ Int i;
+ struct vki_iovec * vec = (struct vki_iovec *)ARG2;
+ Int remains = RES;
+
+ /* RES holds the number of bytes read. */
+ for (i = 0; i < (Int)ARG3; i++) {
+ Int nReadThisBuf = vec[i].iov_len;
+ if (nReadThisBuf > remains) nReadThisBuf = remains;
+ POST_MEM_WRITE( (Addr)vec[i].iov_base, nReadThisBuf );
+ remains -= nReadThisBuf;
+ if (remains < 0) VG_(core_panic)("readv: remains < 0");
+ }
+ }
+}
+
+PRE(sys_kthread_ctl)
+{
+ *flags |= SfMayBlock;
+ PRINT("kthread_ctl (BOGUS HANDLER)");
+}
+
+PRE(sys_kwaitpid)
+{
+ /* Note: args 1 and 2 (status, pid) opposite way round
+ from generic handler */
+ *flags |= SfMayBlock;
+ PRINT("kwaitpid ( %p, %ld, %ld, %p, %p )", ARG1,ARG2,ARG3,ARG4,ARG5);
+ PRE_REG_READ3(long, "waitpid",
+ unsigned int *, status, int, pid, int, options);
+
+ if (ARG1 != (Addr)NULL)
+ PRE_MEM_WRITE( "kwaitpid(status)", ARG1, sizeof(int) );
+}
+POST(sys_kwaitpid)
+{
+ if (ARG1 != (Addr)NULL)
+ POST_MEM_WRITE( ARG1, sizeof(int) );
+}
+
+PRE(sys_kwrite)
+{
+ //zz Bool ok;
+ *flags |= SfMayBlock;
+ PRINT("sys_kwrite ( %ld, %p, %llu )", ARG1, ARG2, (ULong)ARG3);
+ PRE_REG_READ3(ssize_t, "kwrite",
+ unsigned int, fd, const char *, buf, vki_size_t, count);
+ /* check to see if it is allowed. If not, try for an exemption from
+ --sim-hints=enable-outer (used for self hosting). */
+ //zz ok = ML_(fd_allowed)(ARG1, "write", tid, False);
+ //zz if (!ok && ARG1 == 2/*stderr*/
+ //zz && VG_(strstr)(VG_(clo_sim_hints),"enable-outer"))
+ //zz ok = True;
+ //zz if (!ok)
+ //zz SET_STATUS_Failure( VKI_EBADF );
+ //zz else
+ PRE_MEM_READ( "write(buf)", ARG2, ARG3 );
+}
+
+PRE(sys_kwritev)
+{
+ PRINT("kwritev (BOGUS HANDLER)");
+}
+
+PRE(sys_listen)
+{
+ PRINT("listen (BOGUS HANDLER)");
+}
+
+PRE(sys_loadbind)
+{
+ PRINT("loadbind( %ld, %p, %p )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "loadbind", int, flag,
+ void*, ExportPointer, void*, ImportPointer);
+}
+
+PRE(sys_loadquery)
+{
+ /* loadquery ( int flags, void* buffer, unsigned int bufferlength ) */
+ PRINT("loadquery ( %p, %p, %ld )", ARG1, ARG2, ARG3);
+ PRE_MEM_WRITE( "loadquery(buf)", ARG2, ARG3 );
+}
+POST(sys_loadquery)
+{
+ vg_assert(SUCCESS);
+ POST_MEM_WRITE( ARG2, ARG3 );
+}
+
+PRE(sys_lseek)
+{
+ PRINT("lseek (%ld, %ld, %ld)", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(long, "lseek", long, fd, long, offset, long, whence);
+}
+
+PRE(sys_mkdir)
+{
+ PRINT("mkdir (%p(%s), %p)", ARG1,ARG1, ARG2);
+ PRE_REG_READ2(int, "mkdir", char*, path, int, mode);
+ PRE_MEM_RASCIIZ( "mkdir(path)", ARG1 );
+}
+
+PRE(sys_mmap)
+{
+ PRINT("mmap ( %p, %ld, %p, %p, %ld, %ld )",
+ ARG1, ARG2, ARG3, ARG4, ARG5, ARG6);
+ PRE_REG_READ6(void*, "mmap", void*, addr, int, len,
+ int, prot, int, flags, int, fd, int, off);
+}
+POST(sys_mmap)
+{
+ vg_assert(SUCCESS);
+ Addr addr = (Addr)RES;
+ UWord len = (UWord)ARG2;
+ UWord prot = (UWord)ARG3;
+ UWord flags = (UWord)ARG4;
+ Bool r = (prot & VKI_PROT_READ) > 0;
+ Bool w = (prot & VKI_PROT_WRITE) > 0;
+ Bool x = (prot & VKI_PROT_EXEC) > 0;
+ VG_TRACK( new_mem_mmap, addr, len, r,w,x );
+ Bool d = VG_(am_notify_client_mmap)( addr, len, prot, flags,
+ 0/*fake fd*/, 0/*fake offset*/);
+ if (d)
+ VG_(discard_translations)( addr, len, "POST(sys_mmap)" );
+}
+
+PRE(sys_mprotect)
+{
+ PRINT("mprotect (BOGUS HANDLER)( %p, %ld, %p )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "mprotect", void*, addr, long, len, long, prot);
+}
+POST(sys_mprotect)
+{
+ Bool d;
+ vg_assert(SUCCESS);
+ Addr addr = ARG1;
+ UWord len = ARG2;
+ UWord prot = ARG3;
+ d = VG_(am_notify_mprotect)( addr, len, prot );
+ if (d)
+ VG_(discard_translations)( addr, len, "POST(sys_mprotect)" );
+}
+
+PRE(sys_munmap)
+{
+ PRINT("munmap ( %p, %ld )", ARG1, ARG2);
+ PRE_REG_READ2(int, "munmap", void*, addr, long, len);
+}
+POST(sys_munmap)
+{
+ Bool d;
+ vg_assert(SUCCESS);
+ Addr addr = ARG1;
+ UWord len = ARG2;
+ VG_TRACK( die_mem_munmap, addr, len );
+ d = VG_(am_notify_munmap)( addr, len );
+ if (d)
+ VG_(discard_translations)( addr, len, "POST(sys_munmap)" );
+}
+
+PRE(sys_naccept)
+{
+ PRINT("naccept (%ld, %p, %p)", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "naccept", int, socket, char*, addr, int*, addrlen);
+ PRE_MEM_READ( "naccept(addrlen)", ARG3, sizeof(UInt) );
+ PRE_MEM_WRITE( "naccept(addr)", ARG2, *(UInt*)ARG3 );
+}
+POST(sys_naccept)
+{
+ POST_MEM_WRITE( ARG3, sizeof(UInt) );
+ POST_MEM_WRITE( ARG2, *(UInt*)ARG3 );
+}
+
+PRE(sys_ngetpeername)
+{
+ PRINT("ngetpeername ( %ld, %p, %p )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "ngetpeername", int, fd, char*, name, int*, namelen);
+ PRE_MEM_READ( "ngetpeername(namelen)", ARG3, sizeof(UInt) );
+ PRE_MEM_WRITE( "ngetpeername(name)", ARG2, *(UInt*)ARG3 );
+}
+POST(sys_ngetpeername)
+{
+ POST_MEM_WRITE( ARG3, sizeof(UInt) );
+ POST_MEM_WRITE( ARG2, *(UInt*)ARG3 );
+}
+
+PRE(sys_ngetsockname)
+{
+ PRINT("ngetsockname ( %ld, %p, %p )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "ngetsockname", int, fd, char*, name, int*, namelen);
+ PRE_MEM_READ( "ngetsockname(namelen)", ARG3, sizeof(UInt) );
+ PRE_MEM_WRITE( "ngetsockname(name)", ARG2, *(UInt*)ARG3 );
+}
+POST(sys_ngetsockname)
+{
+ POST_MEM_WRITE( ARG3, sizeof(UInt) );
+ POST_MEM_WRITE( ARG2, *(UInt*)ARG3 );
+}
+
+PRE(sys_nrecvfrom)
+{
+ *flags |= SfMayBlock;
+ PRINT("nrecvfrom ( %ld, %p, %ld, %ld, %p, %p )",
+ ARG1, ARG2, ARG3, ARG4, ARG5, ARG6 );
+ PRE_REG_READ6(ssize_t, "nrecvfrom",
+ int, s, void*, buf, size_t, len, int, flags,
+ void*, from, UInt*, fromlen);
+ PRE_MEM_WRITE( "nrecvfrom(buf)", ARG2, ARG3 );
+ if (ARG5) {
+ PRE_MEM_READ( "nrecvfrom(fromlen)", ARG6, sizeof(UInt) );
+ PRE_MEM_WRITE( "nrecvfrom(from)", ARG5, *(UInt*)ARG6 );
+ }
+}
+POST(sys_nrecvfrom)
+{
+ POST_MEM_WRITE( ARG2, RES );
+ if (ARG5) {
+ POST_MEM_WRITE(ARG6, sizeof(UInt));
+ POST_MEM_WRITE(ARG5, *(UInt*)ARG6);
+ }
+}
+
+PRE(sys_nrecvmsg)
+{
+ *flags |= SfMayBlock;
+ PRINT("nrecvmsg(BOGUS HANDLER)( %ld, %p, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(long, "nrecvmsg", long, arg1, void*, arg2, long, arg3);
+}
+
+PRE(sys_nsendmsg)
+{
+ *flags |= SfMayBlock;
+ PRINT("nsendmsg(BOGUS HANDLER)( %ld, %p, %ld )", ARG1, ARG2, ARG3);
+}
+
+PRE(sys_open) /* XXX CoG */
+{
+ //zz HChar name[30];
+ //zz SysRes sres;
+
+ if (ARG2 & VKI_O_CREAT) {
+ // 3-arg version
+ PRINT("sys_open ( %p(%s), %p, %ld )",ARG1,ARG1,ARG2,ARG3);
+ PRE_REG_READ3(long, "open",
+ const char *, filename, int, flags, int, mode);
+ } else {
+ // 2-arg version
+ PRINT("sys_open ( %p(%s), %p )",ARG1,ARG1,ARG2);
+ PRE_REG_READ2(long, "open",
+ const char *, filename, int, flags);
+ }
+ PRE_MEM_RASCIIZ( "open(filename)", ARG1 );
+
+ //zz /* Handle the case where the open is of /proc/self/cmdline or
+ //zz /proc/<pid>/cmdline, and just give it a copy of the fd for the
+ //zz fake file we cooked up at startup (in m_main). Also, seek the
+ //zz cloned fd back to the start. */
+ //zz
+ //zz VG_(sprintf)(name, "/proc/%d/cmdline", VG_(getpid)());
+ //zz if (ML_(safe_to_deref)( (void*)ARG1, 1 )
+ //zz && (VG_(strcmp)((Char *)ARG1, name) == 0
+ //zz || VG_(strcmp)((Char *)ARG1, "/proc/self/cmdline") == 0)) {
+ //zz sres = VG_(dup)( VG_(cl_cmdline_fd) );
+ //zz SET_STATUS_from_SysRes( sres );
+ //zz if (!sres.isError) {
+ //zz OffT off = VG_(lseek)( sres.res, 0, VKI_SEEK_SET );
+ //zz if (off < 0)
+ //zz SET_STATUS_Failure( VKI_EMFILE );
+ //zz }
+ //zz return;
+ //zz }
+
+ /* Otherwise handle normally */
+ *flags |= SfMayBlock;
+}
+POST(sys_open)
+{
+ vg_assert(SUCCESS);
+ //zz if (!ML_(fd_allowed)(RES, "open", tid, True)) {
+ //zz VG_(close)(RES);
+ //zz SET_STATUS_Failure( VKI_EMFILE );
+ //zz } else {
+ //zz if (VG_(clo_track_fds))
+ //zz ML_(record_fd_open_with_given_name)(tid, RES, (Char*)ARG1);
+ //zz }
+}
+
+PRE(sys_pipe)
+{
+ PRINT("sys_pipe ( %p )", ARG1);
+ PRE_REG_READ1(int, "pipe", int *, filedes);
+ PRE_MEM_WRITE( "pipe(filedes)", ARG1, 2*sizeof(int) );
+}
+POST(sys_pipe)
+{
+ //zz Int *p = (Int *)ARG1;
+
+ //zz if (!ML_(fd_allowed)(p[0], "pipe", tid, True) ||
+ //zz !ML_(fd_allowed)(p[1], "pipe", tid, True)) {
+ //zz VG_(close)(p[0]);
+ //zz VG_(close)(p[1]);
+ //zz SET_STATUS_Failure( VKI_EMFILE );
+ //zz } else {
+ POST_MEM_WRITE( ARG1, 2*sizeof(int) );
+ //zz if (VG_(clo_track_fds)) {
+ //zz ML_(record_fd_open_nameless)(tid, p[0]);
+ //zz ML_(record_fd_open_nameless)(tid, p[1]);
+ //zz }
+ //zz }
+}
+
+PRE(sys_privcheck)
+{
+ PRINT("privcheck ( %ld )", ARG1);
+ PRE_REG_READ1(int, "privcheck", int, arg1);
+}
+
+PRE(sys_readlink)
+{
+ PRINT("readlink ( 0x%x(%s),0x%x,%ld )", ARG1,ARG1, ARG2, ARG3);
+ PRE_REG_READ3(long, "readlink",
+ const char *, path, char *, buf, int, bufsiz);
+ PRE_MEM_RASCIIZ( "readlink(path)", ARG1 );
+ PRE_MEM_WRITE( "readlink(buf)", ARG2,ARG3 );
+}
+POST(sys_readlink)
+{
+ POST_MEM_WRITE( ARG2, RES + 1 );
+}
+
+PRE(sys_recv)
+{
+ *flags |= SfMayBlock;
+ PRINT("recv ( %ld, %p, %ld, %ld )",
+ ARG1, ARG2, ARG3, ARG4);
+ PRE_REG_READ4(int, "recv", int, fd, void*, buf, int, len, int, flags);
+ PRE_MEM_WRITE( "recv(buf)", ARG2, ARG3);
+}
+POST(sys_recv)
+{
+ if (RES > 0)
+ POST_MEM_WRITE(ARG2, RES);
+}
+
+PRE(sys_rename)
+{
+ *flags |= SfMayBlock;
+ PRINT( "rename ( %p(%s), %p(%s) )", ARG1,ARG1, ARG2,ARG2 );
+ PRE_REG_READ2(int, "rename", char*, frompath, char*, topath);
+ PRE_MEM_RASCIIZ( "rename(frompath)", ARG1 );
+ PRE_MEM_RASCIIZ( "rename(topath)", ARG2 );
+}
+
+PRE(sys_sbrk)
+{
+ PRINT("sbrk (BOGUS HANDLER)( %p )", ARG1);
+ PRE_REG_READ1(long, "sbrk", long, arg1);
+ /* After a zero sbrk, disallow aspacem from doing sbrk, since libc
+ might rely on the value returned by this syscall. */
+ /* 1 Oct 06: not currently used (aspacemgr-aix5.c ignores it) */
+ VG_(am_aix5_sbrk_allowed) = toBool(ARG1 != 0);
+ /* Disallow libc from moving the brk backwards as that might trash
+ SkPreAlloc sections acquired by aspacem from previous uses of
+ sbrk. */
+ if (ARG1 < 0)
+ ARG1 = 0;
+ /* Do this as a sync syscall, so the sbrk_allowed flag gets turned
+ back on ASAP. Typically libc does sbrk(0) and then sbrk(x > 0)
+ in quick succession. Although surely it should hold some kind
+ of lock at that point, else it cannot safely use the result from
+ the first sbrk call to influence the second one? */
+ *flags &= ~SfMayBlock;
+}
+POST(sys_sbrk)
+{
+ vg_assert(SUCCESS);
+ handle_sbrk(ARG1);
+}
+
+PRE(sys_sched_get_priority_max)
+{
+ PRINT("sched_get_priority_max ( %ld )", ARG1);
+ PRE_REG_READ1(int, "sched_get_priority_max", int, arg1);
+}
+
+PRE(sys_sem_destroy)
+{
+ PRINT("sem_destroy ( %p )", ARG1);
+ PRE_REG_READ1(int, "sem_destroy", sem_t*, sem);
+ PRE_MEM_READ( "sem_destroy(sem)", ARG1, sizeof(sem_t) );
+}
+
+PRE(sys_sem_init)
+{
+ PRINT("sem_init ( %p, %ld, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "sem_init", sem_t*, sem, int, pshared, int, value);
+ PRE_MEM_WRITE( "sem_init(sem)", ARG1, sizeof(sem_t) );
+}
+POST(sys_sem_init)
+{
+ POST_MEM_WRITE( ARG1, sizeof(sem_t) );
+}
+
+PRE(sys_sem_post)
+{
+ PRINT("sem_post ( %p )", ARG1);
+ PRE_REG_READ1(int, "sem_post", sem_t*, sem);
+ PRE_MEM_READ("sem_post(sem)", ARG1, sizeof(sem_t));
+}
+POST(sys_sem_post)
+{
+ POST_MEM_WRITE(ARG1, sizeof(sem_t));
+}
+
+PRE(sys_send)
+{
+ *flags |= SfMayBlock;
+ PRINT("send (BOGUS HANDLER)( %ld, %p, %ld, %ld )",
+ ARG1, ARG2, ARG3, ARG4);
+}
+
+PRE(sys_setgid)
+{
+ PRINT("setgid ( %ld )", ARG1);
+ PRE_REG_READ1(void, "setgid", int, uid);
+}
+
+PRE(sys_setsockopt)
+{
+ PRINT("setsockopt ( %ld, %ld, %ld, %p, %ld )",
+ ARG1,ARG2,ARG3,ARG4,ARG5 );
+ PRE_REG_READ5(long, "setsockopt",
+ long, socket, long, level, long, optionname,
+ void*, optionvalue, long, optlen);
+ if (ARG4)
+ PRE_MEM_READ( "setsockopt(optionvalue)", ARG4, ARG5 );
+}
+
+PRE(sys_setuid)
+{
+ PRINT("setuid ( %ld )", ARG1);
+ PRE_REG_READ1(void, "setuid", int, uid);
+}
+
+static UWord get_shm_size ( Word shmid )
+{
+ SysRes res;
+ struct shmid_ds buf;
+ vg_assert(__NR_AIX5_shmctl != __NR_AIX5_UNKNOWN);
+ res = VG_(do_syscall3)(__NR_AIX5_shmctl, shmid, IPC_STAT, (UWord)&buf);
+ if (0)
+ VG_(printf)("XXX: shm_size(%ld) = %d %d\n", shmid, res.res, res.err);
+ if (res.isError) {
+ if (0)
+ VG_(printf)("XXX: shm_size(shmid = %ld): FAILED\n", shmid);
+ return 0* 4096;
+ } else {
+ return buf.shm_segsz;
+ }
+ /* fails with 22 and 13 (22 = EINVAL, Invalid argument,
+ 13 = EACCES, Permission denied) */
+ /* shmat (4, 0x0, 0x1800) --> Success(0x40000000)
+ XXX: shm_size(4) = -1 22
+ shmat: seg size = 0
+ XXX: shm_size(4) = -1 22
+
+ shmat (5, 0x0, 0x1800) --> Success(0x50000000)
+ XXX: shm_size(5) = -1 13
+ shmat: seg size = 0
+ XXX: shm_size(5) = -1 13
+
+ shmat (4, 0x0, 0x1800) --> Success(0x40000000)
+ XXX: shm_size(4) = -1 22
+ shmat: seg size = 0
+ XXX: shm_size(4) = -1 22
+ */
+}
+PRE(sys_shmat)
+{
+ UWord segmentSize;
+ /* void* shmat ( int shmid, const void* shmaddr, int flags ) */
+ PRINT("shmat (%ld, %p, %p)", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(void*, "shmat", int, shmid, void*, shmaddr, int, flags);
+ segmentSize = get_shm_size( ARG1 );
+ if (0) VG_(printf)("shmat: seg size = %lu\n", segmentSize);
+}
+POST(sys_shmat)
+{
+ UInt segmentSize;
+ vg_assert(SUCCESS);
+ vg_assert(RES != -1L);
+ segmentSize = get_shm_size ( ARG1 );
+ if ( segmentSize > 0 ) {
+ UInt prot = VKI_PROT_READ|VKI_PROT_WRITE;
+ Bool d;
+
+ if (ARG2 & SHM_RDONLY)
+ prot &= ~VKI_PROT_WRITE;
+
+ d = VG_(am_notify_client_shmat)( RES, VG_PGROUNDUP(segmentSize), prot );
+
+ /* we don't distinguish whether it's read-only or
+ * read-write -- it doesn't matter really. */
+ VG_TRACK( new_mem_mmap, RES, segmentSize, True, True, False );
+ if (d)
+ VG_(discard_translations)( (Addr64)RES,
+ (ULong)VG_PGROUNDUP(segmentSize),
+ "ML_(generic_POST_sys_shmat)" );
+ }
+}
+
+PRE(sys_shmctl)
+{
+ PRINT("shmctl ( %ld, %ld, %p )", ARG1, ARG2, ARG3 );
+ PRE_REG_READ3(int, "shmctl", int, shmid, int, command, void*, buffer);
+ if (ARG3)
+ PRE_MEM_WRITE( "shmctl(buffer)", ARG3, sizeof(struct shmid_ds) );
+}
+POST(sys_shmctl)
+{
+ if ((ARG3) && ARG2 == IPC_STAT)
+ POST_MEM_WRITE( ARG3, sizeof(struct shmid_ds) );
+}
+
+PRE(sys_shmdt)
+{
+ PRINT("shmdt ( %p )", ARG1);
+ PRE_REG_READ1(long, "shmdt", void*, address);
+}
+POST(sys_shmdt)
+{
+ NSegment const*const s = VG_(am_find_nsegment)(ARG1);
+
+ if (s != NULL) {
+ Addr s_start = s->start;
+ SizeT s_len = s->end+1 - s->start;
+ Bool d;
+
+ vg_assert(s->kind == SkShmC && s->start == ARG1);
+
+ d = VG_(am_notify_munmap)(s_start, s_len);
+ /* s is now invalid; do not use after here */
+ VG_TRACK( die_mem_munmap, s_start, s_len );
+ if (d)
+ VG_(discard_translations)( (Addr64)s_start,
+ (ULong)s_len,
+ "ML_(generic_POST_sys_shmdt)" );
+ }
+}
+
+PRE(sys_shmget)
+{
+ PRINT("shmget ( %ld, %ld, %ld )", ARG1, ARG2, ARG3 );
+ PRE_REG_READ3(int, "shmget", key_t, key, size_t, size, int, shmFlag);
+}
+
+PRE(sys_shutdown)
+{
+ PRINT("shutdown (BOGUS HANDLER)");
+}
+
+PRE(sys_sigcleanup)
+{
+ PRINT("sigcleanup (UNDOCUMENTED)");
+}
+
+PRE(sys_sigprocmask)
+{
+ PRINT("sigprocmask ( %ld, %p, %p )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(long, "sigprocmask",
+ int, how, vki_sigset_t *, set, vki_sigset_t *, oldset);
+ if (ARG2 != 0)
+ PRE_MEM_READ( "sigprocmask(set)", ARG2, sizeof(vki_sigset_t));
+ if (ARG3 != 0)
+ PRE_MEM_WRITE( "sigprocmask(oldset)", ARG3, sizeof(vki_sigset_t));
+
+ SET_STATUS_from_SysRes(
+ VG_(do_sys_sigprocmask) ( tid, ARG1, (vki_sigset_t*)ARG2,
+ (vki_sigset_t*)ARG3 )
+ );
+
+ if (SUCCESS)
+ *flags |= SfPollAfter;
+}
+POST(sys_sigprocmask)
+{
+ vg_assert(SUCCESS);
+ if (RES == 0 && ARG3 != 0)
+ POST_MEM_WRITE( ARG3, sizeof(vki_sigset_t));
+}
+
+PRE(sys_socket)
+{
+ PRINT("socket ( %ld, %ld, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "socket", int, domain, int, type, int, protocol);
+}
+
+PRE(sys_statfs)
+{
+ PRINT("sys_statfs ( %p(%s), %p )",ARG1,ARG1,ARG2);
+ PRE_REG_READ2(long, "statfs", const char *, path, struct statfs *, buf);
+ PRE_MEM_RASCIIZ( "statfs(path)", ARG1 );
+ PRE_MEM_WRITE( "statfs(buf)", ARG2, sizeof(struct statfs) );
+}
+POST(sys_statfs)
+{
+ POST_MEM_WRITE( ARG2, sizeof(struct statfs) );
+}
+
+PRE(sys_statx)
+{
+ PRINT("statx ( %p(%s), %p, %ld, %ld )", ARG1,ARG1,ARG2,ARG3,ARG4);
+ PRE_MEM_RASCIIZ( "statx(file_name)", ARG1 );
+ PRE_REG_READ4(Word, "statx", UWord, fd, void*, buf,
+ UWord, len, UWord, cmd);
+ PRE_MEM_WRITE( "statx(buf)", ARG2, ARG3 );
+}
+POST(sys_statx)
+{
+ POST_MEM_WRITE( ARG2, ARG3 );
+}
+
+PRE(sys_symlink)
+{
+ PRINT("symlink (BOGUS HANDLER)");
+}
+
+PRE(sys_sys_parm)
+{
+ PRINT("sys_parm (%ld, %ld, %p)", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "sys_parm", int, cmd, int, cmdflag,
+ struct vario*, parmp);
+ /* this is a bit of a kludge, but if parmp has uninitialised areas
+ and we're doing SYSP_SET, lots of errors will be tiresomely
+ reported. Hence just ignore the definedness of the area and
+ only check addressability. */
+ PRE_MEM_WRITE( "sys_parm(parmp)", ARG3, sizeof(struct vario));
+}
+POST(sys_sys_parm)
+{
+ if (ARG1 == SYSP_GET)
+ POST_MEM_WRITE( ARG3, sizeof(struct vario) );
+}
+
+PRE(sys_sysconfig)
+{
+ PRINT("sysconfig ( %ld, %p, %ld )", ARG1, ARG2, ARG3);
+ PRE_REG_READ3(int, "sysconfig", int, cmd, void*, parmp, int, parmlen);
+ /* It may be that the area is read sometimes as well as written,
+ but for the same reasons as sys_parm, just check addressibility,
+ not definedness. */
+ PRE_MEM_WRITE( "sysconfig(parmp)", ARG2, ARG3 );
+}
+POST(sys_sysconfig)
+{
+ POST_MEM_WRITE( ARG2, ARG3 );
+}
+
+PRE(sys_thread_create)
+{
+ *flags |= SfMayBlock;
+ PRINT("thread_create ( )");
+}
+POST(sys_thread_create)
+{
+ vg_assert(SUCCESS);
+ if (0) VG_(printf)("new lwpid is %ld\n", RES);
+
+ /* Allocate a new thread slot (which sets it to VgTs_Init), and
+ record the lwpid in it, so can later find it again when handling
+ sys_thread_setstate for that lwpid. */
+
+ ThreadId ctid = VG_(alloc_ThreadState)();
+ ThreadState* ctst = VG_(get_ThreadState)(ctid);
+
+ vg_assert(ctst->status == VgTs_Init);
+
+ { /* Clear all os_state fields except for the vg stack ones, so any
+ existing stack gets reused. */
+ Addr v_s_b = ctst->os_state.valgrind_stack_base;
+ Addr v_s_i_SP = ctst->os_state.valgrind_stack_init_SP;
+ VG_(memset)(&ctst->os_state, 0, sizeof(ThreadOSstate));
+ ctst->os_state.valgrind_stack_base = v_s_b;
+ ctst->os_state.valgrind_stack_init_SP = v_s_i_SP;
+ }
+ ctst->os_state.lwpid = RES;
+}
+
+PRE(sys_thread_init)
+{
+ *flags |= SfMayBlock;
+ PRE_REG_READ2(long, "thread_init", long, arg1, long, arg2);
+ PRINT("thread_init (BOGUS HANDLER) ( %p, %p )", ARG1, ARG2);
+}
+
+PRE(sys_thread_kill)
+{
+ Int target_lwpid, my_lwpid;
+ PRINT("thread_kill ( %ld, %ld )", ARG1, ARG2);
+
+ if ( ((Word)ARG1) == (Word)(-1)
+ && ARG2 == VKI_SIGSEGV ) {
+ /* too difficult to continue; give up. */
+ ML_(aix5_set_threadstate_for_emergency_exit)
+ (tid, "exiting due to thread_kill(..,SIGSEGV) to process");
+ SET_STATUS_Success(0);
+ return;
+ }
+
+ /* Check to see if this kill gave us a pending signal */
+ *flags |= SfPollAfter;
+
+ target_lwpid = (Int)ARG1;
+ my_lwpid = VG_(gettid)();
+ /* we still hold the lock. Do deadlock-avoidance stuff. */
+ if (target_lwpid == my_lwpid) {
+ /* sending a signal to myself, which may be fatal. Therefore
+ drop the lock so that if the signal kills me, some other
+ thread can pick it up. */
+ *flags |= SfMayBlock;
+ } else {
+ /* sending a signal to some other thread, which may kill it;
+ therefore I'd better hold on to the lock to ensure that the
+ target doesn't get killed whilst holding it. */
+ }
+}
+
+/* thread_setmymask_fast is handled on a per platform basis */
+
+PRE(sys_thread_setmystate)
+{
+ *flags |= SfMayBlock;
+ /* args: struct tstate *, struct tstate *
+ I assume: first is new state, if not NULL.
+ Second is place to write the previous state, if not NULL.
+ (in the style of sigaction) */
+ PRINT("thread_setmystate (BOGUS HANDLER) ( %p, %p )",
+ ARG1, ARG2 );
+ PRE_REG_READ2(long, "thread_setmystate",
+ struct tstate *, newstate,
+ struct tstate *, oldstate );
+ if (ARG1)
+ PRE_MEM_READ( "thread_setmystate(arg1)", ARG1, sizeof(struct tstate) );
+ if (ARG2)
+ PRE_MEM_WRITE( "thread_setmystate(arg2)", ARG2, sizeof(struct tstate) );
+ if (1 && VG_(clo_trace_syscalls) && ARG1)
+ ML_(aix5debugstuff_show_tstate)(ARG1, "thread_setmystate (NEW)");
+
+ struct tstate* newts = (struct tstate*)ARG1;
+ struct tstate* oldts = (struct tstate*)ARG2;
+
+ /* Are we just messing with the signal mask? If so intercept it
+ and do it ourselves. Same idea as handling for
+ thread_setmymask_fast in 32-bit mode. */
+ if (newts && newts->flags == TSTATE_CHANGE_SIGMASK) {
+ vki_sigset_t* newset = newts ? (vki_sigset_t*)&newts->sigmask : NULL;
+ vki_sigset_t* oldset = oldts ? (vki_sigset_t*)&oldts->sigmask : NULL;
+ SET_STATUS_from_SysRes(
+ VG_(do_sys_sigprocmask) ( tid, VKI_SIG_SETMASK, newset, oldset )
+ );
+ *flags &= ~SfMayBlock;
+ return;
+ }
+}
+POST(sys_thread_setmystate)
+{
+ if (ARG2)
+ POST_MEM_WRITE( ARG2, sizeof(struct tstate) );
+ if (0 && VG_(clo_trace_syscalls) && ARG2)
+ ML_(aix5debugstuff_show_tstate)(ARG2, "thread_setmystate (OLD)");
+}
+
+PRE(sys_thread_setmystate_fast)
+{
+ UWord how = ARG1;
+ /* args: ?? */
+ PRINT("thread_setmystate_fast (BOGUS HANDLER)"
+ "(%p,%p(%s),%p(%s))",
+ ARG1,
+ ARG2, ML_(aix5debugstuff_pc_to_fnname)(ARG2),
+ ARG3, ML_(aix5debugstuff_pc_to_fnname)(ARG3)
+ );
+ PRE_REG_READ3(long, "thread_setmystate_fast",
+ long, arg1, long, arg2, long, arg3);
+ if (1 && VG_(clo_trace_syscalls))
+ ML_(aix5debugstuff_show_tstate_flags)( how );
+
+ if (how & TSTATE_CHANGE_FLAGS) {
+ /* Messing with cancellation type/state. Pay attention. */
+ Bool async = (how & TSTATE_CANCEL_DEFER) == 0;
+ Bool disabled = (how & TSTATE_CANCEL_DISABLE) > 0;
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(cancellation state -> %s %s)",
+ async ? "ASYNC" : "DEFER",
+ disabled ? "DISABLED" : " ENABLED");
+ tst->os_state.cancel_async = async;
+ tst->os_state.cancel_disabled = disabled;
+ /* If cancellation has been enabled for this thread and there is
+ a request outstanding, honour it now. */
+ if ((!disabled)
+ && tst->os_state.cancel_progress == Canc_Requested) {
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(honouring previous cancellation request)");
+ tst->os_state.cancel_progress = Canc_Actioned;
+ Bool ok = ML_(aix5_force_thread_into_pthread_exit)(tid);
+ if (!ok) {
+ /* now at serious risk of deadlock/livelock. Give up
+ rather than continue. */
+ ML_(aix5_set_threadstate_for_emergency_exit)
+ (tid, "pthread_cancel(case1): "
+ "cannot find pthread_exit; aborting");
+ SET_STATUS_Success(0);
+ return;
+ }
+ }
+ SET_STATUS_Success(0);
+ return;
+ }
+
+ /* In all other cases, hand to kernel. */
+ *flags |= SfMayBlock;
+}
+
+/* thread_setstate is handled in syswrap-ppc{32,64}-aix5.c. */
+
+PRE(sys_thread_terminate_unlock)
+{
+ ThreadState* tst;
+ /* simple; just make this thread exit */
+ PRINT("thread_terminate_unlock( %p )", ARG1);
+ PRE_REG_READ1(void, "thread_terminate_unlock", void*, exitcode);
+ tst = VG_(get_ThreadState)(tid);
+ /* Drop the lock we were holding, since we're not really going to
+ exit the host thread with thread_terminate_unlock. */
+ if (0) VG_(printf)("XXXXX dropping lock\n");
+ if (1) VG_(do_syscall1)(__NR_AIX5_thread_unlock, ARG1);
+ /* Set the thread's status to be exiting, then claim that the
+ syscall succeeded. */
+ tst->exitreason = VgSrc_ExitThread;
+ tst->os_state.exitcode = 0;
+ SET_STATUS_Success(0);
+}
+
+PRE(sys_thread_tsleep)
+{
+ *flags |= SfMayBlock;
+ PRINT("thread_tsleep (BOGUS HANDLER)( %ld, %p, %p, %p )",
+ ARG1, ARG2, ARG3, ARG4 );
+}
+
+PRE(sys_thread_tsleep_event)
+{
+ *flags |= SfMayBlock;
+ PRINT("thread_tsleep_event (UNDOCUMENTED)( %p, %p, %ld, %p )",
+ ARG1, ARG2, ARG3, ARG4 );
+}
+
+PRE(sys_thread_twakeup)
+{
+ *flags |= SfMayBlock;
+ PRINT("thread_twakeup (BOGUS HANDLER)( tid=%ld, val=%p )", ARG1, ARG2 );
+}
+
+PRE(sys_thread_twakeup_event)
+{
+ *flags |= SfMayBlock;
+ PRINT("thread_twakeup_event (BOGUS HANDLER)( %p, %ld, %ld )",
+ ARG1, ARG2, ARG3 );
+}
+
+PRE(sys_thread_unlock)
+{
+ *flags |= SfMayBlock;
+ PRINT("thread_unlock (BOGUS HANDLER)" );
+}
+
+PRE(sys_thread_waitlock)
+{
+ *flags |= SfMayBlock;
+ PRINT("thread_waitlock (BOGUS HANDLER)" );
+}
+
+PRE(sys_thread_waitlock_)
+{
+ *flags |= SfMayBlock;
+ PRINT("thread_waitlock_ (BOGUS HANDLER)" );
+}
+
+PRE(sys_times)
+{
+ PRINT("times ( %p )", ARG1);
+ PRE_REG_READ1(long, "times", struct tms *, buffer);
+ PRE_MEM_WRITE("times(buf)", ARG1, sizeof(struct tms) );
+}
+POST(sys_times)
+{
+ POST_MEM_WRITE( ARG1, sizeof(struct tms) );
+}
+
+PRE(sys_umask)
+{
+ PRINT("umask (BOGUS HANDLER)");
+}
+
+PRE(sys_uname)
+{
+ PRINT("uname (BOGUS HANDLER)");
+}
+
+PRE(sys_unlink)
+{
+ PRINT("unlink ( %p(%s) )", ARG1);
+ PRE_REG_READ1(int, "unlink", char*, path);
+ PRE_MEM_RASCIIZ( "unlink(path)", ARG1 );
+}
+
+PRE(sys_utimes)
+{
+ PRINT("utimes ( %p(%s), %p )", ARG1, ARG2);
+ PRE_REG_READ2(int, "utimes", char*, path, struct timeval*, times);
+ PRE_MEM_RASCIIZ( "utimes(path)", ARG1 );
+ PRE_MEM_READ( "utimes(times)", ARG2, 2 * sizeof(struct vki_timeval) );
+}
+
+PRE(sys_vmgetinfo)
+{
+ PRINT("vmgetinfo ( %p, %ld, %ld )", ARG1, ARG2, ARG3 );
+ PRE_REG_READ3(int, "vmgetinfo", void*, out, int, command, int, arg);
+ /* It looks like libc's vmgetinfo just hands stuff through to the
+ syscall. The man page says that the interpretation of ARG3(arg)
+ depends on ARG2(cmd); nevertheless in all cases basically this
+ writes the buffer (ARG1, ARG3). */
+ PRE_MEM_WRITE("vmgetinfo(buf)", ARG1, ARG3);
+}
+POST(sys_vmgetinfo)
+{
+ vg_assert(SUCCESS);
+ POST_MEM_WRITE(ARG1, ARG3);
+}
+
+PRE(sys_yield)
+{
+ *flags |= SfMayBlock;
+ PRINT("yield ( )");
+}
+
+#undef PRE
+#undef POST
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- Platform-specific syscalls stuff. syswrap-ppc32-aix5.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2006-2006 OpenWorks LLP
+ info@open-works.co.uk
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_vkiscnums.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_debuglog.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_options.h"
+#include "pub_core_scheduler.h"
+#include "pub_core_sigframe.h" // For VG_(sigframe_destroy)()
+#include "pub_core_signals.h"
+#include "pub_core_syscall.h"
+#include "pub_core_syswrap.h"
+#include "pub_core_tooliface.h"
+
+#include "priv_types_n_macros.h"
+#include "priv_syswrap-aix5.h" /* for decls of aix5-common wrappers */
+#include "priv_syswrap-main.h"
+
+
+/* --------- HACKS --------- */
+/* XXXXXXXXXXXX these HACKS are copies of stuff in syswrap-linux.c;
+ check for duplication. */
+/* HACK: is in syswrap-generic.c, but that doesn't get built on AIX. */
+/* Dump out a summary, and a more detailed list, of open file descriptors. */
+void VG_(show_open_fds) ( void )
+{
+ I_die_here;
+}
+static Bool i_am_the_only_thread ( void )
+{
+ Int c = VG_(count_living_threads)();
+ vg_assert(c >= 1); /* stay sane */
+ return c == 1;
+}
+void VG_(reap_threads)(ThreadId self)
+{
+ while (!i_am_the_only_thread()) {
+ /* Let other thread(s) run */
+ VG_(vg_yield)();
+ VG_(poll_signals)(self);
+ }
+ vg_assert(i_am_the_only_thread());
+}
+void VG_(init_preopened_fds) ( void )
+{
+ I_die_here;
+}
+
+
+// Run a thread from beginning to end and return the thread's
+// scheduler-return-code.
+static VgSchedReturnCode thread_wrapper(Word /*ThreadId*/ tidW)
+{
+ VgSchedReturnCode ret;
+ ThreadId tid = (ThreadId)tidW;
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+
+ VG_(debugLog)(1, "syswrap-aix32",
+ "thread_wrapper(tid=%lld): entry\n",
+ (ULong)tidW);
+
+ vg_assert(tst->status == VgTs_Init);
+
+ /* make sure we get the CPU lock before doing anything significant */
+ VG_(set_running)(tid, "thread_wrapper(starting new thread)");
+
+ if (0)
+ VG_(printf)("thread tid %d started: stack = %p\n",
+ tid, &tid);
+
+ VG_TRACK ( post_thread_create, tst->os_state.parent, tid );
+
+ tst->os_state.lwpid = VG_(gettid)();
+ tst->os_state.threadgroup = VG_(getpid)();
+
+ /* Thread created with all signals blocked; scheduler will set the
+ appropriate mask */
+ ret = VG_(scheduler)(tid);
+
+ vg_assert(VG_(is_exiting)(tid));
+
+ vg_assert(tst->status == VgTs_Runnable);
+ vg_assert(VG_(is_running_thread)(tid));
+
+ VG_(debugLog)(1, "syswrap-aix32",
+ "thread_wrapper(tid=%lld): exit\n",
+ (ULong)tidW);
+
+ /* Return to caller, still holding the lock. */
+ return ret;
+}
+
+
+/* Run a thread all the way to the end, then do appropriate exit actions
+ (this is the last-one-out-turn-off-the-lights bit). */
+static void run_a_thread_NORETURN ( Word tidW )
+{
+ ThreadId tid = (ThreadId)tidW;
+ VgSchedReturnCode src;
+ Int c;
+
+ VG_(debugLog)(1, "syswrap-aix32",
+ "run_a_thread_NORETURN(tid=%lld): pre-thread_wrapper\n",
+ (ULong)tidW);
+
+ /* Run the thread all the way through. */
+ src = thread_wrapper(tid);
+
+ VG_(debugLog)(1, "syswrap-aix32",
+ "run_a_thread_NORETURN(tid=%lld): post-thread_wrapper\n",
+ (ULong)tidW);
+
+ c = VG_(count_living_threads)();
+ vg_assert(c >= 1); /* stay sane */
+
+ vg_assert(src == VgSrc_ExitThread
+ || src == VgSrc_ExitProcess
+ || src == VgSrc_FatalSig);
+
+ if (c == 1 || src == VgSrc_ExitProcess) {
+
+ VG_(debugLog)(1, "syswrap-aix32",
+ "run_a_thread_NORETURN(tid=%lld): "
+ "exit process (%d threads remaining)\n",
+ (ULong)tidW, c);
+
+ /* We are the last one standing. Keep hold of the lock and
+ carry on to show final tool results, then exit the entire system.
+ Use the continuation pointer set at startup in m_main. */
+ ( * VG_(address_of_m_main_shutdown_actions_NORETURN) ) (tid, src);
+
+ } else {
+
+ ThreadState *tst;
+
+ VG_(debugLog)(1, "syswrap-aix32",
+ "run_a_thread_NORETURN(tid=%lld): "
+ "not last one standing\n",
+ (ULong)tidW);
+
+ /* OK, thread is dead, but others still exist. Just exit. */
+ vg_assert(c >= 2);
+ tst = VG_(get_ThreadState)(tid);
+
+ /* This releases the run lock */
+ VG_(exit_thread)(tid);
+ vg_assert(tst->status == VgTs_Zombie);
+
+ /* We have to use this sequence to terminate the thread to
+ prevent a subtle race. If VG_(exit_thread)() had left the
+ ThreadState as Empty, then it could have been reallocated,
+ reusing the stack while we're doing these last cleanups.
+ Instead, VG_(exit_thread) leaves it as Zombie to prevent
+ reallocation. We need to make sure we don't touch the stack
+ between marking it Empty and exiting. Hence the
+ assembler. */
+ { UInt block[4];
+ vg_assert(sizeof(tst->status == 4));
+ vg_assert(__NR_AIX5_thread_terminate
+ != __NR_AIX5_UNKNOWN);
+ block[0] = (UInt)VgTs_Empty;
+ block[1] = (UInt) & (tst->status);
+ block[2] = (UInt) tst->os_state.exitcode;
+ block[3] = __NR_AIX5_thread_terminate;
+ asm volatile (
+ "mr 29,%0\n\t" /* r29 = &block[0] */
+ "lwz 20, 0(29)\n\t" /* r20 = VgTs_Empty */
+ "lwz 21, 4(29)\n\t" /* r21 = & (tst->status) */
+ "lwz 22, 8(29)\n\t" /* r22 = tst->os_state.exitcode */
+ "lwz 23, 12(29)\n\t" /* r23 = __NR_exit */
+ /* after this point we can't safely use the stack. */
+ "stw 20, 0(21)\n\t" /* tst->status = VgTs_Empty */
+ "mr 2,23\n\t" /* r2 = __NR_exit */
+ "mr 3,22\n\t" /* set r3 = tst->os_state.exitcode */
+ /* set up for syscall */
+ ".long 0x48000005\n\t" /* "bl here+4" */
+ "mflr 29\n\t"
+ "addi 29,29,16\n\t"
+ "mtlr 29\n\t"
+ "sc\n\t" /* exit(tst->os_state.exitcode) */
+ :
+ : "b" (&block[0])
+ : "lr", "memory", "r2", "r3", "r20", "r21", "r22", "r23", "r29"
+ );
+ }
+
+ VG_(core_panic)("Thread exit failed?\n");
+ }
+
+ /*NOTREACHED*/
+ vg_assert(0);
+}
+
+
+static Word start_thread_NORETURN ( void* arg )
+{
+ ThreadState* tst = (ThreadState*)arg;
+ ThreadId tid = tst->tid;
+
+ run_a_thread_NORETURN ( (Word)tid );
+ /*NOTREACHED*/
+ vg_assert(0);
+}
+
+
+/* Call f(arg1), but first switch stacks, using 'stack' as the new
+ stack. f itself needs to never return. */
+__attribute__((noreturn))
+static
+void call_on_new_stack_0_1_NORETURN ( Addr stack,
+ void (*f_NORETURN)(Word),
+ Word arg1 )
+{
+ UWord* fdescr = (UWord*)f_NORETURN;
+ volatile UWord block[5];
+ block[0] = fdescr[0]; /* nia */
+ block[1] = stack; /* r1 */
+ block[2] = fdescr[1]; /* r2 */
+ block[3] = arg1; /* r3 */
+ block[4] = fdescr[2]; /* r11 */
+ __asm__ __volatile__(
+ "mr 4,%0\n\t" /* r4 = block */
+ "lwz 1,4(4)\n\t"
+ "lwz 2,8(4)\n\t"
+ "lwz 3,12(4)\n\t"
+ "lwz 11,16(4)\n\t"
+ "lwz 4,0(4)\n\t"
+ "mtctr 4\n\t"
+ "bctr\n"
+ : /*out*/ : /*in*/ "b"(&block[0])
+ );
+ /*NOTREACHED*/
+ __asm__ __volatile__("trap");
+ while (1) {} /* convince gcc that this really doesn't return */
+}
+
+
+/* Allocate a stack for the main thread, and run it all the way to the
+ end. Although we already have a working VgStack
+ (VG_(interim_stack)) it's better to allocate a new one, so that
+ overflow detection works uniformly for all threads.
+*/
+void VG_(main_thread_wrapper_NORETURN)(ThreadId tid)
+{
+ Addr sp;
+ VG_(debugLog)(1, "syswrap-aix32",
+ "entering VG_(main_thread_wrapper_NORETURN)\n");
+
+ sp = ML_(allocstack)(tid);
+
+ /* make a stack frame */
+ sp -= 16;
+ sp &= ~0xF;
+ *(UWord *)sp = 0;
+
+ /* If we can't even allocate the first thread's stack, we're hosed.
+ Give up. */
+ vg_assert2(sp != 0, "Cannot allocate main thread's stack.");
+
+ /* shouldn't be any other threads around yet */
+ vg_assert( VG_(count_living_threads)() == 1 );
+
+ call_on_new_stack_0_1_NORETURN(
+ (Addr)sp, /* stack */
+ run_a_thread_NORETURN, /* fn to call */
+ (Word)tid /* arg to give it */
+ );
+
+ /*NOTREACHED*/
+ vg_assert(0);
+}
+
+/* --------- end HACKS --------- */
+
+
+/* ---------------------------------------------------------------------
+ More thread stuff
+ ------------------------------------------------------------------ */
+
+void VG_(cleanup_thread) ( ThreadArchState* arch )
+{
+}
+
+
+/* ---------------------------------------------------------------------
+ PRE/POST wrappers for ppc32/AIX5-specific syscalls
+ ------------------------------------------------------------------ */
+
+/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
+#include <sys/thread.h>
+/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
+
+
+/* Add prototypes for the wrappers declared here, so that gcc doesn't
+ harass us for not having prototypes. Really this is a kludge --
+ the right thing to do is to make these wrappers 'static' since they
+ aren't visible outside this file, but that requires even more macro
+ magic. */
+
+#define PRE(name) DEFN_PRE_TEMPLATE(ppc32_aix5, name)
+#define POST(name) DEFN_POST_TEMPLATE(ppc32_aix5, name)
+
+DECL_TEMPLATE(ppc32_aix5, sys___loadx);
+DECL_TEMPLATE(ppc32_aix5, sys___unload);
+DECL_TEMPLATE(ppc32_aix5, sys_thread_setmymask_fast);
+DECL_TEMPLATE(ppc32_aix5, sys_thread_setstate);
+DECL_TEMPLATE(ppc32_aix5, sys_FAKE_SIGRETURN);
+
+
+PRE(sys___loadx)
+{
+ *flags |= SfMayBlock;
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_LOAD) {
+ PRINT("__loadx(0x%x(DL_LOAD),0x%x,%d,0x%x(%s),0x%x(%s))",
+ ARG1,ARG2,ARG3,
+ ARG4,ARG4,
+ ARG5, (ARG5 ? (HChar*)ARG5 : "nil") );
+ /* It would appear that (ARG2, ARG3) describe a buffer
+ which is written into by the kernel on success. */
+ PRE_MEM_WRITE("__loadx(DL_LOAD)(ARG2,ARG3)", ARG2, ARG3);
+ }
+ else
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_POSTLOADQ) {
+ PRINT("__loadx(0x%x(DL_POSTLOADQ),0x%x,%d,0x%x)",
+ ARG1,ARG2,ARG3,ARG4);
+ /* It would appear that (ARG2, ARG3) describe a buffer
+ which is written into by the kernel on success. */
+ PRE_MEM_WRITE("__loadx(DL_POSTLOADQ)(ARG2,ARG3)", ARG2, ARG3);
+ }
+ else
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_GLOBALSYM) {
+ PRINT("__loadx(0x%x(DL_GLOBALSYM),0x%x(%s),0x%x,0x%x,0x%x)",
+ ARG1, ARG2,ARG2,
+ ARG3, ARG4, ARG5);
+ }
+ else
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_EXITQ) {
+ PRINT("__loadx(0x%x(DL_EXITQ),0x%x,%d)", ARG1, ARG2, ARG3);
+ PRE_MEM_WRITE("__loadx(DL_EXITQ)(ARG2,ARG3)", ARG2, ARG3);
+ }
+ else
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_EXECQ) {
+ PRINT("__loadx(0x%x(DL_EXECQ),0x%x,%d)", ARG1, ARG2, ARG3);
+ PRE_MEM_WRITE("__loadx(DL_EXECQ)(ARG2,ARG3)", ARG2, ARG3);
+ }
+ else
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_GETSYM) {
+ PRINT("__loadx(0x%x(DL_GETSYM),0x%x(%s),%d,0x%x)",
+ ARG1, ARG2,ARG2, ARG3, ARG4);
+ }
+ else
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_PREUNLOADQ) {
+ PRINT("__loadx(0x%x(DL_PREUNLOADQ),0x%x,%d,0x%x)",
+ ARG1,ARG2,ARG3,ARG4);
+ PRE_MEM_WRITE("__loadx(DL_PREUNLOADQ)(ARG2,ARG3)", ARG2, ARG3);
+ }
+ else
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == 0x0D000000) {
+ PRINT("__loadx(0x%x(UNDOCUMENTED),0x%x,0x%xd,0x%x)",
+ ARG1,ARG2,ARG3,ARG4);
+ /* This doesn't appear to have any args, from the examples I've
+ seen. */
+ }
+ else {
+ PRINT("__loadx (BOGUS HANDLER) (0x%x, ..)", ARG1);
+ }
+}
+POST(sys___loadx)
+{
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_PREUNLOADQ
+ && SUCCESS) {
+ POST_MEM_WRITE(ARG2, ARG3);
+ }
+ else
+
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_POSTLOADQ
+ && SUCCESS) {
+ POST_MEM_WRITE(ARG2, ARG3);
+ }
+ else
+
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_EXITQ
+ && SUCCESS) {
+ POST_MEM_WRITE(ARG2, ARG3);
+ }
+ else
+
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_EXECQ
+ && SUCCESS) {
+ POST_MEM_WRITE(ARG2, ARG3);
+ }
+ else
+
+ if ((ARG1 & VKI_DL_FUNCTION_MASK) == VKI_DL_LOAD
+ && SUCCESS) {
+
+ /* See comment in pre-handler */
+ POST_MEM_WRITE(ARG2, ARG3);
+
+ /* A module load succeeded. Tell m_debuginfo, m_transtab, and
+ the tool. */
+ ML_(aix5_rescan_procmap_after_load_or_unload)();
+ }
+}
+
+PRE(sys___unload)
+{
+ PRINT("__unload (UNDOCUMENTED) ( %p )", ARG1);
+}
+POST(sys___unload)
+{
+ /* A module unload succeeded. Tell m_debuginfo, m_transtab, and the
+ tool. */
+ ML_(aix5_rescan_procmap_after_load_or_unload)();
+}
+
+PRE(sys_thread_setmymask_fast)
+{
+ /* args: a 64-bit signal mask in ARG1/2.*/
+ /* On the assumption that this simply sets the thread's signal
+ mask, we act like sigprocmask(SIG_SETMASK, set, NULL) and don't
+ hand this to the kernel. Layout verified 30 July 06. */
+ vki_sigset_t set;
+ PRINT("thread_setmymask_fast (BOGUS HANDLER)( %08x %08x )", ARG1,ARG2 );
+ vg_assert(sizeof(vki_sigset_t) == 8);
+ set.sig[0] = ARG1; /* sigs 1-32 */
+ set.sig[1] = ARG2; /* sigs 32-64 */
+ SET_STATUS_from_SysRes(
+ VG_(do_sys_sigprocmask) ( tid, VKI_SIG_SETMASK, &set, NULL )
+ );
+}
+
+PRE(sys_thread_setstate)
+{
+ UWord dst_lwpid = (UWord)ARG1;
+ struct tstate* ats_new = (struct tstate*)ARG2;
+ struct tstate* ats_old = (struct tstate*)ARG3;
+ ThreadId dst_tid = VG_INVALID_THREADID;
+ ThreadState* dst_ts = NULL;
+ Int i;
+
+ /* Arrgh. We MUST retain the lock during this syscall. Reason is
+ that this is sometimes used for asynchronous thread cancellation
+ (nuking other threads). If we don't have the lock during the
+ syscall, then it's possible that the thread we're nuking might
+ get the lock before it gets killed off, and so we can never
+ re-acquire the lock after this syscall, and the system
+ deadlocks. */
+
+ /* 10 July 06: above comment is a misdiagnosis. It appears that
+ for thread cancellation (that is, with ->flags == TSTATE_INTR)
+ the target thread is has its PC changed by the the kernel to
+ something else, possibly to pthread_exit(), so that it can run
+ its cancellation handlers and exit. Currently is unknown how
+ the kernel knows what to set the target thread's PC to. I did
+ establish that all the other data passed in the struct is not
+ relevant: when ->flags == TSTATE_INTR, all the other words can
+ be set to 0x0 or 0xFFFFFFFF and the syscall still works. So the
+ address is not passed like that. Also I looked at args to
+ thread_setmystate_fast, which is used when a thread sets its
+ cancellation state, but none of those are code addresses.
+
+ Also, it's ok for the kernel to simply change the target
+ thread's PC to something else for async thread cancellation, but
+ for deferred cancellation something else is needed, and I can't
+ see how that would work either.
+
+ Anyway, net result is, target thread ends up not running on the
+ simulator (not dead), which is why it's necessary to hold onto
+ the lock at this point. */
+
+ /* 30 July 06: added kludge to intercept attempts to cancel another
+ thread and instead just force that thread to run
+ pthread_exit(PTHREAD_CANCELED). This allows V to keep
+ control. */
+
+ PRINT("thread_setstate (BOGUS HANDLER) "
+ "( %ld, %p,%p )", dst_lwpid, ats_new, ats_old);
+ if (1 && VG_(clo_trace_syscalls) && ats_new)
+ ML_(aix5debugstuff_show_tstate)((Addr)ats_new,
+ "thread_setstate (NEW)");
+
+ /* Intercept and handle ourselves any attempts to cancel
+ another thread (including this one). */
+
+ if (ats_new && (!ats_old) && ats_new->flags == TSTATE_INTR) {
+ dst_ts = NULL;
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(INTR for lwpid %ld)", dst_lwpid);
+ dst_tid = VG_INVALID_THREADID;
+ for (i = 0; i < VG_N_THREADS; i++) {
+ dst_ts = VG_(get_ThreadState)(i);
+ if ((dst_ts->status == VgTs_Runnable
+ || dst_ts->status == VgTs_Yielding
+ || dst_ts->status == VgTs_WaitSys)
+ && dst_ts->os_state.lwpid == dst_lwpid) {
+ dst_tid = i;
+ break;
+ }
+ }
+ if (VG_(clo_trace_syscalls)) {
+ if (dst_tid == VG_INVALID_THREADID)
+ VG_(printf)("(== unknown tid)");
+ else
+ VG_(printf)("(== tid %d)", (Int)dst_tid);
+ }
+ if (dst_tid != VG_INVALID_THREADID) {
+ /* A cancel has been requested for ctid. If the target
+ thread has cancellation enabled, honour it right now. If
+ not, mark the thread as having a cancellation request, so
+ that if it later enables cancellation then the
+ cancellation will take effect. */
+ vg_assert(dst_ts);
+ if (dst_ts->os_state.cancel_progress == Canc_NoRequest) {
+ if (dst_ts->os_state.cancel_disabled) {
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(target has cancel disabled"
+ "; request lodged)");
+ dst_ts->os_state.cancel_progress = Canc_Requested;
+ } else {
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(forcing target into pthread_exit)");
+ dst_ts->os_state.cancel_progress = Canc_Actioned;
+ Bool ok = ML_(aix5_force_thread_into_pthread_exit)(dst_tid);
+ if (!ok) {
+ /* now at serious risk of deadlock/livelock. Give up
+ rather than continue. */
+ ML_(aix5_set_threadstate_for_emergency_exit)
+ (tid, "pthread_cancel(case2-32): "
+ "cannot find pthread_exit; aborting");
+ SET_STATUS_Success(0);
+ return;
+ }
+ }
+ }
+ SET_STATUS_Success(0);
+ return;
+ }
+ }
+
+ /* Well, it's not a cancellation request. Maybe it is the
+ initialisation of a previously created thread? */
+
+ if (ats_new && !ats_old) {
+ dst_tid = VG_INVALID_THREADID;
+ for (i = 0; i < VG_N_THREADS; i++) {
+ dst_ts = VG_(get_ThreadState)(i);
+ if (dst_ts->status == VgTs_Init
+ && dst_ts->os_state.lwpid == dst_lwpid) {
+ dst_tid = i;
+ break;
+ }
+ }
+ if (dst_tid != VG_INVALID_THREADID) {
+ /* Found the associated child */
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(initialised child tid %d)", (Int)dst_tid);
+ dst_ts = VG_(get_ThreadState)(dst_tid);
+ UWord* stack = (UWord*)ML_(allocstack)(dst_tid);
+ /* XXX TODO: check allocstack failure */
+
+ /* copy the specified child register state into the guest
+ slot (we need that context to run on the simulated CPU,
+ not the real one) and put pointers to our own
+ run-the-simulator function into what we'll hand off to the
+ kernel instead. */
+
+ /* The guest thread is to start running whatever context
+ this syscall showed up with. */
+ dst_ts->arch.vex.guest_GPR0 = ats_new->mst.gpr[0];
+ dst_ts->arch.vex.guest_GPR1 = ats_new->mst.gpr[1]; /* sp */
+ dst_ts->arch.vex.guest_GPR2 = ats_new->mst.gpr[2]; /* toc */
+ dst_ts->arch.vex.guest_GPR3 = ats_new->mst.gpr[3]; /* initarg */
+ dst_ts->arch.vex.guest_GPR4 = ats_new->mst.gpr[4];
+ dst_ts->arch.vex.guest_GPR5 = ats_new->mst.gpr[5];
+ dst_ts->arch.vex.guest_GPR6 = ats_new->mst.gpr[6];
+ dst_ts->arch.vex.guest_GPR7 = ats_new->mst.gpr[7];
+ dst_ts->arch.vex.guest_GPR8 = ats_new->mst.gpr[8];
+ dst_ts->arch.vex.guest_GPR9 = ats_new->mst.gpr[9];
+ dst_ts->arch.vex.guest_GPR10 = ats_new->mst.gpr[10];
+ dst_ts->arch.vex.guest_GPR11 = ats_new->mst.gpr[11]; /* ?? */
+ dst_ts->arch.vex.guest_GPR12 = ats_new->mst.gpr[12];
+ dst_ts->arch.vex.guest_GPR13 = ats_new->mst.gpr[13];
+ dst_ts->arch.vex.guest_GPR14 = ats_new->mst.gpr[14];
+ dst_ts->arch.vex.guest_GPR15 = ats_new->mst.gpr[15];
+ dst_ts->arch.vex.guest_GPR16 = ats_new->mst.gpr[16];
+ dst_ts->arch.vex.guest_GPR17 = ats_new->mst.gpr[17];
+ dst_ts->arch.vex.guest_GPR18 = ats_new->mst.gpr[18];
+ dst_ts->arch.vex.guest_GPR19 = ats_new->mst.gpr[19];
+ dst_ts->arch.vex.guest_GPR20 = ats_new->mst.gpr[20];
+ dst_ts->arch.vex.guest_GPR21 = ats_new->mst.gpr[21];
+ dst_ts->arch.vex.guest_GPR22 = ats_new->mst.gpr[22];
+ dst_ts->arch.vex.guest_GPR23 = ats_new->mst.gpr[23];
+ dst_ts->arch.vex.guest_GPR24 = ats_new->mst.gpr[24];
+ dst_ts->arch.vex.guest_GPR25 = ats_new->mst.gpr[25];
+ dst_ts->arch.vex.guest_GPR26 = ats_new->mst.gpr[26];
+ dst_ts->arch.vex.guest_GPR27 = ats_new->mst.gpr[27];
+ dst_ts->arch.vex.guest_GPR28 = ats_new->mst.gpr[28];
+ dst_ts->arch.vex.guest_GPR29 = ats_new->mst.gpr[29];
+ dst_ts->arch.vex.guest_GPR30 = ats_new->mst.gpr[30];
+ dst_ts->arch.vex.guest_GPR31 = ats_new->mst.gpr[31];
+ dst_ts->arch.vex.guest_CIA = ats_new->mst.iar; /* pc */
+ dst_ts->arch.vex.guest_LR = ats_new->mst.lr;
+ dst_ts->arch.vex.guest_CTR = ats_new->mst.ctr;
+ LibVEX_GuestPPC32_put_CR( ats_new->mst.cr, &dst_ts->arch.vex );
+ LibVEX_GuestPPC32_put_XER( ats_new->mst.xer, &dst_ts->arch.vex );
+
+ /* Record what seems like the highest legitimate stack
+ address for this thread, so that the stack unwinder works
+ properly. It seems reasonable to use the R1 value
+ supplied here. */
+ dst_ts->client_stack_highest_word = dst_ts->arch.vex.guest_GPR1;
+
+ /* The host thread is to start running
+ start_thread_NORETURN */
+ UWord* wrapper_fdescr = (UWord*) & start_thread_NORETURN;
+ ats_new->mst.gpr[1] = (UWord)stack;
+ ats_new->mst.gpr[2] = wrapper_fdescr[1];
+ ats_new->mst.iar = wrapper_fdescr[0];
+ ats_new->mst.gpr[3] = (UWord)dst_ts;
+
+ /* Set initial cancellation status for the thread. */
+ dst_ts->os_state.cancel_async = False;
+ dst_ts->os_state.cancel_disabled = False;
+ dst_ts->os_state.cancel_progress = Canc_NoRequest;
+ }
+ }
+}
+POST(sys_thread_setstate)
+{
+ if (ARG3)
+ POST_MEM_WRITE( ARG3, sizeof(struct tstate) );
+ if (0 && VG_(clo_trace_syscalls) && ARG3)
+ ML_(aix5debugstuff_show_tstate)(ARG3, "thread_setstate (OLD)");
+}
+
+PRE(sys_FAKE_SIGRETURN)
+{
+ ThreadState* tst;
+ /* This handles the fake signal-return system call created by
+ sigframe-ppc32-aix5.c. */
+ PRINT("FAKE_SIGRETURN ( )");
+
+ vg_assert(VG_(is_valid_tid)(tid));
+ vg_assert(tid >= 1 && tid < VG_N_THREADS);
+ vg_assert(VG_(is_running_thread)(tid));
+
+ /* Remove the signal frame from this thread's (guest) stack,
+ in the process restoring the pre-signal guest state. */
+ VG_(sigframe_destroy)(tid, True);
+
+ /* Now the pre-signal registers are restored. Unfortunately the
+ syscall driver logic will want to copy back the syscall result
+ (not that there is one) into guest r3/r4. So we'd better cook
+ up a syscall result which, when copied back, makes no change. */
+ tst = VG_(get_ThreadState)(tid);
+ SET_STATUS_from_SysRes(
+ VG_(mk_SysRes_ppc32_aix5)(
+ tst->arch.vex.guest_GPR3,
+ tst->arch.vex.guest_GPR4
+ )
+ );
+}
+
+
+/* ---------------------------------------------------------------------
+ The ppc32/AIX5 syscall table
+ ------------------------------------------------------------------ */
+
+typedef
+ struct {
+ UInt* pSysNo;
+ SyscallTableEntry wrappers;
+ }
+ AIX5SCTabEntry;
+
+#undef PLAXY
+#undef PLAX_
+
+#define PLAXY(sysno, name) \
+ { & sysno, \
+ { & WRAPPER_PRE_NAME(ppc32_aix5, name), \
+ & WRAPPER_POST_NAME(ppc32_aix5, name) }}
+
+#define PLAX_(sysno, name) \
+ { & sysno, \
+ { & WRAPPER_PRE_NAME(ppc32_aix5, name), \
+ NULL }}
+
+static /* but not const */
+AIX5SCTabEntry aix5_ppc32_syscall_table[]
+= {
+ AIXXY(__NR_AIX5___libc_sbrk, sys___libc_sbrk),
+ PLAXY(__NR_AIX5___loadx, sys___loadx),
+ AIXX_(__NR_AIX5___msleep, sys___msleep),
+ PLAXY(__NR_AIX5___unload, sys___unload),
+ AIXX_(__NR_AIX5__clock_gettime, sys__clock_gettime),
+ AIXX_(__NR_AIX5__clock_settime, sys__clock_settime),
+ AIXX_(__NR_AIX5__exit, sys__exit),
+ AIXX_(__NR_AIX5__fp_fpscrx_sc, sys__fp_fpscrx_sc),
+ AIXX_(__NR_AIX5__getpgrp, sys__getpgrp),
+ AIXX_(__NR_AIX5__getpid, sys__getpid),
+ AIXX_(__NR_AIX5__getppid, sys__getppid),
+ AIXX_(__NR_AIX5__getpriority, sys__getpriority),
+ AIXXY(__NR_AIX5__nsleep, sys__nsleep),
+ AIXX_(__NR_AIX5__pause, sys__pause),
+ AIXXY(__NR_AIX5__poll, sys__poll),
+ AIXX_(__NR_AIX5__select, sys__select),
+ AIXX_(__NR_AIX5__sem_wait, sys__sem_wait),
+ AIXX_(__NR_AIX5__setpgid, sys__setpgid),
+ AIXX_(__NR_AIX5__setsid, sys__setsid),
+ AIXXY(__NR_AIX5__sigaction, sys__sigaction),
+ AIXX_(__NR_AIX5__thread_self, sys__thread_self),
+ AIXX_(__NR_AIX5__thread_setsched, sys__thread_setsched),
+ AIXX_(__NR_AIX5_access, sys_access),
+ AIXX_(__NR_AIX5_accessx, sys_accessx),
+ AIXXY(__NR_AIX5_appgetrlimit, sys_appgetrlimit),
+ AIXXY(__NR_AIX5_appgetrusage, sys_appgetrusage),
+ AIXX_(__NR_AIX5_apprestimer, sys_apprestimer),
+ AIXX_(__NR_AIX5_appsetrlimit, sys_appsetrlimit),
+ AIXX_(__NR_AIX5_appulimit, sys_appulimit),
+ AIXX_(__NR_AIX5_bind, sys_bind),
+ AIXX_(__NR_AIX5_chdir, sys_chdir),
+ AIXX_(__NR_AIX5_chmod, sys_chmod),
+ AIXX_(__NR_AIX5_chown, sys_chown),
+ AIXX_(__NR_AIX5_close, sys_close),
+ AIXX_(__NR_AIX5_connext, sys_connext),
+ AIXX_(__NR_AIX5_execve, sys_execve),
+ AIXXY(__NR_AIX5_finfo, sys_finfo),
+ AIXXY(__NR_AIX5_fstatx, sys_fstatx),
+ AIXX_(__NR_AIX5_fsync, sys_fsync),
+ AIXXY(__NR_AIX5_getdirent, sys_getdirent),
+ AIXXY(__NR_AIX5_getdirent64, sys_getdirent64),
+ AIXXY(__NR_AIX5_getdomainname, sys_getdomainname),
+ AIXX_(__NR_AIX5_getgidx, sys_getgidx),
+ AIXXY(__NR_AIX5_getgroups, sys_getgroups),
+ AIXXY(__NR_AIX5_gethostname, sys_gethostname),
+ AIXXY(__NR_AIX5_getpriv, sys_getpriv),
+ AIXXY(__NR_AIX5_getprocs, sys_getprocs),
+ AIXX_(__NR_AIX5_getrpid, sys_getrpid),
+ AIXXY(__NR_AIX5_getsockopt, sys_getsockopt),
+ AIXX_(__NR_AIX5_gettimerid, sys_gettimerid),
+ AIXX_(__NR_AIX5_getuidx, sys_getuidx),
+ AIXXY(__NR_AIX5_incinterval, sys_incinterval),
+ AIXXY(__NR_AIX5_kfcntl, sys_kfcntl),
+ AIXX_(__NR_AIX5_kfork, sys_kfork),
+ AIXX_(__NR_AIX5_kftruncate, sys_kftruncate),
+ AIXX_(__NR_AIX5_kgetsidx, sys_kgetsidx),
+ AIXX_(__NR_AIX5_kill, sys_kill),
+ AIXXY(__NR_AIX5_kioctl, sys_kioctl),
+ AIXX_(__NR_AIX5_klseek, sys_klseek),
+ AIXX_(__NR_AIX5_knlist, sys_knlist),
+ AIXXY(__NR_AIX5_kpread, sys_kpread),
+ AIXXY(__NR_AIX5_kread, sys_kread),
+ AIXXY(__NR_AIX5_kreadv, sys_kreadv),
+ AIXX_(__NR_AIX5_kthread_ctl, sys_kthread_ctl),
+ AIXXY(__NR_AIX5_kwaitpid, sys_kwaitpid),
+ AIXX_(__NR_AIX5_kwrite, sys_kwrite),
+ AIXX_(__NR_AIX5_kwritev, sys_kwritev),
+ AIXX_(__NR_AIX5_listen, sys_listen),
+ AIXX_(__NR_AIX5_loadbind, sys_loadbind),
+ AIXXY(__NR_AIX5_loadquery, sys_loadquery),
+ AIXX_(__NR_AIX5_lseek, sys_lseek),
+ AIXX_(__NR_AIX5_mkdir, sys_mkdir),
+ AIXXY(__NR_AIX5_mmap, sys_mmap),
+ AIXXY(__NR_AIX5_mprotect, sys_mprotect),
+ AIXXY(__NR_AIX5_munmap, sys_munmap),
+ AIXXY(__NR_AIX5_naccept, sys_naccept),
+ AIXXY(__NR_AIX5_ngetpeername, sys_ngetpeername),
+ AIXXY(__NR_AIX5_ngetsockname, sys_ngetsockname),
+ AIXXY(__NR_AIX5_nrecvfrom, sys_nrecvfrom),
+ AIXX_(__NR_AIX5_nrecvmsg, sys_nrecvmsg),
+ AIXX_(__NR_AIX5_nsendmsg, sys_nsendmsg),
+ AIXX_(__NR_AIX5_open, sys_open),
+ AIXXY(__NR_AIX5_pipe, sys_pipe),
+ AIXX_(__NR_AIX5_privcheck, sys_privcheck),
+ AIXXY(__NR_AIX5_readlink, sys_readlink),
+ AIXXY(__NR_AIX5_recv, sys_recv),
+ AIXX_(__NR_AIX5_rename, sys_rename),
+ AIXXY(__NR_AIX5_sbrk, sys_sbrk),
+ AIXX_(__NR_AIX5_sched_get_priority_max, sys_sched_get_priority_max),
+ AIXX_(__NR_AIX5_sem_destroy, sys_sem_destroy),
+ AIXXY(__NR_AIX5_sem_init, sys_sem_init),
+ AIXXY(__NR_AIX5_sem_post, sys_sem_post),
+ AIXX_(__NR_AIX5_send, sys_send),
+ AIXX_(__NR_AIX5_setgid, sys_setgid),
+ AIXX_(__NR_AIX5_setsockopt, sys_setsockopt),
+ AIXX_(__NR_AIX5_setuid, sys_setuid),
+ AIXXY(__NR_AIX5_shmat, sys_shmat),
+ AIXXY(__NR_AIX5_shmctl, sys_shmctl),
+ AIXXY(__NR_AIX5_shmdt, sys_shmdt),
+ AIXX_(__NR_AIX5_shmget, sys_shmget),
+ AIXX_(__NR_AIX5_shutdown, sys_shutdown),
+ AIXX_(__NR_AIX5_sigcleanup, sys_sigcleanup),
+ AIXXY(__NR_AIX5_sigprocmask, sys_sigprocmask),
+ AIXX_(__NR_AIX5_socket, sys_socket),
+ AIXXY(__NR_AIX5_statx, sys_statx),
+ AIXXY(__NR_AIX5_statfs, sys_statfs),
+ AIXX_(__NR_AIX5_symlink, sys_symlink),
+ AIXXY(__NR_AIX5_sys_parm, sys_sys_parm),
+ AIXXY(__NR_AIX5_sysconfig, sys_sysconfig),
+ AIXXY(__NR_AIX5_thread_create, sys_thread_create),
+ AIXX_(__NR_AIX5_thread_init, sys_thread_init),
+ AIXX_(__NR_AIX5_thread_kill, sys_thread_kill),
+ PLAX_(__NR_AIX5_thread_setmymask_fast, sys_thread_setmymask_fast),
+ AIXXY(__NR_AIX5_thread_setmystate, sys_thread_setmystate),
+ AIXX_(__NR_AIX5_thread_setmystate_fast, sys_thread_setmystate_fast),
+ PLAXY(__NR_AIX5_thread_setstate, sys_thread_setstate),
+ AIXX_(__NR_AIX5_thread_terminate_unlock, sys_thread_terminate_unlock),
+ AIXX_(__NR_AIX5_thread_tsleep, sys_thread_tsleep),
+ AIXX_(__NR_AIX5_thread_tsleep_event, sys_thread_tsleep_event),
+ AIXX_(__NR_AIX5_thread_twakeup, sys_thread_twakeup),
+ AIXX_(__NR_AIX5_thread_twakeup_event, sys_thread_twakeup_event),
+ AIXX_(__NR_AIX5_thread_unlock, sys_thread_unlock),
+ AIXX_(__NR_AIX5_thread_waitlock, sys_thread_waitlock),
+ AIXX_(__NR_AIX5_thread_waitlock_, sys_thread_waitlock_),
+ AIXXY(__NR_AIX5_times, sys_times),
+ AIXX_(__NR_AIX5_umask, sys_umask),
+ AIXX_(__NR_AIX5_uname, sys_uname),
+ AIXX_(__NR_AIX5_unlink, sys_unlink),
+ AIXX_(__NR_AIX5_utimes, sys_utimes),
+ AIXXY(__NR_AIX5_vmgetinfo, sys_vmgetinfo),
+ AIXX_(__NR_AIX5_yield, sys_yield),
+ PLAX_(__NR_AIX5_FAKE_SIGRETURN, sys_FAKE_SIGRETURN)
+ };
+
+SyscallTableEntry* ML_(get_ppc32_aix5_syscall_entry) ( UInt sysno )
+{
+ Int i;
+ AIX5SCTabEntry tmp;
+
+ const Int tab_size = sizeof(aix5_ppc32_syscall_table)
+ / sizeof(aix5_ppc32_syscall_table[0]);
+
+ for (i = 0; i < tab_size; i++)
+ if (sysno == *(aix5_ppc32_syscall_table[i].pSysNo))
+ break;
+
+ vg_assert(i >= 0 && i <= tab_size);
+ if (i == tab_size)
+ return NULL; /* can't find a wrapper */
+
+ /* Move found one a bit closer to the front, so as to
+ make future searches cheaper. */
+ if (i > 0) {
+ tmp = aix5_ppc32_syscall_table[i-1];
+ aix5_ppc32_syscall_table[i-1] = aix5_ppc32_syscall_table[i];
+ aix5_ppc32_syscall_table[i] = tmp;
+ i--;
+ }
+
+ vg_assert(i >= 0 && i < tab_size);
+ return &aix5_ppc32_syscall_table[i].wrappers;
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- Platform-specific syscalls stuff. syswrap-ppc64-aix5.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2006-2006 OpenWorks LLP
+ info@open-works.co.uk
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_vkiscnums.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_debuglog.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_options.h"
+#include "pub_core_scheduler.h"
+#include "pub_core_sigframe.h" // For VG_(sigframe_destroy)()
+#include "pub_core_signals.h"
+#include "pub_core_syscall.h"
+#include "pub_core_syswrap.h"
+#include "pub_core_tooliface.h"
+
+#include "priv_types_n_macros.h"
+#include "priv_syswrap-aix5.h" /* for decls of aix5-common wrappers */
+#include "priv_syswrap-main.h"
+
+
+/* --------- HACKS --------- */
+/* XXXXXXXXXXXX these HACKS are copies of stuff in syswrap-linux.c;
+ check for duplication. */
+/* HACK: is in syswrap-generic.c, but that doesn't get build on AIX. */
+/* Dump out a summary, and a more detailed list, of open file descriptors. */
+void VG_(show_open_fds) ( void )
+{
+ I_die_here;
+}
+static Bool i_am_the_only_thread ( void )
+{
+ Int c = VG_(count_living_threads)();
+ vg_assert(c >= 1); /* stay sane */
+ return c == 1;
+}
+void VG_(reap_threads)(ThreadId self)
+{
+ while (!i_am_the_only_thread()) {
+ /* Let other thread(s) run */
+ VG_(vg_yield)();
+ VG_(poll_signals)(self);
+ }
+ vg_assert(i_am_the_only_thread());
+}
+void VG_(init_preopened_fds) ( void )
+{
+ I_die_here;
+}
+
+
+// Run a thread from beginning to end and return the thread's
+// scheduler-return-code.
+static VgSchedReturnCode thread_wrapper(Word /*ThreadId*/ tidW)
+{
+ VgSchedReturnCode ret;
+ ThreadId tid = (ThreadId)tidW;
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+
+ VG_(debugLog)(1, "syswrap-aix64",
+ "thread_wrapper(tid=%lld): entry\n",
+ (ULong)tidW);
+
+ vg_assert(tst->status == VgTs_Init);
+
+ /* make sure we get the CPU lock before doing anything significant */
+ VG_(set_running)(tid, "thread_wrapper(starting new thread)");
+
+ if (0)
+ VG_(printf)("thread tid %d started: stack = %p\n",
+ tid, &tid);
+
+ VG_TRACK ( post_thread_create, tst->os_state.parent, tid );
+
+ tst->os_state.lwpid = VG_(gettid)();
+ tst->os_state.threadgroup = VG_(getpid)();
+
+ /* Thread created with all signals blocked; scheduler will set the
+ appropriate mask */
+ ret = VG_(scheduler)(tid);
+
+ vg_assert(VG_(is_exiting)(tid));
+
+ vg_assert(tst->status == VgTs_Runnable);
+ vg_assert(VG_(is_running_thread)(tid));
+
+ VG_(debugLog)(1, "syswrap-aix64",
+ "thread_wrapper(tid=%lld): exit\n",
+ (ULong)tidW);
+
+ /* Return to caller, still holding the lock. */
+ return ret;
+}
+
+
+/* Run a thread all the way to the end, then do appropriate exit actions
+ (this is the last-one-out-turn-off-the-lights bit). */
+static void run_a_thread_NORETURN ( Word tidW )
+{
+ ThreadId tid = (ThreadId)tidW;
+ VgSchedReturnCode src;
+ Int c;
+
+ VG_(debugLog)(1, "syswrap-aix64",
+ "run_a_thread_NORETURN(tid=%lld): pre-thread_wrapper\n",
+ (ULong)tidW);
+
+ /* Run the thread all the way through. */
+ src = thread_wrapper(tid);
+
+ VG_(debugLog)(1, "syswrap-aix64",
+ "run_a_thread_NORETURN(tid=%lld): post-thread_wrapper\n",
+ (ULong)tidW);
+
+ c = VG_(count_living_threads)();
+ vg_assert(c >= 1); /* stay sane */
+
+ vg_assert(src == VgSrc_ExitThread
+ || src == VgSrc_ExitProcess
+ || src == VgSrc_FatalSig);
+
+ if (c == 1 || src == VgSrc_ExitProcess) {
+
+ VG_(debugLog)(1, "syswrap-aix64",
+ "run_a_thread_NORETURN(tid=%lld): "
+ "exit process (%d threads remaining)\n",
+ (ULong)tidW, c);
+
+ /* We are the last one standing. Keep hold of the lock and
+ carry on to show final tool results, then exit the entire system.
+ Use the continuation pointer set at startup in m_main. */
+ ( * VG_(address_of_m_main_shutdown_actions_NORETURN) ) (tid, src);
+
+ } else {
+
+ ThreadState *tst;
+
+ VG_(debugLog)(1, "syswrap-aix64",
+ "run_a_thread_NORETURN(tid=%lld): "
+ "not last one standing\n",
+ (ULong)tidW);
+
+ /* OK, thread is dead, but others still exist. Just exit. */
+ vg_assert(c >= 2);
+ tst = VG_(get_ThreadState)(tid);
+
+ /* This releases the run lock */
+ VG_(exit_thread)(tid);
+ vg_assert(tst->status == VgTs_Zombie);
+
+ /* We have to use this sequence to terminate the thread to
+ prevent a subtle race. If VG_(exit_thread)() had left the
+ ThreadState as Empty, then it could have been reallocated,
+ reusing the stack while we're doing these last cleanups.
+ Instead, VG_(exit_thread) leaves it as Zombie to prevent
+ reallocation. We need to make sure we don't touch the stack
+ between marking it Empty and exiting. Hence the
+ assembler. */
+ { ULong block[4];
+ vg_assert(sizeof(tst->status == 8));
+ vg_assert(__NR_AIX5_thread_terminate
+ != __NR_AIX5_UNKNOWN);
+ block[0] = (ULong)VgTs_Empty;
+ block[1] = (ULong) & (tst->status);
+ block[2] = (ULong) tst->os_state.exitcode;
+ block[3] = __NR_AIX5_thread_terminate;
+ asm volatile (
+ "mr 29,%0\n\t" /* r29 = &block[0] */
+ "ld 20, 0(29)\n\t" /* r20 = VgTs_Empty */
+ "ld 21, 8(29)\n\t" /* r21 = & (tst->status) */
+ "ld 22, 16(29)\n\t" /* r22 = tst->os_state.exitcode */
+ "ld 23, 24(29)\n\t" /* r23 = __NR_exit */
+ /* after this point we can't safely use the stack. */
+ "std 20, 0(21)\n\t" /* tst->status = VgTs_Empty */
+ "mr 2,23\n\t" /* r2 = __NR_exit */
+ "mr 3,22\n\t" /* set r3 = tst->os_state.exitcode */
+ /* set up for syscall */
+ ".long 0x48000005\n\t" /* "bl here+4" */
+ "mflr 29\n\t"
+ "addi 29,29,16\n\t"
+ "mtlr 29\n\t"
+ "sc\n\t" /* exit(tst->os_state.exitcode) */
+ :
+ : "b" (&block[0])
+ : "lr", "memory", "r2", "r3", "r20", "r21", "r22", "r23", "r29"
+ );
+ }
+
+ VG_(core_panic)("Thread exit failed?\n");
+ }
+
+ /*NOTREACHED*/
+ vg_assert(0);
+}
+
+
+static Word start_thread_NORETURN ( void* arg )
+{
+ ThreadState* tst = (ThreadState*)arg;
+ ThreadId tid = tst->tid;
+
+ run_a_thread_NORETURN ( (Word)tid );
+ /*NOTREACHED*/
+ vg_assert(0);
+}
+
+
+/* Call f(arg1), but first switch stacks, using 'stack' as the new
+ stack. f itself needs to never return. */
+__attribute__((noreturn))
+static
+void call_on_new_stack_0_1_NORETURN ( Addr stack,
+ void (*f_NORETURN)(Word),
+ Word arg1 )
+{
+ UWord* fdescr = (UWord*)f_NORETURN;
+ volatile UWord block[5];
+ block[0] = fdescr[0]; /* nia */
+ block[1] = stack; /* r1 */
+ block[2] = fdescr[1]; /* r2 */
+ block[3] = arg1; /* r3 */
+ block[4] = fdescr[2]; /* r11 */
+ __asm__ __volatile__(
+ "mr 4,%0\n\t" /* r4 = block */
+ "ld 1, 8(4)\n\t"
+ "ld 2, 16(4)\n\t"
+ "ld 3, 24(4)\n\t"
+ "ld 11,32(4)\n\t"
+ "ld 4, 0(4)\n\t"
+ "mtctr 4\n\t"
+ "bctr\n"
+ : /*out*/ : /*in*/ "b"(&block[0])
+ );
+ /*NOTREACHED*/
+ __asm__ __volatile__("trap");
+ while (1) {} /* convince gcc that this really doesn't return */
+}
+
+
+/* Allocate a stack for the main thread, and run it all the way to the
+ end. Although we already have a working VgStack
+ (VG_(interim_stack)) it's better to allocate a new one, so that
+ overflow detection works uniformly for all threads.
+*/
+void VG_(main_thread_wrapper_NORETURN)(ThreadId tid)
+{
+ Addr sp;
+ VG_(debugLog)(1, "syswrap-aix64",
+ "entering VG_(main_thread_wrapper_NORETURN)\n");
+
+ sp = ML_(allocstack)(tid);
+
+ /* If we can't even allocate the first thread's stack, we're hosed.
+ Give up. */
+ vg_assert2(sp != 0, "Cannot allocate main thread's stack.");
+
+ /* shouldn't be any other threads around yet */
+ vg_assert( VG_(count_living_threads)() == 1 );
+
+ /* make a stack frame */
+ sp -= 16;
+ sp &= ~0xF;
+ *(UWord *)sp = 0;
+
+ call_on_new_stack_0_1_NORETURN(
+ (Addr)sp, /* stack */
+ run_a_thread_NORETURN, /* fn to call */
+ (Word)tid /* arg to give it */
+ );
+
+ /*NOTREACHED*/
+ vg_assert(0);
+}
+
+/* --------- end HACKS --------- */
+
+
+/* ---------------------------------------------------------------------
+ More thread stuff
+ ------------------------------------------------------------------ */
+
+void VG_(cleanup_thread) ( ThreadArchState* arch )
+{
+}
+
+
+/* ---------------------------------------------------------------------
+ PRE/POST wrappers for ppc64/AIX5-specific syscalls
+ ------------------------------------------------------------------ */
+
+/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
+#include <sys/thread.h>
+/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
+
+
+/* Add prototypes for the wrappers declared here, so that gcc doesn't
+ harass us for not having prototypes. Really this is a kludge --
+ the right thing to do is to make these wrappers 'static' since they
+ aren't visible outside this file, but that requires even more macro
+ magic. */
+
+#define PRE(name) DEFN_PRE_TEMPLATE(ppc64_aix5, name)
+#define POST(name) DEFN_POST_TEMPLATE(ppc64_aix5, name)
+
+DECL_TEMPLATE(ppc64_aix5, sys__fp_fpscrx64_);
+DECL_TEMPLATE(ppc64_aix5, sys_kload);
+DECL_TEMPLATE(ppc64_aix5, sys_kunload64);
+DECL_TEMPLATE(ppc64_aix5, sys_thread_setstate);
+DECL_TEMPLATE(ppc64_aix5, sys_FAKE_SIGRETURN);
+
+
+PRE(sys__fp_fpscrx64_)
+{
+ PRINT("_fp_fpscrx64_ (BOGUS HANDLER)");
+}
+
+PRE(sys_kload)
+{
+ PRINT("kload (UNDOCUMENTED)( %p(%s), %ld, %ld )",
+ ARG1,ARG1, ARG2, ARG3 );
+ PRE_REG_READ3(void*, "kload", char*, name, long, arg2, char*, arg3);
+}
+POST(sys_kload)
+{
+ vg_assert(SUCCESS);
+ if (0) VG_(printf)("kload result = %p\n", RES);
+ if (RES)
+ POST_MEM_WRITE( RES, 64 );
+ ML_(aix5_rescan_procmap_after_load_or_unload)();
+}
+
+PRE(sys_kunload64)
+{
+ PRINT("kunload64 (UNDOCUMENTED)( %p, %ld, %ld, %p )",
+ ARG1, ARG2, ARG3, ARG4 );
+ PRE_REG_READ4(long, "kunload64",
+ void*, arg1, long, arg2, long, arg3, void*, arg4);
+}
+POST(sys_kunload64)
+{
+ vg_assert(SUCCESS);
+ ML_(aix5_rescan_procmap_after_load_or_unload)();
+}
+
+PRE(sys_thread_setstate)
+{
+ UWord dst_lwpid = (UWord)ARG1;
+ struct tstate* ats_new = (struct tstate*)ARG2;
+ struct tstate* ats_old = (struct tstate*)ARG3;
+ ThreadId dst_tid = VG_INVALID_THREADID;
+ ThreadState* dst_ts = NULL;
+ Int i;
+
+ /* Arrgh. We MUST retain the lock during this syscall. Reason is
+ that this is sometimes used for asynchronous thread cancellation
+ (nuking other threads). If we don't have the lock during the
+ syscall, then it's possible that the thread we're nuking might
+ get the lock before it gets killed off, and so we can never
+ re-acquire the lock after this syscall, and the system
+ deadlocks. */
+
+ /* 10 July 06: above comment is a misdiagnosis. It appears that
+ for thread cancellation (that is, with ->flags == TSTATE_INTR)
+ the target thread is has its PC changed by the the kernel to
+ something else, possibly to pthread_exit(), so that it can run
+ its cancellation handlers and exit. Currently is unknown how
+ the kernel knows what to set the target thread's PC to. I did
+ establish that all the other data passed in the struct is not
+ relevant: when ->flags == TSTATE_INTR, all the other words can
+ be set to 0x0 or 0xFFFFFFFF and the syscall still works. So the
+ address is not passed like that. Also I looked at args to
+ thread_setmystate_fast, which is used when a thread sets its
+ cancellation state, but none of those are code addresses.
+
+ Also, it's ok for the kernel to simply change the target
+ thread's PC to something else for async thread cancellation, but
+ for deferred cancellation something else is needed, and I can't
+ see how that would work either.
+
+ Anyway, net result is, target thread ends up not running on the
+ simulator (not dead), which is why it's necessary to hold onto
+ the lock at this point. */
+
+ /* 30 July 06: added kludge to intercept attempts to cancel another
+ thread and instead just force that thread to run
+ pthread_exit(PTHREAD_CANCELED). This allows V to keep
+ control. */
+
+ PRINT("thread_setstate (BOGUS HANDLER) "
+ "( %ld, %p,%p )", dst_lwpid, ats_new, ats_old);
+ if (1 && VG_(clo_trace_syscalls) && ats_new)
+ ML_(aix5debugstuff_show_tstate)((Addr)ats_new,
+ "thread_setstate (NEW)");
+
+ /* Intercept and handle ourselves any attempts to cancel
+ another thread (including this one). */
+
+ if (ats_new && (!ats_old) && ats_new->flags == TSTATE_INTR) {
+ dst_ts = NULL;
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(INTR for lwpid %ld)", dst_lwpid);
+ dst_tid = VG_INVALID_THREADID;
+ for (i = 0; i < VG_N_THREADS; i++) {
+ dst_ts = VG_(get_ThreadState)(i);
+ if ((dst_ts->status == VgTs_Runnable
+ || dst_ts->status == VgTs_Yielding
+ || dst_ts->status == VgTs_WaitSys)
+ && dst_ts->os_state.lwpid == dst_lwpid) {
+ dst_tid = i;
+ break;
+ }
+ }
+ if (VG_(clo_trace_syscalls)) {
+ if (dst_tid == VG_INVALID_THREADID)
+ VG_(printf)("(== unknown tid)");
+ else
+ VG_(printf)("(== tid %d)", (Int)dst_tid);
+ }
+ if (dst_tid != VG_INVALID_THREADID) {
+ /* A cancel has been requested for ctid. If the target
+ thread has cancellation enabled, honour it right now. If
+ not, mark the thread as having a cancellation request, so
+ that if it later enables cancellation then the
+ cancellation will take effect. */
+ vg_assert(dst_ts);
+ if (dst_ts->os_state.cancel_progress == Canc_NoRequest) {
+ if (dst_ts->os_state.cancel_disabled) {
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(target has cancel disabled"
+ "; request lodged)");
+ dst_ts->os_state.cancel_progress = Canc_Requested;
+ } else {
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(forcing target into pthread_exit)");
+ dst_ts->os_state.cancel_progress = Canc_Actioned;
+ Bool ok = ML_(aix5_force_thread_into_pthread_exit)(dst_tid);
+ if (!ok) {
+ /* now at serious risk of deadlock/livelock. Give up
+ rather than continue. */
+ ML_(aix5_set_threadstate_for_emergency_exit)
+ (tid, "pthread_cancel(case2-64): "
+ "cannot find pthread_exit; aborting");
+ SET_STATUS_Success(0);
+ return;
+ }
+ }
+ }
+ SET_STATUS_Success(0);
+ return;
+ }
+ }
+
+ /* Well, it's not a cancellation request. Maybe it is the
+ initialisation of a previously created thread? */
+
+ if (ats_new && !ats_old) {
+ dst_tid = VG_INVALID_THREADID;
+ for (i = 0; i < VG_N_THREADS; i++) {
+ dst_ts = VG_(get_ThreadState)(i);
+ if (dst_ts->status == VgTs_Init
+ && dst_ts->os_state.lwpid == dst_lwpid) {
+ dst_tid = i;
+ break;
+ }
+ }
+ if (dst_tid != VG_INVALID_THREADID) {
+ /* Found the associated child */
+ if (VG_(clo_trace_syscalls))
+ VG_(printf)("(initialised child tid %d)", (Int)dst_tid);
+ dst_ts = VG_(get_ThreadState)(dst_tid);
+ UWord* stack = (UWord*)ML_(allocstack)(dst_tid);
+ /* XXX TODO: check allocstack failure */
+
+ /* copy the specified child register state into the guest
+ slot (we need that context to run on the simulated CPU,
+ not the real one) and put pointers to our own
+ run-the-simulator function into what we'll hand off to the
+ kernel instead. */
+
+ /* The guest thread is to start running whatever context
+ this syscall showed up with. */
+ dst_ts->arch.vex.guest_GPR0 = ats_new->mst.gpr[0];
+ dst_ts->arch.vex.guest_GPR1 = ats_new->mst.gpr[1]; /* sp */
+ dst_ts->arch.vex.guest_GPR2 = ats_new->mst.gpr[2]; /* toc */
+ dst_ts->arch.vex.guest_GPR3 = ats_new->mst.gpr[3]; /* initarg */
+ dst_ts->arch.vex.guest_GPR4 = ats_new->mst.gpr[4];
+ dst_ts->arch.vex.guest_GPR5 = ats_new->mst.gpr[5];
+ dst_ts->arch.vex.guest_GPR6 = ats_new->mst.gpr[6];
+ dst_ts->arch.vex.guest_GPR7 = ats_new->mst.gpr[7];
+ dst_ts->arch.vex.guest_GPR8 = ats_new->mst.gpr[8];
+ dst_ts->arch.vex.guest_GPR9 = ats_new->mst.gpr[9];
+ dst_ts->arch.vex.guest_GPR10 = ats_new->mst.gpr[10];
+ dst_ts->arch.vex.guest_GPR11 = ats_new->mst.gpr[11]; /* ?? */
+ dst_ts->arch.vex.guest_GPR12 = ats_new->mst.gpr[12];
+ dst_ts->arch.vex.guest_GPR13 = ats_new->mst.gpr[13];
+ dst_ts->arch.vex.guest_GPR14 = ats_new->mst.gpr[14];
+ dst_ts->arch.vex.guest_GPR15 = ats_new->mst.gpr[15];
+ dst_ts->arch.vex.guest_GPR16 = ats_new->mst.gpr[16];
+ dst_ts->arch.vex.guest_GPR17 = ats_new->mst.gpr[17];
+ dst_ts->arch.vex.guest_GPR18 = ats_new->mst.gpr[18];
+ dst_ts->arch.vex.guest_GPR19 = ats_new->mst.gpr[19];
+ dst_ts->arch.vex.guest_GPR20 = ats_new->mst.gpr[20];
+ dst_ts->arch.vex.guest_GPR21 = ats_new->mst.gpr[21];
+ dst_ts->arch.vex.guest_GPR22 = ats_new->mst.gpr[22];
+ dst_ts->arch.vex.guest_GPR23 = ats_new->mst.gpr[23];
+ dst_ts->arch.vex.guest_GPR24 = ats_new->mst.gpr[24];
+ dst_ts->arch.vex.guest_GPR25 = ats_new->mst.gpr[25];
+ dst_ts->arch.vex.guest_GPR26 = ats_new->mst.gpr[26];
+ dst_ts->arch.vex.guest_GPR27 = ats_new->mst.gpr[27];
+ dst_ts->arch.vex.guest_GPR28 = ats_new->mst.gpr[28];
+ dst_ts->arch.vex.guest_GPR29 = ats_new->mst.gpr[29];
+ dst_ts->arch.vex.guest_GPR30 = ats_new->mst.gpr[30];
+ dst_ts->arch.vex.guest_GPR31 = ats_new->mst.gpr[31];
+ dst_ts->arch.vex.guest_CIA = ats_new->mst.iar; /* pc */
+ dst_ts->arch.vex.guest_LR = ats_new->mst.lr;
+ dst_ts->arch.vex.guest_CTR = ats_new->mst.ctr;
+ LibVEX_GuestPPC64_put_CR( ats_new->mst.cr, &dst_ts->arch.vex );
+ LibVEX_GuestPPC64_put_XER( ats_new->mst.xer, &dst_ts->arch.vex );
+
+ /* Record what seems like the highest legitimate stack
+ address for this thread, so that the stack unwinder works
+ properly. It seems reasonable to use the R1 value
+ supplied here. */
+ dst_ts->client_stack_highest_word = dst_ts->arch.vex.guest_GPR1;
+
+ /* The host thread is to start running
+ start_thread_NORETURN */
+ UWord* wrapper_fdescr = (UWord*) & start_thread_NORETURN;
+ ats_new->mst.gpr[1] = (UWord)stack;
+ ats_new->mst.gpr[2] = wrapper_fdescr[1];
+ ats_new->mst.iar = wrapper_fdescr[0];
+ ats_new->mst.gpr[3] = (UWord)dst_ts;
+
+ /* Set initial cancellation status for the thread. */
+ dst_ts->os_state.cancel_async = False;
+ dst_ts->os_state.cancel_disabled = False;
+ dst_ts->os_state.cancel_progress = Canc_NoRequest;
+ }
+ }
+}
+POST(sys_thread_setstate)
+{
+ if (ARG3)
+ POST_MEM_WRITE( ARG3, sizeof(struct tstate) );
+ if (0 && VG_(clo_trace_syscalls) && ARG3)
+ ML_(aix5debugstuff_show_tstate)(ARG3, "thread_setstate (OLD)");
+}
+
+PRE(sys_FAKE_SIGRETURN)
+{
+ ThreadState* tst;
+ /* This handles the fake signal-return system call created by
+ sigframe-ppc64-aix5.c. */
+ PRINT("FAKE_SIGRETURN ( )");
+
+ vg_assert(VG_(is_valid_tid)(tid));
+ vg_assert(tid >= 1 && tid < VG_N_THREADS);
+ vg_assert(VG_(is_running_thread)(tid));
+
+ /* Remove the signal frame from this thread's (guest) stack,
+ in the process restoring the pre-signal guest state. */
+ VG_(sigframe_destroy)(tid, True);
+
+ /* Now the pre-signal registers are restored. Unfortunately the
+ syscall driver logic will want to copy back the syscall result
+ (not that there is one) into guest r3/r4. So we'd better cook
+ up a syscall result which, when copied back, makes no change. */
+ tst = VG_(get_ThreadState)(tid);
+ SET_STATUS_from_SysRes(
+ VG_(mk_SysRes_ppc64_aix5)(
+ tst->arch.vex.guest_GPR3,
+ tst->arch.vex.guest_GPR4
+ )
+ );
+
+ *flags |= SfPollAfter;
+}
+
+
+/* ---------------------------------------------------------------------
+ The ppc64/AIX5 syscall table
+ ------------------------------------------------------------------ */
+
+typedef
+ struct {
+ UInt* pSysNo;
+ SyscallTableEntry wrappers;
+ }
+ AIX5SCTabEntry;
+
+#undef PLAXY
+#undef PLAX_
+
+#define PLAXY(sysno, name) \
+ { & sysno, \
+ { & WRAPPER_PRE_NAME(ppc64_aix5, name), \
+ & WRAPPER_POST_NAME(ppc64_aix5, name) }}
+
+#define PLAX_(sysno, name) \
+ { & sysno, \
+ { & WRAPPER_PRE_NAME(ppc64_aix5, name), \
+ NULL }}
+
+static /* but not const */
+AIX5SCTabEntry aix5_ppc64_syscall_table[]
+= {
+ AIXXY(__NR_AIX5___libc_sbrk, sys___libc_sbrk),
+ AIXX_(__NR_AIX5___msleep, sys___msleep),
+ AIXX_(__NR_AIX5__exit, sys__exit),
+ PLAX_(__NR_AIX5__fp_fpscrx64_, sys__fp_fpscrx64_),
+ AIXX_(__NR_AIX5__getpid, sys__getpid),
+ AIXXY(__NR_AIX5__nsleep, sys__nsleep),
+ AIXX_(__NR_AIX5__pause, sys__pause),
+ AIXXY(__NR_AIX5__poll, sys__poll),
+ AIXX_(__NR_AIX5__select, sys__select),
+ AIXXY(__NR_AIX5__sigaction, sys__sigaction),
+ AIXX_(__NR_AIX5__thread_self, sys__thread_self),
+ AIXX_(__NR_AIX5_access, sys_access),
+ AIXX_(__NR_AIX5_accessx, sys_accessx),
+ AIXXY(__NR_AIX5_appgetrlimit, sys_appgetrlimit),
+ AIXXY(__NR_AIX5_appgetrusage, sys_appgetrusage),
+ AIXX_(__NR_AIX5_appsetrlimit, sys_appsetrlimit),
+ AIXX_(__NR_AIX5_appulimit, sys_appulimit),
+ AIXX_(__NR_AIX5_bind, sys_bind),
+ AIXX_(__NR_AIX5_chdir, sys_chdir),
+ AIXX_(__NR_AIX5_chmod, sys_chmod),
+ AIXX_(__NR_AIX5_chown, sys_chown),
+ AIXX_(__NR_AIX5_close, sys_close),
+ AIXX_(__NR_AIX5_connext, sys_connext),
+ AIXX_(__NR_AIX5_execve, sys_execve),
+ AIXXY(__NR_AIX5_fstatx, sys_fstatx),
+ AIXXY(__NR_AIX5_getdirent, sys_getdirent),
+ AIXXY(__NR_AIX5_getdirent64, sys_getdirent64),
+ AIXXY(__NR_AIX5_getdomainname, sys_getdomainname),
+ AIXX_(__NR_AIX5_getgidx, sys_getgidx),
+ AIXXY(__NR_AIX5_gethostname, sys_gethostname),
+ AIXXY(__NR_AIX5_getpriv, sys_getpriv),
+ AIXXY(__NR_AIX5_getprocs, sys_getprocs),
+ AIXXY(__NR_AIX5_getprocs64, sys_getprocs), /* XXX: correct? */
+ AIXX_(__NR_AIX5_getrpid, sys_getrpid),
+ AIXXY(__NR_AIX5_getsockopt, sys_getsockopt),
+ AIXX_(__NR_AIX5_gettimerid, sys_gettimerid),
+ AIXX_(__NR_AIX5_getuidx, sys_getuidx),
+ AIXXY(__NR_AIX5_incinterval, sys_incinterval),
+ AIXXY(__NR_AIX5_kfcntl, sys_kfcntl),
+ AIXX_(__NR_AIX5_kfork, sys_kfork),
+ AIXX_(__NR_AIX5_kill, sys_kill),
+ AIXXY(__NR_AIX5_kioctl, sys_kioctl),
+ PLAXY(__NR_AIX5_kload, sys_kload),
+ AIXX_(__NR_AIX5_klseek, sys_klseek),
+ AIXXY(__NR_AIX5_kread, sys_kread),
+ AIXXY(__NR_AIX5_kreadv, sys_kreadv),
+ AIXX_(__NR_AIX5_kthread_ctl, sys_kthread_ctl),
+ PLAXY(__NR_AIX5_kunload64, sys_kunload64),
+ AIXXY(__NR_AIX5_kwaitpid, sys_kwaitpid),
+ AIXX_(__NR_AIX5_kwrite, sys_kwrite),
+ AIXX_(__NR_AIX5_kwritev, sys_kwritev),
+ AIXX_(__NR_AIX5_lseek, sys_lseek),
+ AIXX_(__NR_AIX5_mkdir, sys_mkdir),
+ AIXXY(__NR_AIX5_mmap, sys_mmap),
+ AIXXY(__NR_AIX5_mprotect, sys_mprotect),
+ AIXXY(__NR_AIX5_munmap, sys_munmap),
+ AIXXY(__NR_AIX5_ngetpeername, sys_ngetpeername),
+ AIXXY(__NR_AIX5_ngetsockname, sys_ngetsockname),
+ AIXXY(__NR_AIX5_nrecvfrom, sys_nrecvfrom),
+ AIXX_(__NR_AIX5_nrecvmsg, sys_nrecvmsg),
+ AIXX_(__NR_AIX5_open, sys_open),
+ AIXXY(__NR_AIX5_pipe, sys_pipe),
+ AIXX_(__NR_AIX5_privcheck, sys_privcheck),
+ AIXX_(__NR_AIX5_rename, sys_rename),
+ AIXXY(__NR_AIX5_sbrk, sys_sbrk),
+ AIXX_(__NR_AIX5_send, sys_send),
+ AIXX_(__NR_AIX5_setgid, sys_setgid),
+ AIXX_(__NR_AIX5_setsockopt, sys_setsockopt),
+ AIXX_(__NR_AIX5_setuid, sys_setuid),
+ AIXXY(__NR_AIX5_shmat, sys_shmat),
+ AIXXY(__NR_AIX5_shmctl, sys_shmctl),
+ AIXXY(__NR_AIX5_shmdt, sys_shmdt),
+ AIXX_(__NR_AIX5_shmget, sys_shmget),
+ AIXX_(__NR_AIX5_shutdown, sys_shutdown),
+ AIXX_(__NR_AIX5_sigcleanup, sys_sigcleanup),
+ AIXXY(__NR_AIX5_sigprocmask, sys_sigprocmask),
+ AIXXY(__NR_AIX5_sys_parm, sys_sys_parm),
+ AIXXY(__NR_AIX5_sysconfig, sys_sysconfig),
+ AIXX_(__NR_AIX5_socket, sys_socket),
+ AIXXY(__NR_AIX5_statx, sys_statx),
+ AIXXY(__NR_AIX5_thread_create, sys_thread_create),
+ AIXX_(__NR_AIX5_thread_init, sys_thread_init),
+ AIXX_(__NR_AIX5_thread_kill, sys_thread_kill),
+ AIXXY(__NR_AIX5_thread_setmystate, sys_thread_setmystate),
+ AIXX_(__NR_AIX5_thread_setmystate_fast, sys_thread_setmystate_fast),
+ PLAXY(__NR_AIX5_thread_setstate, sys_thread_setstate),
+ AIXX_(__NR_AIX5_thread_terminate_unlock, sys_thread_terminate_unlock),
+ AIXX_(__NR_AIX5_thread_tsleep, sys_thread_tsleep),
+ AIXX_(__NR_AIX5_thread_twakeup, sys_thread_twakeup),
+ AIXX_(__NR_AIX5_thread_unlock, sys_thread_unlock),
+ AIXX_(__NR_AIX5_thread_waitlock_, sys_thread_waitlock_),
+ AIXXY(__NR_AIX5_times, sys_times),
+ AIXX_(__NR_AIX5_unlink, sys_unlink),
+ AIXX_(__NR_AIX5_utimes, sys_utimes),
+ AIXXY(__NR_AIX5_vmgetinfo, sys_vmgetinfo),
+ AIXX_(__NR_AIX5_yield, sys_yield),
+ PLAX_(__NR_AIX5_FAKE_SIGRETURN, sys_FAKE_SIGRETURN)
+ };
+
+SyscallTableEntry* ML_(get_ppc64_aix5_syscall_entry) ( UInt sysno )
+{
+ Int i;
+ AIX5SCTabEntry tmp;
+
+ const Int tab_size = sizeof(aix5_ppc64_syscall_table)
+ / sizeof(aix5_ppc64_syscall_table[0]);
+
+ for (i = 0; i < tab_size; i++)
+ if (sysno == *(aix5_ppc64_syscall_table[i].pSysNo))
+ break;
+
+ vg_assert(i >= 0 && i <= tab_size);
+ if (i == tab_size)
+ return NULL; /* can't find a wrapper */
+
+ /* Move found one a bit closer to the front, so as to
+ make future searches cheaper. */
+ if (i > 0) {
+ tmp = aix5_ppc64_syscall_table[i-1];
+ aix5_ppc64_syscall_table[i-1] = aix5_ppc64_syscall_table[i];
+ aix5_ppc64_syscall_table[i] = tmp;
+ i--;
+ }
+
+ vg_assert(i >= 0 && i < tab_size);
+ return &aix5_ppc64_syscall_table[i].wrappers;
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/