]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Merge r6106 (rename the Linux launcher and add an AIX5 one.)
authorJulian Seward <jseward@acm.org>
Tue, 17 Oct 2006 00:39:31 +0000 (00:39 +0000)
committerJulian Seward <jseward@acm.org>
Tue, 17 Oct 2006 00:39:31 +0000 (00:39 +0000)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6241

coregrind/launcher-aix5-bootblock.h [new file with mode: 0644]
coregrind/launcher-aix5.c [new file with mode: 0644]
coregrind/launcher-linux.c [moved from coregrind/launcher.c with 100% similarity]

diff --git a/coregrind/launcher-aix5-bootblock.h b/coregrind/launcher-aix5-bootblock.h
new file mode 100644 (file)
index 0000000..d504a14
--- /dev/null
@@ -0,0 +1,85 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Structure written into the child process by launcher-aix5.c. ---*/
+/*---                                    launcher-aix5-bootblock.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.
+*/
+
+
+/* This is the structure written into the child process by the AIX5
+   launcher.  The tool's entry point (see bottom of
+   coregrind/m_main.c) must be aware of the layout as it will have to
+   read info from it at startup.  This struct is designed to work
+   equally well in 32- and 64-bit mode.  It must be placed at an
+   8-aligned address in the child. */
+
+#define N_BOOTBLOCK_INSNS    60  /* number of insns */
+#define N_BOOTBLOCK_ERRMSG   64  /* max bytes in the error message */
+#define N_BOOTBLOCK_TOOLFILE 256 /* max bytes in the tool file name */
+
+typedef
+
+   struct {
+      /* Adler32 checksum of the uncompressed data of the compressed
+        page (the second part of which contains this struct. */
+      /*   0 */ UInt adler32;
+      /* The system call numbers for enough critical syscalls that the
+         tool can start both debug logging and also read the
+         /proc/../sysent file. */
+      /*   4 */ UInt __NR_getpid;
+      /*   8 */ UInt __NR_write;
+      /*  12 */ UInt __NR_exit;
+      /*  16 */ UInt __NR_open;
+      /*  20 */ UInt __NR_read;
+      /*  24 */ UInt __NR_close;
+      /*  28 */ UInt __off28;
+
+      /* The 37 integer registers for the client, as they should be at
+         startup.  On 32-bit targets the registers are stored in the
+         lower half of each quadword, which, since this is a bigendian
+         platform, is the higher-addressed 4 bytes. */
+      /* MUST BE 8-aligned */
+      /*  32 */ ULong iregs_pc_cr_lr_ctr_xer[37];
+
+      /* The instructions for the bootstrap loader. */
+      /* 328 */ UInt code[N_BOOTBLOCK_INSNS];
+
+      /* A zero-terminated error message to be used when the bootstrap
+         loader fails. */
+      /* 628 */ UChar errmsg[N_BOOTBLOCK_ERRMSG];
+
+      /* The name of the tool file, again zero-terminated. */
+      /* 692 */ UChar toolfile[N_BOOTBLOCK_TOOLFILE];
+
+      /* 1024 */
+   }
+   AIX5Bootblock;
+
+/*--------------------------------------------------------------------*/
+/*--- end                                launcher-aix5-bootblock.h ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/launcher-aix5.c b/coregrind/launcher-aix5.c
new file mode 100644 (file)
index 0000000..cf317b1
--- /dev/null
@@ -0,0 +1,1636 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Launching Valgrind on AIX5.                  launcher-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.
+*/
+
+/* Cut-down version of the normal launcher, except it is completely
+   different on AIX5.  Does not handle shell scripts, only real
+   machine code XCOFF executables.
+
+   Note: this is a "normal" program and not part of Valgrind proper,
+   and so it doesn't have to conform to Valgrind's arcane rules on
+   no-glibc-usage etc.
+*/
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+/* Get both struct __ld_info32 and struct __ld_info64. */
+#define __LDINFO_PTRACE32__ 1
+#define __LDINFO_PTRACE64__ 1
+#include <sys/ldr.h>
+
+#include <sys/reg.h>     /* GPR0 .. GPR31 */
+#include <sys/procfs.h>  /* prsysent_t */
+
+#include "pub_core_debuglog.h"
+#include "pub_core_vki.h"
+#include "pub_core_vkiscnums.h"
+#include "pub_core_libcproc.h"  // For VALGRIND_LIB, VALGRIND_LAUNCHER
+
+/* Get the definition for the AIX5Bootblock structure.  This is what
+   we will generate and patch into the child's address space. */
+#include "launcher-aix5-bootblock.h"
+
+/* Simple routines for Huffman compression/decompression */
+#include "m_initimg/simple_huffman.c"
+
+
+/* -------------------------------------------------------------- */
+/* ---                                                        --- */
+/* --- A uniform interface to the ptrace facilities we need.  --- */
+/* ---                                                        --- */
+/* -------------------------------------------------------------- */
+
+typedef
+   struct {
+      pid_t pid;
+      Bool  is64;
+   } 
+   Child;
+
+
+/* Read len bytes from target's rsrc to local ldst.  Returns True if
+   error. */
+static 
+Bool ptrace_READ_BLOCK ( Child* ch, Int len, void* ldst, Addr64 rsrc )
+{
+   Int r;
+   assert(len >= 0 && len <= 1024);
+   r = ptrace64( PT_READ_BLOCK, (ULong)ch->pid, rsrc, len, ldst );
+   if (r == len)
+      return False; /* success */
+   return True; /* error */
+}
+
+
+/* Write len bytes to target's rdst from local lsrc.  Returns True if
+   error. */
+static
+Bool ptrace_WRITE_BLOCK ( Child* child, Int len, Addr64 rdst, void* lsrc )
+{
+   Int r;
+   assert(len >= 0 && len <= 1024);
+   r = ptrace64( PT_WRITE_BLOCK, (ULong)child->pid, rdst, len, lsrc );
+   if (r == len)
+      return False; /* success */
+   return True; /* error */
+}
+
+
+/* Read a GPR from the target.  Returns True if error. */
+static
+Bool ptrace_READ_GPR ( Child* child, Int reg, ULong* ldst )
+{
+   ULong w64;
+   UInt  w32;
+   errno = 0;
+   if (child->is64) {
+      (void)ptrace64( PT_READ_GPR, 
+                      (ULong)child->pid, (ULong)reg, 8, (Int*)(&w64) );
+      if (errno != 0) return True; /* error */
+   } else {
+      w32 = ptrace64( PT_READ_GPR, 
+                      (ULong)child->pid, (ULong)reg, 0, 0 );
+      if (errno != 0) return True; /* error */
+      w64 = (ULong)w32;
+   }
+   *ldst = w64;
+   return False; /* success */
+}
+
+
+/* Write a GPR to the target.  Returns True if error. */
+static
+Bool ptrace_WRITE_GPR ( Child* child, Int reg, ULong val )
+{
+   ULong w64;
+   UInt w32;
+   errno = 0;
+   if (child->is64) {
+      w64 = val;
+      (void)ptrace64( PT_WRITE_GPR,
+                      (ULong)child->pid, (ULong)reg, 8, (Int*)&w64 );
+      if (errno != 0) return True; /* error */
+   } else {
+      w32 = (UInt)val;
+      (void)ptrace64( PT_WRITE_GPR, 
+                      (ULong)child->pid, (ULong)reg, w32, 0 );
+      if (errno != 0) return True; /* error */
+   }
+   return False; /* success */
+}
+
+
+/* -------------------------------------------------------------- */
+/* ---                                                        --- */
+/* --- Helper functions                                       --- */
+/* ---                                                        --- */
+/* -------------------------------------------------------------- */
+
+/* Search the path for the client program */
+static const char* find_client ( const char* clientname )
+{
+   static char fullname[PATH_MAX];
+   const char *path = getenv("PATH");
+   const char *colon;
+
+   while (path)
+   {
+      if ((colon = strchr(path, ':')) == NULL)
+      {
+         strcpy(fullname, path);
+         path = NULL;
+      }
+      else
+      {
+         memcpy(fullname, path, colon - path);
+         fullname[colon - path] = '\0';
+         path = colon + 1;
+      }
+      strcat(fullname, "/");
+      strcat(fullname, clientname);
+
+      if (access(fullname, R_OK|X_OK) == 0)
+         return fullname;
+    }
+
+    return clientname;
+}
+
+/* Examine the given file.  If it looks like valid XCOFF32 return 32,
+   if valid XCOFF64 return 64, else return 0. */
+static Int examine_client ( const char* clientname )
+{
+   UChar buf[16];
+   Int n;
+   FILE* f = fopen( clientname, "r" );
+   if (f == NULL)
+      return 0;
+   n = fread( buf, 1, 16, f );
+   fclose(f);
+   if (n != 16)
+      return 0;
+   if (buf[0] == 0x01 && buf[1] == 0xDF)
+      return 32; /* XCOFF32 */
+   if (buf[0] == 0x01 && buf[1] == 0xF7)
+      return 64; /* XCOFF64 */
+   return 0;
+}
+
+static Bool file_exists ( char* fname )
+{
+   struct stat buf;
+   int r = stat(fname, &buf);
+   return r == 0;
+}
+
+static Addr64 ROUNDDN_PAGE ( Addr64 v )
+{
+   ULong p = (ULong)v;
+   ULong a = PAGE_SIZE;
+   p &= ~(a-1);
+   return (Addr64)p;
+}
+
+static Bool IS_PAGE_ALIGNED ( Addr64 v )
+{
+   ULong p = (ULong)v;
+   ULong a = PAGE_SIZE;
+   if (p & (a-1))
+      return False;
+   else
+      return True;
+}
+
+static Bool IS_8_ALIGNED ( Addr64 v )
+{
+   ULong p = (ULong)v;
+   ULong a = 8;
+   if (p & (a-1))
+      return False;
+   else
+      return True;
+}
+
+
+/* Read a 4096-byte page from CHILD's address space at location SRC,
+   into local address space at DST.  Returns True if error, False
+   otherwise.
+*/
+static Bool ptrace_read_page ( Child* child, UChar* ldst, Addr64 rsrc )
+{
+   Int  off;
+   Bool err;
+
+   assert(IS_PAGE_ALIGNED(rsrc));
+
+   off = 0;
+   err = ptrace_READ_BLOCK(child, 1024, ldst + off, rsrc + off);
+   if (err) return err;
+
+   off += 1024;
+   err = ptrace_READ_BLOCK(child, 1024, ldst + off, rsrc + off);
+   if (err) return err;
+
+   off += 1024;
+   err = ptrace_READ_BLOCK(child, 1024, ldst + off, rsrc + off);
+   if (err) return err;
+
+   off += 1024;
+   err = ptrace_READ_BLOCK(child, 1024, ldst + off, rsrc + off);
+   if (err) return err;
+
+   off += 1024;
+   assert(off == PAGE_SIZE);
+
+   return False;
+}
+
+
+/* Write a 4096-byte page from local address space at SRC to CHILD's
+   address space at location DST.  Returns True if error, False
+   otherwise.
+*/
+static Bool ptrace_write_page ( Child* child, Addr64 rdst, UChar* lsrc )
+{
+   Int  off;
+   Bool err;
+
+   assert(IS_PAGE_ALIGNED(rdst));
+
+   off = 0;
+   err = ptrace_WRITE_BLOCK(child, 1024, rdst + off, lsrc + off);
+   if (err) return err;
+
+   off += 1024;
+   err = ptrace_WRITE_BLOCK(child, 1024, rdst + off, lsrc + off);
+   if (err) return err;
+
+   off += 1024;
+   err = ptrace_WRITE_BLOCK(child, 1024, rdst + off, lsrc + off);
+   if (err) return err;
+
+   off += 1024;
+   err = ptrace_WRITE_BLOCK(child, 1024, rdst + off, lsrc + off);
+   if (err) return err;
+
+   off += 1024;
+   assert(off == PAGE_SIZE);
+
+   return False;
+}
+
+
+/* Get 37 integer registers (GPR0 .. GPR31, PC, CR, LR, CTR, XER) from
+   CHILD into the given array.  Returns True if there is any kind of
+   error. */
+static 
+Bool ptrace_get_iregs_pc_cr_lr_ctr_xer ( 
+        Child* child, 
+        /*OUT*/ULong* iregs_pc_cr_lr_ctr_xer 
+     )
+{
+   Int  i, j;
+   Bool err;
+
+   for (i = GPR0; i <= GPR31; i++) {
+      j = i - GPR0;
+      assert(j >= 0 && j < 32);
+      err = ptrace_READ_GPR( child, i, &iregs_pc_cr_lr_ctr_xer[j] );
+      if (err) return err;
+   }
+
+   /* PC */
+   err = ptrace_READ_GPR( child, IAR, &iregs_pc_cr_lr_ctr_xer[32+0] );
+   if (err) return err;
+
+   /* CR */
+   err = ptrace_READ_GPR( child, CR, &iregs_pc_cr_lr_ctr_xer[32+1] );
+   if (err) return err;
+
+   /* LR */
+   err = ptrace_READ_GPR( child, LR, &iregs_pc_cr_lr_ctr_xer[32+2] );
+   if (err) return err;
+
+   /* CTR */
+   err = ptrace_READ_GPR( child, CTR, &iregs_pc_cr_lr_ctr_xer[32+3] );
+   if (err) return err;
+
+   /* XER */
+   err = ptrace_READ_GPR( child, XER, &iregs_pc_cr_lr_ctr_xer[32+4] );
+   if (err) return err;
+
+   return False;
+}
+
+
+/* Set CHILD's program counter to the given value.  Returns True if
+   there is any kind of error. */
+static 
+Bool ptrace_put_pc ( Child* child, ULong newpc )
+{
+   return ptrace_WRITE_GPR( child, IAR, newpc );
+}
+
+
+/* Set CHILD's R31 to the given value.  Returns True if there is any
+   kind of error. */
+static 
+Bool ptrace_put_r31 ( Child* child, ULong newr31 )
+{
+   return ptrace_WRITE_GPR( child, GPR31, newr31 );
+}
+
+
+/* ------ Instruction generators ------ */
+
+static UInt mkFormD ( UInt opc1, UInt r1, UInt r2, UInt imm )
+{
+   UInt theInstr;
+   assert(opc1 < 0x40);
+   assert(r1   < 0x20);
+   assert(r2   < 0x20);
+   imm = imm & 0xFFFF;
+   theInstr = ((opc1<<26) | (r1<<21) | (r2<<16) | (imm));
+   return theInstr;
+}
+static UInt mkFormX ( UInt opc1, 
+                      UInt r1, UInt r2, UInt r3, UInt opc2, UInt b0 )
+{
+   UInt theInstr;
+   assert(opc1 < 0x40);
+   assert(r1   < 0x20);
+   assert(r2   < 0x20);
+   assert(r3   < 0x20);
+   assert(opc2 < 0x400);
+   assert(b0   < 0x2);
+   theInstr = ((opc1<<26) | (r1<<21) | (r2<<16) |
+               (r3<<11) | (opc2<<1) | (b0));
+   return theInstr;
+}
+static UInt mkFormXFX ( UInt r1, UInt f2, UInt opc2 )
+{
+   UInt theInstr;
+   assert(r1   < 0x20);
+   assert(f2   < 0x20);
+   assert(opc2 < 0x400);
+   switch (opc2) {
+   case 144:  // mtcrf
+      assert(f2 < 0x100);
+      f2 = f2 << 1;
+      break;
+   case 339:  // mfspr
+   case 371:  // mftb
+   case 467:  // mtspr
+      assert(f2 < 0x400);
+      // re-arrange split field
+      f2 = ((f2>>5) & 0x1F) | ((f2 & 0x1F)<<5);
+      break;
+   default: assert(0);
+   }
+   theInstr = ((31<<26) | (r1<<21) | (f2<<11) | (opc2<<1));
+   return theInstr;
+}
+static UInt mkFormMD ( UInt opc1, UInt r1, UInt r2,
+                       UInt imm1, UInt imm2, UInt opc2 )
+{
+   UInt theInstr;
+   assert(opc1 < 0x40);
+   assert(r1   < 0x20);
+   assert(r2   < 0x20);
+   assert(imm1 < 0x40);
+   assert(imm2 < 0x40);
+   assert(opc2 < 0x08);
+   imm2 = ((imm2 & 0x1F) << 1) | (imm2 >> 5);
+   theInstr = ((opc1<<26) | (r1<<21) | (r2<<16) |
+               ((imm1 & 0x1F)<<11) | (imm2<<5) |
+               (opc2<<2) | ((imm1 >> 5)<<1));
+   return theInstr;
+}
+static UInt mkFormXO ( UInt opc1, UInt r1, UInt r2,
+                       UInt r3, UInt b10, UInt opc2, UInt b0 )
+{
+   UInt theInstr;
+   assert(opc1 < 0x40);
+   assert(r1   < 0x20);
+   assert(r2   < 0x20);
+   assert(r3   < 0x20);
+   assert(b10  < 0x2);
+   assert(opc2 < 0x200);
+   assert(b0   < 0x2);
+   theInstr = ((opc1<<26) | (r1<<21) | (r2<<16) |
+               (r3<<11) | (b10 << 10) | (opc2<<1) | (b0));
+   return theInstr;
+}
+
+static UInt gen_lis_r_N ( UInt r, UInt N ) {
+   return mkFormD(15, r, 0, N & 0xFFFF); /* lis r,r,N */
+}
+static UInt gen_ori_r_r_N ( UInt r, UInt N ) {
+   return mkFormD(24, r, r, N & 0xFFFF); /* ori r,r,N */
+}
+static UInt gen_addi_rd_rs_N ( UInt rd, UInt rs, UInt N ) {
+   assert(rs != 0);
+   return mkFormD(14, rd, rs, N & 0xFFFF); /* addi rd,rs,N */
+}
+static UInt gen_crorc_6_6_6 ( void ) {
+   return 0x4CC63342; /* crorc 6,6,6 */
+}
+static UInt gen_mr_rd_rs ( UInt rd, UInt rs ) {
+   return mkFormX(31, rs, rd, rs, 444, 0); /* or rd,rs,ts */
+}
+static UInt gen_bl_next ( void ) {
+   return 0x48000005; /* bl .+4 */
+}
+static UInt gen_mflr_r ( UInt r ) {
+   return mkFormXFX(r, 8, 339); /* mflr r */
+}
+static UInt gen_mtlr_r ( UInt r ) {
+   return mkFormXFX(r, 8, 467); /* mtlr r */
+}
+static UInt gen_blr ( void ) {
+   return 0x4E800020; /* blr */
+}
+__attribute__((unused))
+static UInt gen_blrl ( void ) {
+   return 0x4E800021; /* blrl */
+}
+static UInt gen_add_r_N ( UInt r, UInt N ) {
+   return mkFormD(14, r, r, N & 0xFFFF); /* addi r,r,N */
+}
+static UInt gen_cmpli_cr7_r_N ( UInt r, UInt N ) {
+   return mkFormD(10, 7<<2, r, N & 0xFFFF); /* cmpli cr7,r,N */
+}
+static UInt gen_bne_cr7_delta ( UInt delta ) {
+   return 0x409E0000 | (delta & 0x0000FFFC); /* bne- cr7,delta */
+}
+__attribute__((unused))
+static UInt gen_beq_cr7_delta ( UInt delta ) {
+   return 0x419E0000 | (delta & 0x0000FFFC); /* beq- cr7,delta */
+}
+static UInt gen_sc ( void ) {
+   return 0x44000002; /* sc */
+}
+static UInt gen_lwz_rd_off_ra ( UInt rd, UInt off, UInt ra ) {
+   return mkFormD(32, rd, ra, off); /* lwz rd, off(ra) */
+}
+static UInt gen_add_rd_rL_rR (UInt rd, UInt rsrcL, UInt rsrcR ) {
+   return mkFormXO(31, rd, rsrcL, rsrcR, 0, 266, 0);
+}
+static UInt gen_subf_rd_rL_rR (UInt rd, UInt rsrcL, UInt rsrcR ) {
+   return mkFormXO(31, rd, rsrcL, rsrcR, 0, 40, 0);
+}
+
+static Int emit_insn ( UInt* code, Int ix, UInt insn ) {
+   code[ix++] = insn;
+   return ix;
+}
+static Int emit_li32 ( UInt* code, Int ix, UInt rd, UInt imm32 ) {
+   code[ix++] = gen_lis_r_N(rd, imm32 >> 16);
+   code[ix++] = gen_ori_r_r_N(rd, imm32 & 0xFFFF);
+   return ix;
+}
+static Int emit_dosc ( UInt* code, Int ix ) {
+   /* Generate code to do a syscall and continue at the next insn.
+      Note: trashes r29. */
+   code[ix++] = gen_crorc_6_6_6();
+   code[ix++] = gen_bl_next();
+   code[ix++] = gen_mflr_r(29);
+   code[ix++] = gen_add_r_N(29,16);
+   code[ix++] = gen_mtlr_r(29);
+   code[ix++] = gen_sc();
+   return ix;
+}
+
+/* Generate 64-bit insns */
+static Int emit_li64 ( UInt* code, Int ix, UInt rd, ULong imm64 ) {
+   if (imm64 >= 0xFFFFFFFF80000000ULL || imm64 < 0x80000000ULL) {
+      // sign-extendable from 32 bits
+      // addis rd,r0,(imm64>>16) => lis rd, (imm64>>16)
+      code[ix++] = mkFormD(15, rd, 0, (imm64>>16) & 0xFFFF);
+      // ori rd, rd, (imm64 & 0xFFFF)
+      code[ix++] = mkFormD(24, rd, rd, imm64 & 0xFFFF);
+   } else {
+      // load high word
+      // lis rd, (imm64>>48) & 0xFFFF
+      code[ix++] = mkFormD(15, rd, 0, (imm64>>48) & 0xFFFF);
+      // ori rd, rd, (imm64>>32) & 0xFFFF
+      code[ix++] = mkFormD(24, rd, rd, (imm64>>32) & 0xFFFF);
+      // shift rd low word to high word => rldicr
+      code[ix++] = mkFormMD(30, rd, rd, 32, 31, 1);
+      // load low word
+      // oris rd, rd, (imm64>>16) & 0xFFFF
+      code[ix++] = mkFormD(25, rd, rd, (imm64>>16) & 0xFFFF);
+      // ori rd, rd, (imm64) & 0xFFFF
+      code[ix++] = mkFormD(24, rd, rd, imm64 & 0xFFFF);
+   }
+   return ix;
+}
+static UInt gen_ld_rd_off_ra ( UInt rd, UInt off, UInt ra ) {
+   assert((off & 3) == 0);
+   return mkFormD(58, rd, ra, off); /* ld rd, off(ra) */
+}
+
+static UInt compute_adler32 ( void* addr, UWord len )
+{
+   UInt   s1 = 1;
+   UInt   s2 = 0;
+   UChar* buf = (UChar*)addr;
+   while (len > 0) {
+      s1 += buf[0];
+      s2 += s1;
+      s1 %= 65521;
+      s2 %= 65521;
+      len--;
+      buf++;
+   }
+   return (s2 << 16) + s1;
+}
+
+
+/* -------------------------------------------------------------- */
+/* ---                                                        --- */
+/* --- BEGIN write bootstrap loader into child process        --- */
+/* ---                                                        --- */
+/* -------------------------------------------------------------- */
+
+/* From using truss, __loadx is used to load a module into a running
+   process in 32-bit mode, and kload in 64-bit mode.  __loadx is
+   simple: it returns a pointer to a standard function descriptor to
+   the entry point.
+
+   kload isn't: it returns a pointer which, from examination of
+   /proc/<pid>/maps, doesn't point into the loaded object image.  It
+   does appear to point to some kind of struct, words [4] and [6] of
+   which do point into the loaded object image.  From comparison with
+   /proc/<pid>/maps, they are respectively the actual VMAs of the text
+   and data sections of the loaded module.
+
+   Knowing this it is possible to find the entry point descriptor:
+   - figure out where the auxiliary header is.  We have a pointer to
+     the start of the mapped text section, so just add the size of
+     the XCOFF file header to that.
+   - figure out the data bias.  We know the avma of the data section;
+     and the svma of it is in the auxiliary header in field
+     o_data_start.  The data bias is therefore the difference between
+     them.
+   - The auxiliary header also gives the svma of the entry point
+     descriptor; (o_entry); therefore its avma is o_entry + the data
+     bias.
+
+   ULong* kr  = (result of kload)
+   // r3 is this value
+
+   AOUTHDR* aux = kr[4] (text_avma) + 24 (size of XCOFF file header);
+   // ld 9,32(3)     kr[4]
+   // addi 9,9,24    + 24
+   // 9=aux
+
+   ULong data_avma = kr[6];
+   // ld 11,48(3)    kr[6]
+   // 9=aux
+   // 11=data_avma
+
+   ULong data_svma = aux->o_data_start;
+   // ld 0,16(9)     aux->o_data_start
+   // 9=aux
+   // 11=data_avma
+   // 0=data_svma
+
+   ULong data_bias = data_avma - data_svma;
+   // subf 11,0,11
+   // 9=aux
+   // 11=data_bias
+   // 0=data_svma
+
+   ULong ent_svma = (ULong)aux->o_entry;
+   // ld 9,80(9)   aux->o_entry
+   // 9=ent_svma
+   // 11=data_bias
+   // 0=data_svma
+
+   ULong ent_avma = ent_svma + data_bias;
+   // add 10,9,11
+   // 9=ent_svma
+   // 11=data_bias
+   // 0=data_svma
+   // 10=ent_avma
+*/
+
+#define LAUNCHER_SYSENT_SIZE 100000
+static char sysent_buf[LAUNCHER_SYSENT_SIZE];
+
+/* The executable loaded must have no more than N_LDINFOs direct
+   shared-object dependencies.  Just increase this value and rebuild,
+   if you ever run out.  We have two arrays, one for each kind of
+   target process. */
+#define N_LDINFOs 1000
+static  struct __ld_info32  ld_info32_array[N_LDINFOs];
+static  struct __ld_info64  ld_info64_array[N_LDINFOs];
+
+
+static 
+UChar* bootstrap_errmsg 
+         = "\nvalgrind: bootstrap loader failed.  Cannot continue.\n\n";
+
+
+/* Write the bootstrap loader and associated data (iow, an
+   AIX5Bootblock structure) into CHILD, so that when
+   ptrace-detached, it will continue by loading TOOLNAME and
+   continuing with that.  Returns NULL on success or an error string
+   on failure. */
+
+static char* write_bootstrap_loader_into_child 
+                ( Child* child, char* toolfile )
+{
+   /* ------ STEP 1: Fill in most parts of the bootblock. ------ */
+
+   /* All parts except code[], off_zdata and len_zdata. */
+
+   AIX5Bootblock block;
+
+   VG_(debugLog)(1, "launcher", "parent: size of bootblock is %ld\n",
+                    sizeof(AIX5Bootblock));
+
+   assert(IS_8_ALIGNED( sizeof(AIX5Bootblock) ));
+
+   memset(&block, 0, sizeof(block));
+
+   /* --- OFFSETS--- */
+
+   /* off_zdata not known yet */
+   /* len_zdata not known yet */
+
+   /* --- SYSCALL NUMBERS --- */
+
+   /* Read some system call entries from the child's
+      /proc/<pid>/sysent file. */
+   char        sysent_name[50];
+   FILE*       sysent_file;
+   int         sysent_used = 0;
+   prsysent_t* sysent_hdr;
+   int         i;
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: reading child's /proc/../sysent\n");
+
+   sprintf(sysent_name, "/proc/%d/sysent", child->pid);
+   sysent_file = fopen(sysent_name, "r");
+   if (sysent_file == NULL)
+      return "Can't open child's /proc/<pid>/sysent file";
+
+   sysent_used = fread(sysent_buf, 1, LAUNCHER_SYSENT_SIZE, sysent_file);
+   if (sysent_used == 0)
+      return "Error reading child's /proc/<pid>/sysent file";
+   if (sysent_used == LAUNCHER_SYSENT_SIZE)
+      return "LAUNCHER_SYSENT_SIZE is too low; increase and recompile";
+   assert(sysent_used > 0 && sysent_used < LAUNCHER_SYSENT_SIZE);
+
+   fclose(sysent_file);
+
+   sysent_hdr = (prsysent_t*)&sysent_buf[0];
+
+   /* Find some syscall numbers for the child. */
+   Int __nr__getpid = -1;
+   Int __nr_kwrite  = -1;
+   Int __nr___loadx = -1; /* 32-bit child only */
+   Int __nr_kload   = -1; /* 64-bit child only */
+   Int __nr__exit   = -1;
+   Int __nr_open    = -1;
+   Int __nr_kread   = -1;
+   Int __nr_close   = -1;
+
+   for (i = 0; i < sysent_hdr->pr_nsyscalls; i++) {
+      char* name = &sysent_buf[ sysent_hdr->pr_syscall[i].pr_nameoff ];
+      int   nmbr = sysent_hdr->pr_syscall[i].pr_number;
+      if (0 == strcmp(name, "_getpid"))
+         __nr__getpid = nmbr;
+      if (0 == strcmp(name, "kwrite"))
+          __nr_kwrite = nmbr;
+      if (0 == strcmp(name, "__loadx"))
+          __nr___loadx = nmbr;
+      if (0 == strcmp(name, "kload"))
+          __nr_kload = nmbr;
+      if (0 == strcmp(name, "_exit"))
+          __nr__exit = nmbr;
+      if (0 == strcmp(name, "open"))
+          __nr_open = nmbr;
+      if (0 == strcmp(name, "kread"))
+          __nr_kread = nmbr;
+      if (0 == strcmp(name, "close"))
+          __nr_close = nmbr;
+   }
+
+   if (__nr__getpid == -1 
+       || __nr_kwrite == -1 
+       || ((!child->is64) && __nr___loadx == -1)
+       || ((child->is64) && __nr_kload == -1)
+       || __nr__exit == -1
+       || __nr_open == -1 
+       || __nr_kread == -1 
+       || __nr_close == -1)
+      return "can't establish syscall #s needed for bootstrap";
+
+   block.__NR_getpid = __nr__getpid;
+   block.__NR_write  = __nr_kwrite;
+   block.__NR_exit   = __nr__exit;
+   block.__NR_open   = __nr_open;
+   block.__NR_read   = __nr_kread;
+   block.__NR_close  = __nr_close;
+
+   /* --- REGS --- */
+
+   /* Continue by copying out the child's current integer register
+      state. */
+   VG_(debugLog)(1, "launcher", 
+                    "parent: reading child's int registers\n");
+
+   Bool err = ptrace_get_iregs_pc_cr_lr_ctr_xer 
+                 ( child, &block.iregs_pc_cr_lr_ctr_xer[0] );
+   if (err)
+      return "read of child's int registers failed";
+
+   /* --- CODE --- */
+
+   /* We'll leave that till last (is difficult). */
+
+   /* --- ERRMSG --- */
+
+   if (1 + strlen(bootstrap_errmsg) > N_BOOTBLOCK_ERRMSG)
+      return "bootstrap error message won't fit in bootblock";
+
+   for (i = 0; bootstrap_errmsg[i]; i++)
+      block.errmsg[i] = bootstrap_errmsg[i];
+   assert(i <= N_BOOTBLOCK_ERRMSG);
+
+   /* --- TOOLFILE --- */
+
+   if (1 + strlen(toolfile) > N_BOOTBLOCK_TOOLFILE)
+      return "tool file path is too long, won't fit in bootblock";
+
+   for (i = 0; toolfile[i]; i++)
+      block.toolfile[i] = toolfile[i];
+   assert(i <= N_BOOTBLOCK_TOOLFILE);
+
+
+   /* ------ STEP 2: Generate the bootblock code. ------ */
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: creating bootblock code ..\n");
+
+   /* This is the tricky bit.  The resulting code has to be position
+      independent since we don't yet know where it's going to be
+      placed.  The code is entered with r31 pointing at the bootblock.
+      r29-31 are callee-saved, so presumably they don't get trashed
+      across syscalls.  r30 is used as scratch, and r29 is also used
+      as scratch by 'emit_dosc'. */
+
+   /* Preliminaries: to do a syscall, we have to do 'crorc 6,6,6' and
+      put the continuation address in LR, which is a bit of a drag.
+      Hence the following macro:
+
+         SYSCALL_SEQUENCE = crorc 6,6,6
+                            bl   .+4
+                            mflr 29
+                            addi 29,29,16
+                            mtlr 29
+                            sc
+
+      Also: 'imm' is an imaginary instruction to get a 32-bit literal into
+      a register.  It's really li followed by oris.
+   */
+
+   /* So, the code.  First, prepare for and do a _loadx syscall, to
+      get the tool aboard:
+         imm  2, __NR__loadx
+         imm  3, VKI_DL_LOAD
+         imm  4, 0
+         mr   5, 4
+         addi 6, 31, offset_of_toolfile
+         mr   7, 4
+         mr   8, 4
+         mr   9, 4
+         mr   10,4
+         SYSCALL_SEQUENCE
+
+      If the syscall failed, r4 will be nonzero.  Branch elsewhere if so.
+         cmpi 4, 0
+         bne  error
+   */
+   int ix = 0;
+
+#  if 1
+#  define TRAP \
+      do { \
+         ix=emit_insn( &block.code[0],ix, 0x7fe00008 ); } \
+      while (0)
+#  define SEGV \
+      do { \
+         if (child->is64) { \
+            ix=emit_li64( &block.code[0],ix, 28,0); \
+            ix=emit_insn( &block.code[0],ix, \
+                          gen_ld_rd_off_ra(27,0xfffc,28)); \
+         } else { \
+            ix=emit_li32( &block.code[0],ix, 28,0); \
+            ix=emit_insn( &block.code[0],ix, \
+                          gen_lwz_rd_off_ra(27,0xffff,28)); \
+        } \
+      } while (0)
+#  define ILL \
+      do { \
+         ix=emit_insn( &block.code[0],ix, 0 ); } \
+      while (0)      
+#  endif
+
+   if (child->is64) {
+
+      /* 64-bit sequence */
+      /* Set up for 'sys_kload(toolfile, 0, 0)'
+         li64  2, __NR_kload
+         addi  3, 31, offset_toolfile
+         li64  4, 0
+         mr    5, 4
+         mr    6, 4
+         mr    7, 4
+         mr    8, 4
+         mr    9, 4
+         mr    10,4
+         SYSCALL_SEQUENCE
+
+         // if kload failed, r3 will hold zero
+         cmpdi 3,0
+         beq error
+
+         // from result of kload, figure out entry point address
+         // as described above
+         ld   9,32(3)
+         addi 9,9,24
+         ld   11,48(3)
+         ld   0,16(9)
+         subf 11,0,11
+         ld   9,80(9)
+         add  10,9,11  // r10 is entry descriptor avma
+
+         void(*fn)(void*) = (void(*)(void*))ent_avma;
+         fn();
+         ld   9,0(10)
+         mtlr 9
+         ld   2,8(10)
+         ld   11,16(10)
+         mr   3,31  // arg to pass
+         blr
+      */
+      ix = emit_li64( &block.code[0],ix, 2, __nr_kload );
+      ix = emit_insn( &block.code[0],ix, 
+                      gen_addi_rd_rs_N(3,31,offsetof(AIX5Bootblock,toolfile)));
+      ix = emit_li64( &block.code[0],ix, 4, 0 );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(5,4) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(6,4) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(7,4) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(8,4) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(9,4) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(10,4) );
+      ix = emit_dosc( &block.code[0],ix );
+
+      ix = emit_insn( &block.code[0],ix, gen_cmpli_cr7_r_N(3,0) );
+      Int ix_beq = ix; /* Patch this later */
+      ix = emit_insn( &block.code[0],ix, 0 );
+
+      ix = emit_insn( &block.code[0],ix, gen_ld_rd_off_ra( 9, 32, 3 ) );
+      ix = emit_insn( &block.code[0],ix, gen_addi_rd_rs_N( 9, 9, 24 ) );
+      ix = emit_insn( &block.code[0],ix, gen_ld_rd_off_ra( 11, 48, 3 ) );
+      ix = emit_insn( &block.code[0],ix, gen_ld_rd_off_ra( 0, 16, 9 ) );
+      ix = emit_insn( &block.code[0],ix, gen_subf_rd_rL_rR( 11, 0, 11 ) );
+      ix = emit_insn( &block.code[0],ix, gen_ld_rd_off_ra( 9, 80, 9 ) );
+      ix = emit_insn( &block.code[0],ix, gen_add_rd_rL_rR( 10, 9, 11 ) );
+
+      ix = emit_insn( &block.code[0],ix, gen_ld_rd_off_ra( 9, 0, 10 ) );
+      ix = emit_insn( &block.code[0],ix, gen_mtlr_r( 9 ) );
+      ix = emit_insn( &block.code[0],ix, gen_ld_rd_off_ra( 2, 8, 10 ) );
+      ix = emit_insn( &block.code[0],ix, gen_ld_rd_off_ra( 11, 16, 10 ) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(3, 31) );
+      ix = emit_insn( &block.code[0],ix, gen_blr() );
+      TRAP;
+      assert(ix <= N_BOOTBLOCK_INSNS);
+
+      /* error:
+         We get here if the kload syscall fails.  Write a terse message
+         to stderr saying so, then exit, carrying the error code of the
+         kload call.  The latter is saved in r30 across the write() call.
+            mr   30,4 (4 contains the error result from kload)
+            imm  2, __NR_write
+            imm  3,2 (2=stderr)
+            addi 4, 31, offset_of_errormsg
+            imm  5, length(errormsg)
+            SYSCALL_SEQUENCE
+            imm  2, __NR_exit
+            mr   3, 30
+            SYSCALL_SEQUENCE
+   
+         Well, we shouldn't be alive here.  But just in case we do, put
+         a zero word, which will generate SIGILL and definitely stop the
+         party.
+            .word 0
+      */
+      /* fill in the conditional jump */
+      (void)emit_insn( &block.code[0],ix_beq, 
+                                      gen_beq_cr7_delta(4*(ix-ix_beq)));
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(30,4) );
+      ix = emit_li64( &block.code[0],ix, 2, __nr_kwrite);
+      ix = emit_li64( &block.code[0],ix, 3, 2);
+      ix = emit_insn( &block.code[0],ix, 
+                      gen_addi_rd_rs_N(4,31,offsetof(AIX5Bootblock,errmsg)));
+      ix = emit_li64( &block.code[0],ix, 5, strlen(bootstrap_errmsg));
+      ix = emit_dosc( &block.code[0],ix );
+      ix = emit_li64( &block.code[0],ix, 2, __nr__exit);
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(3,30) );
+      ix = emit_dosc( &block.code[0],ix );
+      ix = emit_insn( &block.code[0],ix, 0 );
+      assert(ix <= N_BOOTBLOCK_INSNS);
+
+   } else {
+
+      /* 32-bit sequence */
+      ix = emit_li32( &block.code[0],ix, 2, __nr___loadx );
+      ix = emit_li32( &block.code[0],ix, 3, VKI_DL_LOAD );
+      ix = emit_li32( &block.code[0],ix, 4, 0 );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(5,4) );
+      ix = emit_insn( &block.code[0],ix, 
+                      gen_addi_rd_rs_N(6,31,offsetof(AIX5Bootblock,toolfile)));
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(7,4) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(8,4) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(9,4) );
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(10,4) );
+      ix = emit_dosc( &block.code[0],ix );
+      ix = emit_insn( &block.code[0],ix, gen_cmpli_cr7_r_N(4,0) );
+      Int ix_bne = ix; /* Patch this later */
+      ix = emit_insn( &block.code[0],ix, 0 );
+      assert(ix <= N_BOOTBLOCK_INSNS);
+
+      /* Looks like we're good.  r3 now points at a standard function
+         descriptor for the entry point of the module we just loaded.
+         Load r2/r11 from the descriptor, then put the address of the
+         bootstrap area in r3, and jump to the code address.  Not a
+         call -- we don't intend to return here.  Note, must use r30
+         as scratch here since r31 is live.
+            lwz  30, 0(3)
+            mtlr 30
+            lwz  2, 4(3)
+            lwz  11, 8(3)
+            mr   3, 31
+            blr
+      */
+      ix = emit_insn( &block.code[0],ix, gen_lwz_rd_off_ra(30, 0, 3));
+      ix = emit_insn( &block.code[0],ix, gen_mtlr_r(30) );
+      ix = emit_insn( &block.code[0],ix, gen_lwz_rd_off_ra( 2, 4, 3));
+      ix = emit_insn( &block.code[0],ix, gen_lwz_rd_off_ra(11, 8, 3));
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(3,31));
+      ix = emit_insn( &block.code[0],ix, gen_blr() );
+      assert(ix <= N_BOOTBLOCK_INSNS);
+
+      /* error:
+         We get here if the _loadx syscall fails.  Write a terse message
+         to stderr saying so, then exit, carrying the error code of the
+         _loadx call.  The latter is saved in r30 across the write() call.
+            mr   30,4 (4 contains the error result from __loadx)
+            imm  2, __NR_write
+            imm  3,2 (2=stderr)
+            addi 4, 31, offset_of_errormsg
+            imm  5, length(errormsg)
+            SYSCALL_SEQUENCE
+            imm  2, __NR_exit
+            mr   3, 30
+            SYSCALL_SEQUENCE
+   
+         Well, we shouldn't be alive here.  But just in case we do, put
+         a zero word, which will generate SIGILL and definitely stop the
+         party.
+            .word 0
+      */
+      /* fill in the conditional jump */
+      (void)emit_insn( &block.code[0],ix_bne, 
+                                      gen_bne_cr7_delta(4*(ix-ix_bne)));
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(30,4) );
+      ix = emit_li32( &block.code[0],ix, 2, __nr_kwrite);
+      ix = emit_li32( &block.code[0],ix, 3, 2);
+      ix = emit_insn( &block.code[0],ix, 
+                      gen_addi_rd_rs_N(4,31,offsetof(AIX5Bootblock,errmsg)));
+      ix = emit_li32( &block.code[0],ix, 5, strlen(bootstrap_errmsg));
+      ix = emit_dosc( &block.code[0],ix );
+      ix = emit_li32( &block.code[0],ix, 2, __nr__exit);
+      ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(3,30) );
+      ix = emit_dosc( &block.code[0],ix );
+      ix = emit_insn( &block.code[0],ix, 0 );
+      assert(ix <= N_BOOTBLOCK_INSNS);
+
+   }
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: .. %d instructions emitted\n", ix);
+
+#  if 0
+   for (i = 0; i < ix; i++) {
+      if (0) printf("code[%d] = 0x%08x\n", i, block.code[i]);
+      char buff[100];
+      sprintf(buff, "echo 0x%x | ./ascii2u32", block.code[i]);
+      system(buff);
+   }
+#  endif
+
+   /* ------ STEP 3: Find out where to place stuff in the child. ------ */
+
+   /* We'll have to hijack some space in the data section of the main
+      executable.  First off, find the first and last pages of said
+      data section.  We can't use the text section, because the child
+      is unable to write to its own text section, to undo the
+      compression of the hijacked page.  We can't use the stack
+      because it appears, although stacks in AIX 5.3 appear to be
+      executable, the child gets SIGKILL'd after the ptrace detach if
+      its program counter is pointing into its stack.  The data
+      section of the main executable appears to be executable, though,
+      so use that.
+
+      This requires wading though the list of loaded modules in the
+      child, to find the main executable. */
+
+   long lr;
+   if (child->is64) {
+      lr = ptrace64(PT_LDINFO, (ULong)child->pid,
+                               (ULong)(UWord)&ld_info64_array, 
+                               sizeof(ld_info64_array), 0/*ignored*/);
+   } else {
+      lr = ptrace64(PT_LDINFO, (ULong)child->pid,
+                               (ULong)(UWord)&ld_info32_array, 
+                               sizeof(ld_info32_array), 0/*ignored*/);
+   }
+   VG_(debugLog)(1, "launcher", "parent: ptrace PT_LDINFO got %ld\n", lr);
+   if (lr == -1)
+      return "ptrace(PT_LDINFO, ...) failed";
+   else 
+      assert(lr == 0);
+
+   /* We have to iterate through the entire array to close the object
+      files that this has opened.  Duh. */
+   if (child->is64) {
+      char* p = (char*)&ld_info64_array;
+      while (1) {
+         struct __ld_info64* info = (struct __ld_info64*)p;
+      
+         VG_(debugLog)(1, 
+            "launcher", "parent: text 0x%llx-0x%llx data 0x%llx-0x%llx\n",
+            (Addr64)info->ldinfo_textorg, 
+            (Addr64)info->ldinfo_textorg + (Addr64)info->ldinfo_textsize,
+            (Addr64)info->ldinfo_dataorg, 
+            (Addr64)info->ldinfo_dataorg + (Addr64)info->ldinfo_datasize
+         );
+
+         Int ir = close(info->_file._ldinfo_fd);
+         assert(ir == 0);
+         /* The last entry in the array is marked by having a zero
+            offset-link field. */
+         if (info->ldinfo_next == 0)
+            break;
+         p += info->ldinfo_next;
+      }
+   } else {
+      char* p = (char*)&ld_info32_array;
+      while (1) {
+         struct __ld_info32* info = (struct __ld_info32*)p;
+      
+         VG_(debugLog)(1, 
+            "launcher", "parent: text 0x%llx-0x%llx data 0x%llx-0x%llx\n",
+            (Addr64)(UWord)info->ldinfo_textorg, 
+            (Addr64)(UWord)info->ldinfo_textorg + info->ldinfo_textsize,
+            (Addr64)(UWord)info->ldinfo_dataorg, 
+            (Addr64)(UWord)info->ldinfo_dataorg + info->ldinfo_datasize
+         );
+
+         Int ir = close(info->_file._ldinfo_fd);
+         assert(ir == 0);
+         /* The last entry in the array is marked by having a zero
+            offset-link field. */
+         if (info->ldinfo_next == 0)
+            break;
+         p += info->ldinfo_next;
+      }
+   }
+
+   /* The first entry in that array -- and it is guaranteed to to have
+      at least one entry -- is that of the the main executable.  We
+      need to put our bootblock in one of the pages the main
+      executable's data segment.  The abovementioned AIX 'ptrace'
+      documentation says:
+
+        To allow a debugger to generate code more easily (in order to
+        handle fast trap instructions, for example), memory from the
+        end of the main program up to the next segment boundary can be
+        modified. That memory is read-only to the process but can be
+        modified by the debugger.
+
+      which would be great if it actually worked reliably; but not so.
+      On AIX 5.2 this is true, but on 5.3 it appears to be impossible
+      to read or write (via ptrace) anything beyond the last page of
+      the executable's text section.
+   */
+   Addr64 c_cand_text_first, c_cand_text_last;
+
+   if (child->is64) {
+      c_cand_text_first
+         = (Addr64)ld_info64_array[0].ldinfo_dataorg;
+      c_cand_text_last 
+         = c_cand_text_first
+           + ld_info64_array[0].ldinfo_datasize - 1;
+   } else {
+      c_cand_text_first
+         = (Addr64)(UWord)ld_info32_array[0].ldinfo_dataorg;
+      c_cand_text_last 
+         = c_cand_text_first
+           + ld_info32_array[0].ldinfo_datasize - 1;
+   }
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: candidate first 0x%llx last 0x%llx\n",
+                    c_cand_text_first, c_cand_text_last);
+
+   /* Page align the text section limits. */
+   Addr64 c_first_page = ROUNDDN_PAGE( c_cand_text_first );
+   Addr64 c_last_page  = ROUNDDN_PAGE( c_cand_text_last );
+
+   /* It's safe to try out any page p satisfying
+         c_first_page <= p && p <= c_last_page
+   */
+
+   /* CHOOSE A PAGE.  Do a test compression of available pages until
+      we find one for which compression yields enough free space to
+      put the bootblock in. */
+   Int    zsize;
+   Addr64 c_chosen_page = 0;
+   Addr64 c_page;
+   UChar  p_page_unzbuf[PAGE_SIZE];
+   UChar  p_page_unzbuf2[PAGE_SIZE];
+   UChar  p_page_zbuf[PAGE_SIZE + 384 + 8/*paranoia*/];
+
+   for (c_page = c_first_page; c_page <= c_last_page; c_page += PAGE_SIZE) {
+      assert(IS_PAGE_ALIGNED(c_page));
+      err = ptrace_read_page( child, p_page_unzbuf, c_page );
+      if (err)
+         return "read of page from child failed(1)";
+      zsize = Huffman_Compress(p_page_unzbuf, p_page_zbuf, PAGE_SIZE);
+      assert(zsize >= 0 && zsize <= PAGE_SIZE + 384);
+
+      /* Do a test decompression, to check the compress/decompress
+         cycle works properly */
+      Huffman_Uncompress( p_page_zbuf, p_page_unzbuf2, 
+                          PAGE_SIZE + 384, PAGE_SIZE);
+      assert(0 == memcmp(p_page_unzbuf, p_page_unzbuf2, PAGE_SIZE));
+
+      VG_(debugLog)(1, "launcher", 
+                       "parent: page 0x%llx has %d usable bytes\n", 
+                       c_page, PAGE_SIZE - zsize);
+
+      if ( (Int)(PAGE_SIZE - zsize) 
+           >= (Int)sizeof(AIX5Bootblock)+8/*paranoia*/) {
+         c_chosen_page = c_page;
+         break;
+      }
+   }
+
+   if (c_chosen_page == NULL)
+      return "can't find a page with enough free space for bootblock";
+
+   /* Compress the chosen page, leaving the compressed data at the
+      start of the page, and put the bootblock at the end of the
+      page. */
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: reading page at 0x%llx\n", c_chosen_page);
+
+   err = ptrace_read_page( child, p_page_unzbuf, c_chosen_page );
+   if (err)
+      return "read of page from child failed(2)";
+
+   block.adler32 = compute_adler32( p_page_unzbuf, PAGE_SIZE );
+   VG_(debugLog)(1, "launcher", 
+                    "parent: adler32 of unz page is 0x%x\n", block.adler32);
+
+   memset(p_page_zbuf, 0, sizeof(p_page_zbuf));
+   zsize = Huffman_Compress(p_page_unzbuf, p_page_zbuf, PAGE_SIZE);
+   assert(zsize >= 0 && zsize <= PAGE_SIZE + 384);
+
+   assert(PAGE_SIZE - zsize >= sizeof(AIX5Bootblock)+8/*paranoia*/);
+
+   UChar* p_dst = p_page_zbuf   + PAGE_SIZE - sizeof(AIX5Bootblock);
+   Addr64 c_dst = c_chosen_page + PAGE_SIZE - sizeof(AIX5Bootblock);
+   assert(IS_8_ALIGNED(c_dst));
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: free space starts at 0x%llx in child\n",
+                    c_chosen_page + zsize);
+   VG_(debugLog)(1, "launcher", 
+                    "parent: bootblock will be at 0x%llx in child\n",
+                    c_dst);
+
+   *(AIX5Bootblock*)p_dst = block;
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: writing page at 0x%llx\n", c_chosen_page);
+
+   err = ptrace_write_page( child, c_chosen_page, p_page_zbuf );
+   if (err)
+      return "write of page to child failed";
+
+   /* Do a test read back to ensure ptrace didn't screw up. */
+
+   err = ptrace_read_page( child, p_page_unzbuf2, c_chosen_page );
+   if (err)
+      return "test read back of boot page failed (1)";
+   if (0 != memcmp(p_page_zbuf, p_page_unzbuf2, PAGE_SIZE))
+      return "test read back of boot page failed (2)";
+
+   /* Finally .. set the program counter so that when we detach, our
+      magic stub is run, not the original program. */
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: set child's pc to 0x%llx\n",
+                    c_dst + offsetof(AIX5Bootblock,code) );
+   err = ptrace_put_pc ( child, c_dst + offsetof(AIX5Bootblock,code) );
+   if (err)
+      return "write of new initial pc into child failed";
+
+   VG_(debugLog)(1, "launcher", 
+                    "parent: set child's r31 to 0x%llx\n", c_dst);
+   err = ptrace_put_r31 ( child, c_dst );
+   if (err)
+      return "write of new r31 into child failed";
+
+   return NULL; /* success */
+}
+
+
+/* -------------------------------------------------------------- */
+/* ---                                                        --- */
+/* --- END write bootstrap loader into child process          --- */
+/* ---                                                        --- */
+/* -------------------------------------------------------------- */
+
+static void barf ( int exitcode, char* argv0, char* msg )
+{
+   fprintf(stderr, "%s: %s\n", argv0, msg);
+   exit(exitcode);
+}
+
+int main ( int argc, char** argv, char** envp )
+{
+   Child child;
+   Int i, loglevel;
+   const char *toolname = NULL;
+   const char *clientname = NULL;
+
+   /* First, look in our own /proc/<pid>/sysent file to find
+      the syscall numbers for kwrite and _getpid.  These are needed
+      to make the VG_(debugLog) usable.  We'll temporarily use
+      the sysent_buf used by write_bootstrap_loader_into_child for this
+      purpose. */
+
+   char        sysent_name[50];
+   FILE*       sysent_file;
+   int         sysent_used = 0;
+   prsysent_t* sysent_hdr;
+
+   child.pid  = 0;
+   child.is64 = False;
+
+   sprintf(sysent_name, "/proc/%d/sysent", getpid());
+   sysent_file = fopen(sysent_name, "r");
+   if (sysent_file == NULL)
+      barf(1, argv[0], "Can't open my own /proc/<pid>/sysent file");
+
+   sysent_used = fread(sysent_buf, 1, LAUNCHER_SYSENT_SIZE, sysent_file);
+   if (sysent_used == 0)
+      barf(1, argv[0], "Error reading my own /proc/<pid>/sysent file");
+   if (sysent_used == LAUNCHER_SYSENT_SIZE)
+      barf(1, argv[0], "LAUNCHER_SYSENT_SIZE is too low; increase and recompile");
+   assert(sysent_used > 0 && sysent_used < LAUNCHER_SYSENT_SIZE);
+
+   fclose(sysent_file);
+
+   sysent_hdr = (prsysent_t*)&sysent_buf[0];
+
+   /* Find some syscall numbers for the child.  Note, we copy them
+      from our own /proc/../sysent file, which isn't really right. */
+   Word __nr__getpid = -1;
+   Word __nr_kwrite  = -1;
+   for (i = 0; i < sysent_hdr->pr_nsyscalls; i++) {
+      char* name = &sysent_buf[ sysent_hdr->pr_syscall[i].pr_nameoff ];
+      int   nmbr = sysent_hdr->pr_syscall[i].pr_number;
+      if (0 == strcmp(name, "_getpid"))
+         __nr__getpid = nmbr;
+      if (0 == strcmp(name, "kwrite"))
+          __nr_kwrite = nmbr;
+   }
+   if (__nr__getpid == -1 || __nr_kwrite == -1)
+      barf(1, argv[0], "can't establish syscall #s needed for startup");
+
+   /* "Tell" m_vkiscnums about them */
+   __NR_getpid = __nr__getpid;
+   __NR_write = __nr_kwrite;
+
+   /* Right, now we're safe to start the debug logging system. */
+   /* Start the debugging-log system ASAP.  First find out how many
+      "-d"s were specified.  This is a pre-scan of the command line.
+      At the same time, look for the tool name. */
+   loglevel = 0;
+   for (i = 1; i < argc; i++) {
+      if (argv[i][0] != '-') {
+         clientname = argv[i];
+         break;
+      }
+      if (0 == strcmp(argv[i], "--")) {
+         if (i+1 < argc)
+            clientname = argv[i+1];
+         break;
+      }
+      if (0 == strcmp(argv[i], "-d"))
+         loglevel++;
+      if (0 == strncmp(argv[i], "--tool=", 7))
+         toolname = argv[i] + 7;
+   }
+
+   /* ... and start the debug logger.  Now we can safely emit logging
+      messages all through startup. */
+   VG_(debugLog_startup)(loglevel, "Stage 1");
+
+   /* Make sure we know which tool we're using */
+   if (toolname) {
+      VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname);
+   } else {
+      VG_(debugLog)(1, "launcher",
+                       "no tool requested, defaulting to 'memcheck'\n");
+      toolname = "memcheck";
+   }
+
+   /* Do some preliminary sanity checks */
+   long pagesize = sysconf(_SC_PAGESIZE);
+   if (pagesize != 4096)
+      barf(1, argv[0], "config error: sysconf(_SC_PAGESIZE) is not 4096");
+
+   assert(PAGE_SIZE == 4096); /* stay sane */
+
+   if (argc < 2 || toolname == NULL || clientname == NULL)
+      barf(1, argv[0], "usage: valgrind [args-for-valgrind] prog args"); 
+
+   /* Find the client, and figure out if it's a 32- or 64-bit
+      executable. */
+   VG_(debugLog)(1, "launcher", "searching for client in $PATH\n");
+   if (strchr(clientname, '/') == NULL)
+      clientname = find_client(clientname);
+   VG_(debugLog)(1, "launcher", "found %s\n", clientname);
+
+   Int client_exekind = examine_client ( clientname );
+   switch (client_exekind) {
+      case 32: 
+         child.is64 = False; 
+         break;
+      case 64: 
+         child.is64 = True; 
+         break;
+      default: 
+         fprintf(stderr, "%s: requested executable %s\n", 
+                         argv[0], clientname);
+         fprintf(stderr, "%s: not found, or is not a valid XCOFF32 "
+                         "or XCOFF64 executable.\n", argv[0]);
+         return 1;
+   }
+
+   VG_(debugLog)(1, "launcher", "client is an XCOFF%d executable\n", 
+                    client_exekind);
+
+   const char* valgrind_lib = VG_LIBDIR;
+   const char* platform = child.is64 ? "ppc64-aix5" : "ppc32-aix5";
+
+   VG_(debugLog)(1, "launcher", "looking for the tool file\n");
+
+   char* toolfile = malloc(strlen(valgrind_lib) 
+                    + strlen(toolname) + strlen(platform) + 3);
+   if (toolfile == NULL) {
+      fprintf(stderr,"%s: malloc of toolfile failed\n", argv[0]);
+      return 1;
+   }
+   sprintf(toolfile, "%s/%s/%s", valgrind_lib, platform, toolname);
+
+   if (!file_exists(toolfile)) {
+      fprintf(stderr,"%s: can't stat %s\n", argv[0], toolfile);
+      return 1;
+   }
+
+   /* Force the client to use a 1:1 threading model - this works
+      because the client inherits our environment. */
+   VG_(debugLog)(1, "launcher", "doing putenv(\"AIXTHREAD_SCOPE=S\")\n");
+   Int putenv_err = putenv("AIXTHREAD_SCOPE=S");
+   if (putenv_err) {
+      fprintf(stderr,"%s: putenv(\"AIXTHREAD_SCOPE=S\") failed\n", argv[0]);
+      return 1;
+   }
+
+   VG_(debugLog)(1, "launcher", "doing putenv(\"MP_SHARED_MEMORY=no\")\n");
+   putenv_err = putenv("MP_SHARED_MEMORY=no");
+   if (putenv_err) {
+      fprintf(stderr,"%s: putenv(\"MP_SHARED_MEMORY=no\") failed\n", argv[0]);
+      return 1;
+   }
+
+   /* Also, cook up the fully qualified name of this executable.  The
+      following is a kludge, but I don't see how to really get the
+      fully qualified name on AIX. */
+   char* up_n_down = "/../../bin/valgrind";
+   char* launcher = malloc(strlen(valgrind_lib)
+                           + strlen(up_n_down) + 2);
+   if (launcher == NULL) {
+      fprintf(stderr,"%s: malloc of launcher failed\n", argv[0]);
+      return 1;
+   }
+   sprintf(launcher, "%s%s", valgrind_lib, up_n_down);
+
+   if (!file_exists(launcher)) {
+      fprintf(stderr,"%s: can't stat %s\n", argv[0], launcher);
+      return 1;
+   }
+
+   /* First, fork.  
+
+      In the child, ask for a ptrace, then exec argv[2 ..].  This
+      causes the kernel to complete the exec, hence loading the
+      child, but does not start it; instead the child remains frozen
+      so that the parent can mess with it via ptrace().
+   */
+   VG_(debugLog)(1, "launcher", "doing fork()\n");
+   child.pid = fork();
+   if (child.pid == -1) {
+      fprintf(stderr,"%s: fork() failed\n", argv[0]);
+      return 1;
+   }
+
+   if (child.pid == 0) {
+      /* --- CHILD --- */
+      VG_(debugLog)(1, "launcher", "child: before ptrace\n");
+      long rl = ptrace64(PT_TRACE_ME, 0,0,0,0);
+      if (rl != 0) {
+         fprintf(stderr,"%s: child: ptrace(PT_TRACE_ME, ...) failed\n", argv[0]);
+         fprintf(stderr,"%s: ", argv[0]);
+         perror(NULL);
+         fflush(stderr);
+         _exit(1);
+      }
+      VG_(debugLog)(1, "launcher", "child: before execve\n");
+
+      /* make VALGRIND_LAUNCHER point at something plausible. */
+      VG_(debugLog)(1, "launcher", "child: launcher = %s\n", launcher);
+      int r = setenv("VALGRIND_LAUNCHER", launcher, 1/*overwrite*/);
+      if (r) {
+         /* setenv failed. */
+         fprintf(stderr,"%s: child: setenv failed\n", argv[0]);
+         fprintf(stderr,"%s: ", argv[0]);
+         perror(NULL);
+         fflush(stderr);
+         _exit(1);
+         /* NOTREACHED */
+      }
+
+      /* This is kind-of strange.  We're execvp-ing the client but
+         argv[0] is the toolname, which is irrelevant - m_main ignores
+         it.  However, setting it like this at least makes m_main's
+         view of the world (as far as the argv goes) look the same as
+         it does in Linux-land: 
+            tool-exe-name [args for V] client-name [args for client]
+      */
+      argv[0] = toolfile;
+      int ri = execvp(clientname, &argv[0]);
+      /* WE ONLY GET HERE IF execve FAILED */
+      assert(ri == -1);
+      fprintf(stderr,"%s: exec failed: %s: ", argv[0], clientname);
+      perror("");
+      return 1;
+      /* NOTREACHED */
+   }
+
+   /* --- PARENT --- */
+   VG_(debugLog)(1, "launcher", "parent: waitpid-ing for child\n");
+   int status;
+   /* Wait to hear back from the child. */
+   pid_t p2 = waitpid(child.pid, &status, 0);
+   /* We could hear back for two reasons.  (1) the exec was
+      successful, and because the child is being ptraced, it is now
+      waiting for the parent.  (2) the exec failed, and so the child
+      did _exit(). */
+   VG_(debugLog)(1, "launcher", "parent: waitpid got pid %d\n", (int)p2);
+   VG_(debugLog)(1, "launcher", "parent: waitpid got status 0x%x\n", status);
+   assert(p2 == child.pid); /* Huh?! We only have one child. */
+
+   if (WIFEXITED(status)) {
+      /* Case (2) - exec failed. */
+      fprintf(stderr, "parent: child's exec failed.\n");
+      return 0;
+   }
+
+   /* else case (1) must apply */
+   assert(WIFSTOPPED(status));
+
+   /* ------ BEGIN write bootstrap pages into child ------ */
+
+   /* In this section, if for any reason we can't continue to the
+      child-detach and so have to give up, we have to kill the child,
+      else it'll become a zombie.  That's what the code at
+      latched_error: does. */
+   char* badness 
+            = write_bootstrap_loader_into_child ( &child, toolfile );
+   /* Returns NULL if no error, else points to a string of at least
+      some descriptiveness. */
+   if (badness)
+      goto latched_error;
+
+   /* ------ END write bootstrap pages into child ------ */
+
+   VG_(debugLog)(1, "launcher", "parent: detaching child\n");
+   long lr = ptrace64(PT_DETACH, (ULong)child.pid, 0, SIGCONT, 0);
+   VG_(debugLog)(1, "launcher", "parent: detach got %ld\n", lr);
+   assert(lr == 0);
+   VG_(debugLog)(1, "launcher", "parent: waiting for child to finish\n");
+
+   p2 = waitpid(child.pid, &status, 0);
+   assert(p2 == child.pid);
+   if (0)
+      fprintf(stderr,"parent: child finished, status 0x%x 0x%x\n", 
+                     status, WEXITSTATUS(status));
+
+   if (WIFEXITED(status)) {
+      VG_(debugLog)(1, "launcher", 
+                       "parent: child finished normally, exit code %d\n",
+                       WEXITSTATUS(status));
+      return WEXITSTATUS(status);
+   } 
+   else if (WIFSIGNALED(status)) {
+      VG_(debugLog)(1, "launcher",
+                       "parent: child exited on signal %d\n",
+                       (int)WTERMSIG(status));
+      /* Since the child exited with a signal, we'd better
+         whack ourselves on the head with the same signal. */
+      kill( getpid(), (int)WTERMSIG(status) );
+      /* presumably NOTREACHED? */
+      return 0; /* This is completely bogus */
+   } 
+   else {
+      /* erm.  Can we ever get here? */
+      assert(0);
+      return 0;
+   }
+
+  latched_error:
+   /* We get here if there was some kind of problem messing with the
+      child whilst we still had it latched by ptrace.  In this case we
+      need to kill it before exiting, since otherwise it will become a
+      zombie. */
+   assert(badness);
+   fprintf(stderr, "%s: error while doing ptracery on '%s'\n",
+                   argv[0], clientname);
+   fprintf(stderr, "%s: error is: %s\n",
+                   argv[0], badness);
+   return 0; /*BOGUS*/
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end                                          launcher-aix5.c ---*/
+/*--------------------------------------------------------------------*/