diff options
Diffstat (limited to 'coregrind/.svn/text-base/launcher-aix5.c.svn-base')
-rw-r--r-- | coregrind/.svn/text-base/launcher-aix5.c.svn-base | 1712 |
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 ---*/ +/*--------------------------------------------------------------------*/ |