summaryrefslogtreecommitdiff
path: root/coregrind/.svn/text-base/launcher-aix5.c.svn-base
diff options
context:
space:
mode:
Diffstat (limited to 'coregrind/.svn/text-base/launcher-aix5.c.svn-base')
-rw-r--r--coregrind/.svn/text-base/launcher-aix5.c.svn-base1712
1 files changed, 1712 insertions, 0 deletions
diff --git a/coregrind/.svn/text-base/launcher-aix5.c.svn-base b/coregrind/.svn/text-base/launcher-aix5.c.svn-base
new file mode 100644
index 0000000..2c41062
--- /dev/null
+++ b/coregrind/.svn/text-base/launcher-aix5.c.svn-base
@@ -0,0 +1,1712 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Launching Valgrind on AIX5. launcher-aix5.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2006-2009 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.
+
+ Neither the names of the U.S. Department of Energy nor the
+ University of California nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without prior written permission.
+*/
+
+/* 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_addis_rd_rs_N ( UInt rd, UInt rs, UInt N ) {
+ assert(rs != 0);
+ return mkFormD(15, rd, rs, N & 0xFFFF); /* addis 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);
+ if (imm32 & 0xFFFF)
+ 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:
+ addis 1, 1, -4
+ imm 2, __NR__loadx
+ imm 3, VKI_DL_LOAD
+ mr 4, 1
+ imm 5, 3<<16
+ addi 6, 31, offset_of_toolfile
+ mr 7, 4
+ mr 8, 4
+ mr 9, 4
+ mr 10,4
+ SYSCALL_SEQUENCE
+ addis 1, 1, 4
+
+ 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_insn( &block.code[0],ix,
+ gen_addis_rd_rs_N(1,1,-4) );
+ ix = emit_li32( &block.code[0],ix, 2, __nr___loadx );
+ ix = emit_li32( &block.code[0],ix, 3, VKI_DL_LOAD );
+ ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(4,1) );
+ ix = emit_li32( &block.code[0],ix, 5, 3<<16 );
+ ix = emit_insn( &block.code[0],ix,
+ gen_addi_rd_rs_N(6,31,offsetof(AIX5Bootblock,toolfile)));
+ ix = emit_li32( &block.code[0],ix, 7, 0);
+ ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(8,7) );
+ ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(9,7) );
+ ix = emit_insn( &block.code[0],ix, gen_mr_rd_rs(10,7) );
+ ix = emit_dosc( &block.code[0],ix );
+ ix = emit_insn( &block.code[0],ix,
+ gen_addis_rd_rs_N(1,1,4) );
+ 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;
+ 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 */
+
+ const char* valgrind_lib = VG_LIBDIR;
+
+ /* If there is no program to run, which will be the case if the
+ user just does "valgrind --help", etc, run a dummy do-nothing
+ program so at least the tool can get started and handle the
+ --help/--version etc. It spots the fact that this is a dummy
+ program and acts like it was started with no program, hence
+ behaving the same as the Linux ports would have. */
+ if (clientname == NULL) {
+ Int j;
+ char** new_argv;
+ const char* noop_exe_name = "no_op_client_for_valgrind";
+ const char* up_n_bindir = "/../../bin";
+ clientname = malloc(strlen(valgrind_lib) + strlen(up_n_bindir)
+ + 2 + strlen(noop_exe_name));
+ if (clientname == NULL) {
+ fprintf(stderr,"%s: malloc of clientname failed\n", argv[0]);
+ return 1;
+ }
+ sprintf(clientname, "%s%s/%s", valgrind_lib, up_n_bindir, noop_exe_name);
+ /* now we have to add it to the end of argv, which means making
+ that one word longer. How tedious. */
+ for (j = 0; argv[j]; j++)
+ ;
+ j += 2;
+ new_argv = calloc(j, sizeof(char*));
+ if (new_argv == NULL) {
+ fprintf(stderr,"%s: malloc of new_argv failed\n", argv[0]);
+ return 1;
+ }
+ for (i = 0; i < j-2; i++)
+ new_argv[i] = argv[i];
+ new_argv[j-2] = clientname;
+ assert(new_argv[j-1] == NULL);
+ argv = new_argv;
+ argc++;
+ }
+
+ 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 = (char*)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* 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, toolname, platform);
+
+ 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;
+ }
+
+ /* Find out what the current working directory is, and stuff it into the
+ environment so that the child can find it. */
+ char wd_buf[4096];
+ memset(wd_buf, 0, sizeof(wd_buf));
+ if (getcwd(wd_buf, sizeof(wd_buf)-1) == NULL) {
+ fprintf(stderr,"%s: getcwd(..) failed\n", argv[0]);
+ return 1;
+ }
+ assert(wd_buf[ sizeof(wd_buf)-1 ] == 0);
+ char* set_cwd = calloc(1, 100+sizeof(wd_buf));
+ if (set_cwd == NULL) {
+ fprintf(stderr,"%s: calloc of set_cwd failed\n", argv[0]);
+ return 1;
+ }
+ sprintf(set_cwd, "VALGRIND_STARTUP_PWD_%d_XYZZY=%s", getpid(), wd_buf);
+ VG_(debugLog)(1, "launcher", "doing putenv(\"%s\")\n", set_cwd);
+ putenv_err = putenv(set_cwd);
+ if (putenv_err) {
+ fprintf(stderr,"%s: putenv(\"VALGRIND_STARTUP_PWD_...\") 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 ---*/
+/*--------------------------------------------------------------------*/