blob: 6a6f188c44d0985a04cc96b456a285fbf35b1f14 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Launching Valgrind on AIX5. launcher-aix5.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2006-2008 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, platform, toolname);
if (!file_exists(toolfile)) {
fprintf(stderr,"%s: can't stat %s\n", argv[0], toolfile);
return 1;
}
/* Force the client to use a 1:1 threading model - this works
because the client inherits our environment. */
VG_(debugLog)(1, "launcher", "doing putenv(\"AIXTHREAD_SCOPE=S\")\n");
Int putenv_err = putenv("AIXTHREAD_SCOPE=S");
if (putenv_err) {
fprintf(stderr,"%s: putenv(\"AIXTHREAD_SCOPE=S\") failed\n", argv[0]);
return 1;
}
VG_(debugLog)(1, "launcher", "doing putenv(\"MP_SHARED_MEMORY=no\")\n");
putenv_err = putenv("MP_SHARED_MEMORY=no");
if (putenv_err) {
fprintf(stderr,"%s: putenv(\"MP_SHARED_MEMORY=no\") failed\n", argv[0]);
return 1;
}
/* 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 ---*/
/*--------------------------------------------------------------------*/