| |
| /*--------------------------------------------------------------------*/ |
| /*--- Launching Valgrind on AIX5. launcher-aix5.c ---*/ |
| /*--------------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2006-2007 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; |
| } |
| |
| /* 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 ---*/ |
| /*--------------------------------------------------------------------*/ |