]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Merge r6101:
authorJulian Seward <jseward@acm.org>
Tue, 17 Oct 2006 01:08:19 +0000 (01:08 +0000)
committerJulian Seward <jseward@acm.org>
Tue, 17 Oct 2006 01:08:19 +0000 (01:08 +0000)
Syscall wrappers for AIX5.  Unfortunately they duplicate some of the
stuff in syswrap-generic.c since making that compile on AIX is just
too difficult.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6250

coregrind/m_syswrap/priv_syswrap-aix5.h [new file with mode: 0644]
coregrind/m_syswrap/syscall-ppc32-aix5.S [new file with mode: 0644]
coregrind/m_syswrap/syscall-ppc64-aix5.S [new file with mode: 0644]
coregrind/m_syswrap/syswrap-aix5.c [new file with mode: 0644]
coregrind/m_syswrap/syswrap-ppc32-aix5.c [new file with mode: 0644]
coregrind/m_syswrap/syswrap-ppc64-aix5.c [new file with mode: 0644]

diff --git a/coregrind/m_syswrap/priv_syswrap-aix5.h b/coregrind/m_syswrap/priv_syswrap-aix5.h
new file mode 100644 (file)
index 0000000..79fc0e0
--- /dev/null
@@ -0,0 +1,202 @@
+
+/*--------------------------------------------------------------------*/
+/*--- 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                                                          ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_syswrap/syscall-ppc32-aix5.S b/coregrind/m_syswrap/syscall-ppc32-aix5.S
new file mode 100644 (file)
index 0000000..92d7d34
--- /dev/null
@@ -0,0 +1,219 @@
+
+/*--------------------------------------------------------------------*/
+/*--- 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                                                          ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_syswrap/syscall-ppc64-aix5.S b/coregrind/m_syswrap/syscall-ppc64-aix5.S
new file mode 100644 (file)
index 0000000..edf7bac
--- /dev/null
@@ -0,0 +1,219 @@
+
+/*--------------------------------------------------------------------*/
+/*--- 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                                                          ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_syswrap/syswrap-aix5.c b/coregrind/m_syswrap/syswrap-aix5.c
new file mode 100644 (file)
index 0000000..07b627c
--- /dev/null
@@ -0,0 +1,2508 @@
+
+/*--------------------------------------------------------------------*/
+/*--- 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                                                          ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_syswrap/syswrap-ppc32-aix5.c b/coregrind/m_syswrap/syswrap-ppc32-aix5.c
new file mode 100644 (file)
index 0000000..31e934c
--- /dev/null
@@ -0,0 +1,890 @@
+
+/*--------------------------------------------------------------------*/
+/*--- 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                                                          ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_syswrap/syswrap-ppc64-aix5.c b/coregrind/m_syswrap/syswrap-ppc64-aix5.c
new file mode 100644 (file)
index 0000000..6f6c08f
--- /dev/null
@@ -0,0 +1,767 @@
+
+/*--------------------------------------------------------------------*/
+/*--- 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                                                          ---*/
+/*--------------------------------------------------------------------*/