mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 18:13:01 +00:00
1713 lines
55 KiB
C
1713 lines
55 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Launching Valgrind on AIX5. launcher-aix5.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2006-2010 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 ---*/
|
|
/*--------------------------------------------------------------------*/
|