]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Move code that creates the initial Linux memory image (stack, env,
authorJulian Seward <jseward@acm.org>
Sun, 15 Oct 2006 01:26:40 +0000 (01:26 +0000)
committerJulian Seward <jseward@acm.org>
Sun, 15 Oct 2006 01:26:40 +0000 (01:26 +0000)
etc) from m_main into m_initimg.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6234

coregrind/Makefile.am
coregrind/m_initimg/initimg-linux.c [new file with mode: 0644]
coregrind/m_main.c
coregrind/pub_core_initimg.h [new file with mode: 0644]

index 35e610006eb1e1cd9ff1d8e00bbe2bbd2e821c54..812d7b098a1cb3e8a812d88a18c24c11fadd1fb7 100644 (file)
@@ -80,6 +80,7 @@ noinst_HEADERS = \
        pub_core_errormgr.h     \
        pub_core_execontext.h   \
        pub_core_hashtable.h    \
+       pub_core_initimg.h      \
        pub_core_libcbase.h     \
        pub_core_libcassert.h   \
        pub_core_libcfile.h     \
@@ -187,7 +188,8 @@ COREGRIND_LINUX_SOURCE = \
        m_coredump/coredump-elf.c \
        m_syswrap/syswrap-linux.c \
        m_syswrap/syswrap-linux-variants.c \
-       m_aspacemgr/aspacemgr-linux.c
+       m_aspacemgr/aspacemgr-linux.c \
+       m_initimg/initimg-linux.c
 
 libcoregrind_x86_linux_a_SOURCES = \
        $(COREGRIND_SOURCES_COMMON) \
diff --git a/coregrind/m_initimg/initimg-linux.c b/coregrind/m_initimg/initimg-linux.c
new file mode 100644 (file)
index 0000000..ddef6c1
--- /dev/null
@@ -0,0 +1,998 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Startup: create initial process image on Linux               ---*/
+/*---                                              initimg-linux.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2000-2006 Julian Seward
+      jseward@acm.org
+
+   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.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_debuglog.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcfile.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_clientstate.h"
+#include "pub_core_aspacemgr.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_machine.h"
+#include "pub_core_ume.h"
+#include "pub_core_options.h"
+#include "pub_core_tooliface.h"       /* VG_TRACK */
+#include "pub_core_threadstate.h"     /* ThreadArchState */
+#include "pub_core_initimg.h"         /* self */
+
+/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+/* This is for ELF types etc, and also the AT_ constants. */
+#include <elf.h>
+/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
+
+
+/*====================================================================*/
+/*=== Find executable                                              ===*/
+/*====================================================================*/
+
+/* Scan a colon-separated list, and call a function on each element.
+   The string must be mutable, because we insert a temporary '\0', but
+   the string will end up unmodified.  (*func) should return True if it
+   doesn't need to see any more.
+
+   This routine will return True if (*func) returns True and False if
+   it reaches the end of the list without that happening.
+*/
+static Bool scan_colsep(char *colsep, Bool (*func)(const char *))
+{
+   char *cp, *entry;
+   int end;
+
+   if (colsep == NULL ||
+       *colsep == '\0')
+      return False;
+
+   entry = cp = colsep;
+
+   do {
+      end = (*cp == '\0');
+
+      if (*cp == ':' || *cp == '\0') {
+        char save = *cp;
+
+        *cp = '\0';
+        if ((*func)(entry)) {
+            *cp = save;
+           return True;
+         }
+        *cp = save;
+        entry = cp+1;
+      }
+      cp++;
+   } while(!end);
+
+   return False;
+}
+
+/* Need a static copy because can't use dynamic mem allocation yet */
+static HChar executable_name_in [VKI_PATH_MAX];
+static HChar executable_name_out[VKI_PATH_MAX];
+
+static Bool match_executable(const char *entry) 
+{
+   HChar buf[VG_(strlen)(entry) + VG_(strlen)(executable_name_in) + 3];
+
+   /* empty PATH element means '.' */
+   if (*entry == '\0')
+      entry = ".";
+
+   VG_(snprintf)(buf, sizeof(buf), "%s/%s", entry, executable_name_in);
+
+   // Don't match directories
+   if (VG_(is_dir)(buf))
+      return False;
+
+   // If we match an executable, we choose that immediately.  If we find a
+   // matching non-executable we remember it but keep looking for an
+   // matching executable later in the path.
+   if (VG_(access)(buf, True/*r*/, False/*w*/, True/*x*/) == 0) {
+      VG_(strncpy)( executable_name_out, buf, VKI_PATH_MAX-1 );
+      executable_name_out[VKI_PATH_MAX-1] = 0;
+      return True;      // Stop looking
+   } else if (VG_(access)(buf, True/*r*/, False/*w*/, False/*x*/) == 0 
+           && VG_STREQ(executable_name_out, "")) 
+   {
+      VG_(strncpy)( executable_name_out, buf, VKI_PATH_MAX-1 );
+      executable_name_out[VKI_PATH_MAX-1] = 0;
+      return False;     // Keep looking
+   } else { 
+      return False;     // Keep looking
+   }
+}
+
+// Returns NULL if it wasn't found.
+static HChar* find_executable ( HChar* exec )
+{
+   vg_assert(NULL != exec);
+   if (VG_(strchr)(exec, '/')) {
+      // Has a '/' - use the name as is
+      VG_(strncpy)( executable_name_out, exec, VKI_PATH_MAX-1 );
+   } else {
+      // No '/' - we need to search the path
+      HChar* path;
+      VG_(strncpy)( executable_name_in,  exec, VKI_PATH_MAX-1 );
+      VG_(memset) ( executable_name_out, 0,    VKI_PATH_MAX );
+      path = VG_(getenv)("PATH");
+      scan_colsep(path, match_executable);
+   }
+   return VG_STREQ(executable_name_out, "") ? NULL : executable_name_out;
+}
+
+
+/*====================================================================*/
+/*=== Loading the client                                           ===*/
+/*====================================================================*/
+
+/* Load the client whose name is VG_(argv_the_exename). */
+
+static void load_client ( /*OUT*/ExeInfo* info, 
+                          /*OUT*/Addr*    client_ip,
+                         /*OUT*/Addr*    client_toc)
+{
+   HChar* exe_name;
+   Int    ret;
+   SysRes res;
+
+   vg_assert( VG_(args_the_exename) != NULL);
+   exe_name = find_executable( VG_(args_the_exename) );
+
+   if (!exe_name) {
+      VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename));
+      VG_(exit)(127);      // 127 is Posix NOTFOUND
+   }
+
+   VG_(memset)(info, 0, sizeof(*info));
+   info->exe_base = VG_(client_base);
+   info->exe_end  = VG_(client_end);
+
+   ret = VG_(do_exec)(exe_name, info);
+
+   // The client was successfully loaded!  Continue.
+
+   /* Get hold of a file descriptor which refers to the client
+      executable.  This is needed for attaching to GDB. */
+   res = VG_(open)(exe_name, VKI_O_RDONLY, VKI_S_IRUSR);
+   if (!res.isError)
+      VG_(cl_exec_fd) = res.val;
+
+   /* Copy necessary bits of 'info' that were filled in */
+   *client_ip  = info->init_ip;
+   *client_toc = info->init_toc;
+   VG_(brk_base) = VG_(brk_limit) = VG_PGROUNDUP(info->brkbase);
+}
+
+
+/*====================================================================*/
+/*=== Setting up the client's environment                          ===*/
+/*====================================================================*/
+
+/* Prepare the client's environment.  This is basically a copy of our
+   environment, except:
+
+     LD_PRELOAD=$VALGRIND_LIB/PLATFORM/vgpreload_core.so:
+                ($VALGRIND_LIB/PLATFORM/vgpreload_TOOL.so:)?
+                $LD_PRELOAD
+
+   If this is missing, then it is added.
+
+   Also, remove any binding for VALGRIND_LAUNCHER=.  The client should
+   not be able to see this.
+
+   If this needs to handle any more variables it should be hacked
+   into something table driven.  The copy is VG_(malloc)'d space.
+*/
+static HChar** setup_client_env ( HChar** origenv, const HChar* toolname)
+{
+   HChar* preload_core    = "vgpreload_core";
+   HChar* ld_preload      = "LD_PRELOAD=";
+   HChar* v_launcher      = VALGRIND_LAUNCHER "=";
+   Int    ld_preload_len  = VG_(strlen)( ld_preload );
+   Int    v_launcher_len  = VG_(strlen)( v_launcher );
+   Bool   ld_preload_done = False;
+   Int    vglib_len       = VG_(strlen)(VG_(libdir));
+
+   HChar** cpp;
+   HChar** ret;
+   HChar*  preload_tool_path;
+   Int     envc, i;
+
+   /* Alloc space for the vgpreload_core.so path and vgpreload_<tool>.so
+      paths.  We might not need the space for vgpreload_<tool>.so, but it
+      doesn't hurt to over-allocate briefly.  The 16s are just cautious
+      slop. */
+   Int preload_core_path_len = vglib_len + sizeof(preload_core) 
+                                         + sizeof(VG_PLATFORM) + 16;
+   Int preload_tool_path_len = vglib_len + VG_(strlen)(toolname) 
+                                         + sizeof(VG_PLATFORM) + 16;
+   Int preload_string_len    = preload_core_path_len + preload_tool_path_len;
+   HChar* preload_string     = VG_(malloc)(preload_string_len);
+   vg_assert(preload_string);
+
+   /* Determine if there's a vgpreload_<tool>.so file, and setup
+      preload_string. */
+   preload_tool_path = VG_(malloc)(preload_tool_path_len);
+   vg_assert(preload_tool_path);
+   VG_(snprintf)(preload_tool_path, preload_tool_path_len,
+                 "%s/%s/vgpreload_%s.so", VG_(libdir), VG_PLATFORM, toolname);
+   if (VG_(access)(preload_tool_path, True/*r*/, False/*w*/, False/*x*/) == 0) {
+      VG_(snprintf)(preload_string, preload_string_len, "%s/%s/%s.so:%s", 
+                    VG_(libdir), VG_PLATFORM, preload_core, preload_tool_path);
+   } else {
+      VG_(snprintf)(preload_string, preload_string_len, "%s/%s/%s.so", 
+                    VG_(libdir), VG_PLATFORM, preload_core);
+   }
+   VG_(free)(preload_tool_path);
+
+   VG_(debugLog)(2, "initimg", "preload_string:\n");
+   VG_(debugLog)(2, "initimg", "  \"%s\"\n", preload_string);
+
+   /* Count the original size of the env */
+   envc = 0;
+   for (cpp = origenv; cpp && *cpp; cpp++)
+      envc++;
+
+   /* Allocate a new space */
+   ret = VG_(malloc) (sizeof(HChar *) * (envc+1+1)); /* 1 new entry + NULL */
+   vg_assert(ret);
+
+   /* copy it over */
+   for (cpp = ret; *origenv; )
+      *cpp++ = *origenv++;
+   *cpp = NULL;
+   
+   vg_assert(envc == (cpp - ret));
+
+   /* Walk over the new environment, mashing as we go */
+   for (cpp = ret; cpp && *cpp; cpp++) {
+      if (VG_(memcmp)(*cpp, ld_preload, ld_preload_len) == 0) {
+         Int len = VG_(strlen)(*cpp) + preload_string_len;
+         HChar *cp = VG_(malloc)(len);
+         vg_assert(cp);
+
+         VG_(snprintf)(cp, len, "%s%s:%s",
+                       ld_preload, preload_string, (*cpp)+ld_preload_len);
+
+         *cpp = cp;
+
+         ld_preload_done = True;
+      }
+   }
+
+   /* Add the missing bits */
+   if (!ld_preload_done) {
+      Int len = ld_preload_len + preload_string_len;
+      HChar *cp = VG_(malloc) (len);
+      vg_assert(cp);
+
+      VG_(snprintf)(cp, len, "%s%s", ld_preload, preload_string);
+
+      ret[envc++] = cp;
+   }
+
+   /* ret[0 .. envc-1] is live now. */
+   /* Find and remove a binding for VALGRIND_LAUNCHER. */
+   for (i = 0; i < envc; i++)
+      if (0 == VG_(memcmp(ret[i], v_launcher, v_launcher_len)))
+         break;
+
+   if (i < envc) {
+      for (; i < envc-1; i++)
+         ret[i] = ret[i+1];
+      envc--;
+   }
+
+   VG_(free)(preload_string);
+   ret[envc] = NULL;
+
+   return ret;
+}
+
+
+/*====================================================================*/
+/*=== Setting up the client's stack                                ===*/
+/*====================================================================*/
+
+#ifndef AT_DCACHEBSIZE
+#define AT_DCACHEBSIZE         19
+#endif /* AT_DCACHEBSIZE */
+
+#ifndef AT_ICACHEBSIZE
+#define AT_ICACHEBSIZE         20
+#endif /* AT_ICACHEBSIZE */
+
+#ifndef AT_UCACHEBSIZE
+#define AT_UCACHEBSIZE         21
+#endif /* AT_UCACHEBSIZE */
+
+#ifndef AT_SYSINFO
+#define AT_SYSINFO             32
+#endif /* AT_SYSINFO */
+
+#ifndef AT_SYSINFO_EHDR
+#define AT_SYSINFO_EHDR                33
+#endif /* AT_SYSINFO_EHDR */
+
+#ifndef AT_SECURE
+#define AT_SECURE 23   /* secure mode boolean */
+#endif /* AT_SECURE */
+
+/* Add a string onto the string table, and return its address */
+static char *copy_str(char **tab, const char *str)
+{
+   char *cp = *tab;
+   char *orig = cp;
+
+   while(*str)
+      *cp++ = *str++;
+   *cp++ = '\0';
+
+   if (0)
+      VG_(printf)("copied %p \"%s\" len %lld\n", orig, orig, (Long)(cp-orig));
+
+   *tab = cp;
+
+   return orig;
+}
+
+
+/* ----------------------------------------------------------------
+   This sets up the client's initial stack, containing the args,
+   environment and aux vector.
+
+   The format of the stack is:
+
+   higher address +-----------------+ <- clstack_end
+                  |                 |
+                 : string table    :
+                 |                 |
+                 +-----------------+
+                 | AT_NULL         |
+                 -                 -
+                 | auxv            |
+                 +-----------------+
+                 | NULL            |
+                 -                 -
+                 | envp            |
+                 +-----------------+
+                 | NULL            |
+                 -                 -
+                 | argv            |
+                 +-----------------+
+                 | argc            |
+   lower address  +-----------------+ <- sp
+                  | undefined       |
+                 :                 :
+
+   Allocate and create the initial client stack.  It is allocated down
+   from clstack_end, which was previously determined by the address
+   space manager.  The returned value is the SP value for the client.
+
+   The client's auxv is created by copying and modifying our own one.
+   As a side effect of scanning our own auxv, some important bits of
+   info are collected:
+
+      VG_(cache_line_size_ppc32) // ppc32 only -- cache line size
+      VG_(have_altivec_ppc32)    // ppc32 only -- is Altivec supported?
+
+   ---------------------------------------------------------------- */
+
+static 
+Addr setup_client_stack( void*  init_sp,
+                         char** orig_envp, 
+                         const ExeInfo* info,
+                         UInt** client_auxv,
+                         Addr   clstack_end,
+                         SizeT  clstack_max_size )
+{
+   SysRes res;
+   char **cpp;
+   char *strtab;               /* string table */
+   char *stringbase;
+   Addr *ptr;
+   struct ume_auxv *auxv;
+   const struct ume_auxv *orig_auxv;
+   const struct ume_auxv *cauxv;
+   unsigned stringsize;                /* total size of strings in bytes */
+   unsigned auxsize;           /* total size of auxv in bytes */
+   Int argc;                   /* total argc */
+   Int envc;                   /* total number of env vars */
+   unsigned stacksize;         /* total client stack size */
+   Addr client_SP;             /* client stack base (initial SP) */
+   Addr clstack_start;
+   Int i;
+   Bool have_exename;
+
+   vg_assert(VG_IS_PAGE_ALIGNED(clstack_end+1));
+
+   /* use our own auxv as a prototype */
+   orig_auxv = VG_(find_auxv)(init_sp);
+
+   /* ==================== compute sizes ==================== */
+
+   /* first of all, work out how big the client stack will be */
+   stringsize   = 0;
+   have_exename = VG_(args_the_exename) != NULL;
+
+   /* paste on the extra args if the loader needs them (ie, the #! 
+      interpreter and its argument) */
+   argc = 0;
+   if (info->interp_name != NULL) {
+      argc++;
+      stringsize += VG_(strlen)(info->interp_name) + 1;
+   }
+   if (info->interp_args != NULL) {
+      argc++;
+      stringsize += VG_(strlen)(info->interp_args) + 1;
+   }
+
+   /* now scan the args we're given... */
+   if (have_exename)
+      stringsize += VG_(strlen)( VG_(args_the_exename) ) + 1;
+
+   for (i = 0; i < VG_(args_for_client).used; i++) {
+      argc++;
+      stringsize += VG_(strlen)( VG_(args_for_client).strs[i] ) + 1;
+   }
+
+   /* ...and the environment */
+   envc = 0;
+   for (cpp = orig_envp; cpp && *cpp; cpp++) {
+      envc++;
+      stringsize += VG_(strlen)(*cpp) + 1;
+   }
+
+   /* now, how big is the auxv? */
+   auxsize = sizeof(*auxv);    /* there's always at least one entry: AT_NULL */
+   for (cauxv = orig_auxv; cauxv->a_type != AT_NULL; cauxv++) {
+      if (cauxv->a_type == AT_PLATFORM)
+        stringsize += VG_(strlen)(cauxv->u.a_ptr) + 1;
+      auxsize += sizeof(*cauxv);
+   }
+
+#  if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
+   auxsize += 2 * sizeof(*cauxv);
+#  endif
+
+   /* OK, now we know how big the client stack is */
+   stacksize =
+      sizeof(Word) +                          /* argc */
+      (have_exename ? sizeof(char **) : 0) +  /* argc[0] == exename */
+      sizeof(char **)*argc +                  /* argv */
+      sizeof(char **) +                              /* terminal NULL */
+      sizeof(char **)*envc +                  /* envp */
+      sizeof(char **) +                              /* terminal NULL */
+      auxsize +                               /* auxv */
+      VG_ROUNDUP(stringsize, sizeof(Word));   /* strings (aligned) */
+
+   if (0) VG_(printf)("stacksize = %d\n", stacksize);
+
+   /* client_SP is the client's stack pointer */
+   client_SP = clstack_end - stacksize;
+   client_SP = VG_ROUNDDN(client_SP, 16); /* make stack 16 byte aligned */
+
+   /* base of the string table (aligned) */
+   stringbase = strtab = (char *)clstack_end 
+                         - VG_ROUNDUP(stringsize, sizeof(int));
+
+   clstack_start = VG_PGROUNDDN(client_SP);
+
+   /* The max stack size */
+   clstack_max_size = VG_PGROUNDUP(clstack_max_size);
+
+   /* Record stack extent -- needed for stack-change code. */
+   VG_(clstk_base) = clstack_start;
+   VG_(clstk_end)  = clstack_end;
+
+   if (0)
+      VG_(printf)("stringsize=%d auxsize=%d stacksize=%d maxsize=0x%x\n"
+                  "clstack_start %p\n"
+                  "clstack_end   %p\n",
+                 stringsize, auxsize, stacksize, (Int)clstack_max_size,
+                  (void*)clstack_start, (void*)clstack_end);
+
+   /* ==================== allocate space ==================== */
+
+   { SizeT anon_size   = clstack_end - clstack_start + 1;
+     SizeT resvn_size  = clstack_max_size - anon_size;
+     Addr  anon_start  = clstack_start;
+     Addr  resvn_start = anon_start - resvn_size;
+     SizeT inner_HACK  = 0;
+     Bool  ok;
+
+     /* So far we've only accounted for space requirements down to the
+        stack pointer.  If this target's ABI requires a redzone below
+        the stack pointer, we need to allocate an extra page, to
+        handle the worst case in which the stack pointer is almost at
+        the bottom of a page, and so there is insufficient room left
+        over to put the redzone in.  In this case the simple thing to
+        do is allocate an extra page, by shrinking the reservation by
+        one page and growing the anonymous area by a corresponding
+        page. */
+     vg_assert(VG_STACK_REDZONE_SZB >= 0);
+     vg_assert(VG_STACK_REDZONE_SZB < VKI_PAGE_SIZE);
+     if (VG_STACK_REDZONE_SZB > 0) {
+        vg_assert(resvn_size > VKI_PAGE_SIZE);
+        resvn_size -= VKI_PAGE_SIZE;
+        anon_start -= VKI_PAGE_SIZE;
+        anon_size += VKI_PAGE_SIZE;
+     }
+
+     vg_assert(VG_IS_PAGE_ALIGNED(anon_size));
+     vg_assert(VG_IS_PAGE_ALIGNED(resvn_size));
+     vg_assert(VG_IS_PAGE_ALIGNED(anon_start));
+     vg_assert(VG_IS_PAGE_ALIGNED(resvn_start));
+     vg_assert(resvn_start == clstack_end + 1 - clstack_max_size);
+
+#    ifdef ENABLE_INNER
+     inner_HACK = 1024*1024; // create 1M non-fault-extending stack
+#    endif
+
+     if (0)
+        VG_(printf)("%p 0x%x  %p 0x%x\n", 
+                    resvn_start, resvn_size, anon_start, anon_size);
+
+     /* Create a shrinkable reservation followed by an anonymous
+        segment.  Together these constitute a growdown stack. */
+     ok = VG_(am_create_reservation)(
+             resvn_start,
+             resvn_size -inner_HACK,
+             SmUpper, 
+             anon_size +inner_HACK
+          );
+     vg_assert(ok);
+     /* allocate a stack - mmap enough space for the stack */
+     res = VG_(am_mmap_anon_fixed_client)(
+              anon_start -inner_HACK,
+              anon_size +inner_HACK,
+             VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC
+          );
+     vg_assert(!res.isError); 
+   }
+
+   /* ==================== create client stack ==================== */
+
+   ptr = (Addr*)client_SP;
+
+   /* --- client argc --- */
+   *ptr++ = argc + (have_exename ? 1 : 0);
+
+   /* --- client argv --- */
+   if (info->interp_name) {
+      *ptr++ = (Addr)copy_str(&strtab, info->interp_name);
+      VG_(free)(info->interp_name);
+   }
+   if (info->interp_args) {
+      *ptr++ = (Addr)copy_str(&strtab, info->interp_args);
+      VG_(free)(info->interp_args);
+   }
+
+   if (have_exename)
+      *ptr++ = (Addr)copy_str(&strtab, VG_(args_the_exename));
+
+   for (i = 0; i < VG_(args_for_client).used; i++) {
+      *ptr++ = (Addr)copy_str(&strtab, VG_(args_for_client).strs[i]);
+   }
+   *ptr++ = 0;
+
+   /* --- envp --- */
+   VG_(client_envp) = (Char **)ptr;
+   for (cpp = orig_envp; cpp && *cpp; ptr++, cpp++)
+      *ptr = (Addr)copy_str(&strtab, *cpp);
+   *ptr++ = 0;
+
+   /* --- auxv --- */
+   auxv = (struct ume_auxv *)ptr;
+   *client_auxv = (UInt *)auxv;
+
+#  if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
+   auxv[0].a_type  = AT_IGNOREPPC;
+   auxv[0].u.a_val = AT_IGNOREPPC;
+   auxv[1].a_type  = AT_IGNOREPPC;
+   auxv[1].u.a_val = AT_IGNOREPPC;
+   auxv += 2;
+#  endif
+
+   for (; orig_auxv->a_type != AT_NULL; auxv++, orig_auxv++) {
+
+      /* copy the entry... */
+      *auxv = *orig_auxv;
+
+      /* ...and fix up / examine the copy */
+      switch(auxv->a_type) {
+
+         case AT_IGNORE:
+         case AT_PHENT:
+         case AT_PAGESZ:
+         case AT_FLAGS:
+         case AT_NOTELF:
+         case AT_UID:
+         case AT_EUID:
+         case AT_GID:
+         case AT_EGID:
+         case AT_CLKTCK:
+         case AT_FPUCW:
+            /* All these are pointerless, so we don't need to do
+               anything about them. */
+            break;
+
+         case AT_PHDR:
+            if (info->phdr == 0)
+               auxv->a_type = AT_IGNORE;
+            else
+               auxv->u.a_val = info->phdr;
+            break;
+
+         case AT_PHNUM:
+            if (info->phdr == 0)
+               auxv->a_type = AT_IGNORE;
+            else
+               auxv->u.a_val = info->phnum;
+            break;
+
+         case AT_BASE:
+            auxv->u.a_val = info->interp_base;
+            break;
+
+         case AT_PLATFORM:
+            /* points to a platform description string */
+            auxv->u.a_ptr = copy_str(&strtab, orig_auxv->u.a_ptr);
+            break;
+
+         case AT_ENTRY:
+            auxv->u.a_val = info->entry;
+            break;
+
+         case AT_HWCAP:
+            break;
+
+         case AT_DCACHEBSIZE:
+         case AT_ICACHEBSIZE:
+         case AT_UCACHEBSIZE:
+#           if defined(VGP_ppc32_linux)
+            /* acquire cache info */
+            if (auxv->u.a_val > 0) {
+               VG_(machine_ppc32_set_clszB)( auxv->u.a_val );
+               VG_(debugLog)(2, "initimg", 
+                                "PPC32 cache line size %u (type %u)\n", 
+                                (UInt)auxv->u.a_val, (UInt)auxv->a_type );
+            }
+#           elif defined(VGP_ppc64_linux)
+            /* acquire cache info */
+            if (auxv->u.a_val > 0) {
+               VG_(machine_ppc64_set_clszB)( auxv->u.a_val );
+               VG_(debugLog)(2, "initimg", 
+                                "PPC64 cache line size %u (type %u)\n", 
+                                (UInt)auxv->u.a_val, (UInt)auxv->a_type );
+            }
+#           endif
+            break;
+
+#        if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
+         case AT_IGNOREPPC:
+            break;
+#        endif
+
+         case AT_SECURE:
+            /* If this is 1, then it means that this program is
+               running suid, and therefore the dynamic linker should
+               be careful about LD_PRELOAD, etc.  However, since
+               stage1 (the thing the kernel actually execve's) should
+               never be SUID, and we need LD_PRELOAD to work for the
+               client, we set AT_SECURE to 0. */
+            auxv->u.a_val = 0;
+            break;
+
+         case AT_SYSINFO:
+#        if !defined(VGP_ppc32_linux) && !defined(VGP_ppc64_linux)
+         case AT_SYSINFO_EHDR:
+#        endif
+            /* Trash this, because we don't reproduce it */
+            auxv->a_type = AT_IGNORE;
+            break;
+
+         default:
+            /* stomp out anything we don't know about */
+            VG_(debugLog)(2, "initimg",
+                             "stomping auxv entry %lld\n", 
+                             (ULong)auxv->a_type);
+            auxv->a_type = AT_IGNORE;
+            break;
+      }
+   }
+   *auxv = *orig_auxv;
+   vg_assert(auxv->a_type == AT_NULL);
+
+   vg_assert((strtab-stringbase) == stringsize);
+
+   /* client_SP is pointing at client's argc/argv */
+
+   if (0) VG_(printf)("startup SP = %p\n", client_SP);
+   return client_SP;
+}
+
+
+/* Allocate the client data segment.  It is an expandable anonymous
+   mapping abutting a shrinkable reservation of size max_dseg_size.
+   The data segment starts at VG_(brk_base), which is page-aligned,
+   and runs up to VG_(brk_limit), which isn't. */
+
+static void setup_client_dataseg ( SizeT max_size )
+{
+   Bool   ok;
+   SysRes sres;
+   Addr   anon_start  = VG_(brk_base);
+   SizeT  anon_size   = VKI_PAGE_SIZE;
+   Addr   resvn_start = anon_start + anon_size;
+   SizeT  resvn_size  = max_size - anon_size;
+
+   vg_assert(VG_IS_PAGE_ALIGNED(anon_size));
+   vg_assert(VG_IS_PAGE_ALIGNED(resvn_size));
+   vg_assert(VG_IS_PAGE_ALIGNED(anon_start));
+   vg_assert(VG_IS_PAGE_ALIGNED(resvn_start));
+
+   /* Because there's been no brk activity yet: */
+   vg_assert(VG_(brk_base) == VG_(brk_limit));
+
+   /* Try to create the data seg and associated reservation where
+      VG_(brk_base) says. */
+   ok = VG_(am_create_reservation)( 
+           resvn_start, 
+           resvn_size, 
+           SmLower, 
+           anon_size
+        );
+
+   if (!ok) {
+      /* Hmm, that didn't work.  Well, let aspacem suggest an address
+         it likes better, and try again with that. */
+      anon_start = VG_(am_get_advisory_client_simple)
+                      ( 0/*floating*/, anon_size+resvn_size, &ok );
+      if (ok) {
+         resvn_start = anon_start + anon_size;
+         ok = VG_(am_create_reservation)( 
+                 resvn_start, 
+                 resvn_size, 
+                 SmLower, 
+                 anon_size
+              );
+         if (ok)
+            VG_(brk_base) = VG_(brk_limit) = anon_start;
+      }
+      /* that too might have failed, but if it has, we're hosed: there
+         is no Plan C. */
+   }
+   vg_assert(ok);
+
+   /* We make the data segment (heap) executable because LinuxThreads on
+      ppc32 creates trampolines in this area.  Also, on x86/Linux the data
+      segment is RWX natively, at least according to /proc/self/maps.
+      Also, having a non-executable data seg would kill any program which
+      tried to create code in the data seg and then run it. */
+   sres = VG_(am_mmap_anon_fixed_client)( 
+             anon_start, 
+             anon_size, 
+             VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC
+          );
+   vg_assert(!sres.isError);
+   vg_assert(sres.val == anon_start);
+}
+
+
+/*====================================================================*/
+/*=== TOP-LEVEL: VG_(setup_client_initial_image)                   ===*/
+/*====================================================================*/
+
+/* Create the client's initial memory image. */
+
+ClientInitImgInfo
+   VG_(setup_client_initial_image)(
+      /*IN*/ HChar** argv,
+      /*IN*/ HChar** envp,
+      /*IN*/ HChar*  toolname,
+      /*IN*/ Addr    clstack_top,
+      /*IN*/ SizeT   clstack_max_size
+   )
+{
+   ClientInitImgInfo ciii = { 0, 0, 0, NULL };
+   ExeInfo           info;
+   HChar**           env = NULL;
+
+   //--------------------------------------------------------------
+   // Load client executable, finding in $PATH if necessary
+   //   p: get_helprequest_and_toolname()  [for 'exec', 'need_help']
+   //   p: layout_remaining_space          [so there's space]
+   //--------------------------------------------------------------
+   VG_(debugLog)(1, "initimg", "Loading client\n");
+
+   if (VG_(args_the_exename) == NULL)
+      VG_(err_missing_prog)();
+
+   load_client(&info, &ciii.initial_client_IP, &ciii.initial_client_TOC);
+
+   //--------------------------------------------------------------
+   // Set up client's environment
+   //   p: set-libdir                   [for VG_(libdir)]
+   //   p: get_helprequest_and_toolname [for toolname]
+   //--------------------------------------------------------------
+   VG_(debugLog)(1, "initimg", "Setup client env\n");
+   env = setup_client_env(envp, toolname);
+
+   //--------------------------------------------------------------
+   // Setup client stack, eip, and VG_(client_arg[cv])
+   //   p: load_client()     [for 'info']
+   //   p: fix_environment() [for 'env']
+   //--------------------------------------------------------------
+   {
+      void* init_sp = argv - 1;
+      SizeT m1  = 1024 * 1024;
+      SizeT m16 = 16 * m1;
+      VG_(debugLog)(1, "initimg", "Setup client stack\n");
+      clstack_max_size = (SizeT)VG_(client_rlimit_stack).rlim_cur;
+      if (clstack_max_size < m1)  clstack_max_size = m1;
+      if (clstack_max_size > m16) clstack_max_size = m16;
+      clstack_max_size = VG_PGROUNDUP(clstack_max_size);
+
+      ciii.initial_client_SP
+         = setup_client_stack( init_sp, env, 
+                               &info, &ciii.client_auxv, 
+                               clstack_top, clstack_max_size );
+
+      VG_(free)(env);
+
+      VG_(debugLog)(2, "initimg",
+                       "Client info: "
+                       "initial_IP=%p initial_SP=%p initial_TOC=%p brk_base=%p\n",
+                       (void*)(ciii.initial_client_IP), 
+                       (void*)(ciii.initial_client_SP),
+                       (void*)(ciii.initial_client_TOC),
+                       (void*)VG_(brk_base) );
+   }
+
+   //--------------------------------------------------------------
+   // Setup client data (brk) segment.  Initially a 1-page segment
+   // which abuts a shrinkable reservation. 
+   //     p: load_client()     [for 'info' and hence VG_(brk_base)]
+   //--------------------------------------------------------------
+   { 
+      SizeT m1 = 1024 * 1024;
+      SizeT m8 = 8 * m1;
+      SizeT dseg_max_size = (SizeT)VG_(client_rlimit_data).rlim_cur;
+      VG_(debugLog)(1, "initimg", "Setup client data (brk) segment\n");
+      if (dseg_max_size < m1) dseg_max_size = m1;
+      if (dseg_max_size > m8) dseg_max_size = m8;
+      dseg_max_size = VG_PGROUNDUP(dseg_max_size);
+
+      setup_client_dataseg( dseg_max_size );
+   }
+
+   return ciii;
+}
+
+
+/*====================================================================*/
+/*=== TOP-LEVEL: VG_(finalise_thread1state)                        ===*/
+/*====================================================================*/
+
+/* Make final adjustments to the initial image.  Also, initialise the
+   VEX guest state for thread 1 (the root thread) and copy in
+   essential starting values.  Is handed the ClientInitImgInfo created
+   by VG_(setup_client_initial_image).  Upon return, the client's
+   memory and register state should be ready to start the JIT. */
+extern 
+void VG_(finalise_thread1state)( /*MOD*/ThreadArchState* arch,
+                                 ClientInitImgInfo ciii )
+{
+   /* On Linux we get client_{ip/sp/toc}, and start the client with
+      all other registers zeroed. */
+
+#  if defined(VGP_x86_linux)
+   vg_assert(0 == sizeof(VexGuestX86State) % 8);
+
+   /* Zero out the initial state, and set up the simulated FPU in a
+      sane way. */
+   LibVEX_GuestX86_initialise(&arch->vex);
+
+   /* Zero out the shadow area. */
+   VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestX86State));
+
+   /* Put essential stuff into the new state. */
+   arch->vex.guest_ESP = ciii.initial_client_SP;
+   arch->vex.guest_EIP = ciii.initial_client_IP;
+
+   /* initialise %cs, %ds and %ss to point at the operating systems
+      default code, data and stack segments */
+   asm volatile("movw %%cs, %0" : : "m" (arch->vex.guest_CS));
+   asm volatile("movw %%ds, %0" : : "m" (arch->vex.guest_DS));
+   asm volatile("movw %%ss, %0" : : "m" (arch->vex.guest_SS));
+
+#  elif defined(VGP_amd64_linux)
+   vg_assert(0 == sizeof(VexGuestAMD64State) % 8);
+
+   /* Zero out the initial state, and set up the simulated FPU in a
+      sane way. */
+   LibVEX_GuestAMD64_initialise(&arch->vex);
+
+   /* Zero out the shadow area. */
+   VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestAMD64State));
+
+   /* Put essential stuff into the new state. */
+   arch->vex.guest_RSP = ciii.initial_client_SP;
+   arch->vex.guest_RIP = ciii.initial_client_IP;
+
+#  elif defined(VGP_ppc32_linux)
+   vg_assert(0 == sizeof(VexGuestPPC32State) % 8);
+
+   /* Zero out the initial state, and set up the simulated FPU in a
+      sane way. */
+   LibVEX_GuestPPC32_initialise(&arch->vex);
+
+   /* Zero out the shadow area. */
+   VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestPPC32State));
+
+   /* Put essential stuff into the new state. */
+   arch->vex.guest_GPR1 = ciii.initial_client_SP;
+   arch->vex.guest_CIA  = ciii.initial_client_IP;
+
+#  elif defined(VGP_ppc64_linux)
+   vg_assert(0 == sizeof(VexGuestPPC64State) % 16);
+
+   /* Zero out the initial state, and set up the simulated FPU in a
+      sane way. */
+   LibVEX_GuestPPC64_initialise(&arch->vex);
+
+   /* Zero out the shadow area. */
+   VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestPPC64State));
+
+   /* Put essential stuff into the new state. */
+   arch->vex.guest_GPR1 = ciii.initial_client_SP;
+   arch->vex.guest_GPR2 = ciii.initial_client_TOC;
+   arch->vex.guest_CIA  = ciii.initial_client_IP;
+
+#  else
+#    error Unknown platform
+#  endif
+
+   /* Tell the tool that we just wrote to the registers. */
+   VG_TRACK( post_reg_write, Vg_CoreStartup, /*tid*/1, /*offset*/0,
+             sizeof(VexGuestArchState));
+}
+
+
+/*--------------------------------------------------------------------*/
+/*---                                              initimg-linux.c ---*/
+/*--------------------------------------------------------------------*/
index 207c4edf0be5947d8831a6c15ec0c9351c43b836..ddef8db48fe21fdeb744acd7bc96473b7d633172 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "pub_core_basics.h"
 #include "pub_core_vki.h"
+#include "pub_core_vkiscnums.h"
 #include "pub_core_threadstate.h"
 #include "pub_core_clientstate.h"
 #include "pub_core_aspacemgr.h"
@@ -37,6 +38,7 @@
 #include "pub_core_debuglog.h"
 #include "pub_core_errormgr.h"
 #include "pub_core_execontext.h"
+#include "pub_core_initimg.h"
 #include "pub_core_libcbase.h"
 #include "pub_core_libcassert.h"
 #include "pub_core_libcfile.h"
 #include "pub_core_signals.h"
 #include "pub_core_stacks.h"        // For VG_(register_stack)
 #include "pub_core_syswrap.h"
-#include "pub_core_translate.h"     // For VG_(translate)
 #include "pub_core_tooliface.h"
+#include "pub_core_translate.h"     // For VG_(translate)
 #include "pub_core_trampoline.h"
 #include "pub_core_transtab.h"
-#include "pub_core_ume.h"
+
+/* Stuff for reading AIX5 /proc/<pid>/sysent files */
+#if defined(VGO_aix5)
+   /* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
+#  include <sys/procfs.h>  /* prsysent_t */
+   /* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
+#  define VG_AIX5_SYSENT_SIZE 100000
+   static UChar aix5_sysent_buf[VG_AIX5_SYSENT_SIZE];
+#endif
 
 
 /*====================================================================*/
@@ -84,797 +94,6 @@ static void print_all_stats ( void )
 }
 
 
-/*====================================================================*/
-/*=== Setting up the client's environment                          ===*/
-/*====================================================================*/
-
-/* Prepare the client's environment.  This is basically a copy of our
-   environment, except:
-
-     LD_PRELOAD=$VALGRIND_LIB/vgpreload_core.so:
-                ($VALGRIND_LIB/vgpreload_TOOL.so:)?
-                $LD_PRELOAD
-
-   If this is missing, then it is added.
-
-   Also, remove any binding for VALGRIND_LAUNCHER=.  The client should
-   not be able to see this.
-
-   If this needs to handle any more variables it should be hacked
-   into something table driven.  The copy is VG_(malloc)'d space.
-*/
-static HChar** setup_client_env ( HChar** origenv, const HChar* toolname)
-{
-   HChar* preload_core    = "vgpreload_core";
-   HChar* ld_preload      = "LD_PRELOAD=";
-   HChar* v_launcher      = VALGRIND_LAUNCHER "=";
-   Int    ld_preload_len  = VG_(strlen)( ld_preload );
-   Int    v_launcher_len  = VG_(strlen)( v_launcher );
-   Bool   ld_preload_done = False;
-   Int    vglib_len       = VG_(strlen)(VG_(libdir));
-
-   HChar** cpp;
-   HChar** ret;
-   HChar*  preload_tool_path;
-   Int     envc, i;
-
-   /* Alloc space for the vgpreload_core.so path and vgpreload_<tool>.so
-      paths.  We might not need the space for vgpreload_<tool>.so, but it
-      doesn't hurt to over-allocate briefly.  The 16s are just cautious
-      slop. */
-   Int preload_core_path_len = vglib_len + sizeof(preload_core) + sizeof(VG_PLATFORM) + 16;
-   Int preload_tool_path_len = vglib_len + VG_(strlen)(toolname) + sizeof(VG_PLATFORM) + 16;
-   Int preload_string_len    = preload_core_path_len + preload_tool_path_len;
-   HChar* preload_string     = VG_(malloc)(preload_string_len);
-   vg_assert(preload_string);
-
-   /* Determine if there's a vgpreload_<tool>.so file, and setup
-      preload_string. */
-   preload_tool_path = VG_(malloc)(preload_tool_path_len);
-   vg_assert(preload_tool_path);
-   VG_(snprintf)(preload_tool_path, preload_tool_path_len,
-                 "%s/%s/vgpreload_%s.so", VG_(libdir), VG_PLATFORM, toolname);
-   if (VG_(access)(preload_tool_path, True/*r*/, False/*w*/, False/*x*/) == 0) {
-      VG_(snprintf)(preload_string, preload_string_len, "%s/%s/%s.so:%s", 
-                    VG_(libdir), VG_PLATFORM, preload_core, preload_tool_path);
-   } else {
-      VG_(snprintf)(preload_string, preload_string_len, "%s/%s/%s.so", 
-                    VG_(libdir), VG_PLATFORM, preload_core);
-   }
-   VG_(free)(preload_tool_path);
-
-   VG_(debugLog)(2, "main", "preload_string:\n");
-   VG_(debugLog)(2, "main", "  \"%s\"\n", preload_string);
-
-   /* Count the original size of the env */
-   envc = 0;
-   for (cpp = origenv; cpp && *cpp; cpp++)
-      envc++;
-
-   /* Allocate a new space */
-   ret = VG_(malloc) (sizeof(HChar *) * (envc+1+1)); /* 1 new entry + NULL */
-   vg_assert(ret);
-
-   /* copy it over */
-   for (cpp = ret; *origenv; )
-      *cpp++ = *origenv++;
-   *cpp = NULL;
-   
-   vg_assert(envc == (cpp - ret));
-
-   /* Walk over the new environment, mashing as we go */
-   for (cpp = ret; cpp && *cpp; cpp++) {
-      if (VG_(memcmp)(*cpp, ld_preload, ld_preload_len) == 0) {
-         Int len = VG_(strlen)(*cpp) + preload_string_len;
-         HChar *cp = VG_(malloc)(len);
-         vg_assert(cp);
-
-         VG_(snprintf)(cp, len, "%s%s:%s",
-                       ld_preload, preload_string, (*cpp)+ld_preload_len);
-
-         *cpp = cp;
-
-         ld_preload_done = True;
-      }
-   }
-
-   /* Add the missing bits */
-   if (!ld_preload_done) {
-      Int len = ld_preload_len + preload_string_len;
-      HChar *cp = VG_(malloc) (len);
-      vg_assert(cp);
-
-      VG_(snprintf)(cp, len, "%s%s", ld_preload, preload_string);
-
-      ret[envc++] = cp;
-   }
-
-   /* ret[0 .. envc-1] is live now. */
-   /* Find and remove a binding for VALGRIND_LAUNCHER. */
-   for (i = 0; i < envc; i++)
-      if (0 == VG_(memcmp(ret[i], v_launcher, v_launcher_len)))
-         break;
-
-   if (i < envc) {
-      for (; i < envc-1; i++)
-         ret[i] = ret[i+1];
-      envc--;
-   }
-
-   VG_(free)(preload_string);
-   ret[envc] = NULL;
-
-   return ret;
-}
-
-
-/*====================================================================*/
-/*=== Setting up the client's stack                                ===*/
-/*====================================================================*/
-
-#ifndef AT_DCACHEBSIZE
-#define AT_DCACHEBSIZE         19
-#endif /* AT_DCACHEBSIZE */
-
-#ifndef AT_ICACHEBSIZE
-#define AT_ICACHEBSIZE         20
-#endif /* AT_ICACHEBSIZE */
-
-#ifndef AT_UCACHEBSIZE
-#define AT_UCACHEBSIZE         21
-#endif /* AT_UCACHEBSIZE */
-
-#ifndef AT_SYSINFO
-#define AT_SYSINFO             32
-#endif /* AT_SYSINFO */
-
-#ifndef AT_SYSINFO_EHDR
-#define AT_SYSINFO_EHDR                33
-#endif /* AT_SYSINFO_EHDR */
-
-#ifndef AT_SECURE
-#define AT_SECURE 23   /* secure mode boolean */
-#endif /* AT_SECURE */
-
-/* Add a string onto the string table, and return its address */
-static char *copy_str(char **tab, const char *str)
-{
-   char *cp = *tab;
-   char *orig = cp;
-
-   while(*str)
-      *cp++ = *str++;
-   *cp++ = '\0';
-
-   if (0)
-      VG_(printf)("copied %p \"%s\" len %lld\n", orig, orig, (Long)(cp-orig));
-
-   *tab = cp;
-
-   return orig;
-}
-
-
-/* ----------------------------------------------------------------
-   This sets up the client's initial stack, containing the args,
-   environment and aux vector.
-
-   The format of the stack is:
-
-   higher address +-----------------+ <- clstack_end
-                  |                 |
-                 : string table    :
-                 |                 |
-                 +-----------------+
-                 | AT_NULL         |
-                 -                 -
-                 | auxv            |
-                 +-----------------+
-                 | NULL            |
-                 -                 -
-                 | envp            |
-                 +-----------------+
-                 | NULL            |
-                 -                 -
-                 | argv            |
-                 +-----------------+
-                 | argc            |
-   lower address  +-----------------+ <- sp
-                  | undefined       |
-                 :                 :
-
-   Allocate and create the initial client stack.  It is allocated down
-   from clstack_end, which was previously determined by the address
-   space manager.  The returned value is the SP value for the client.
-
-   The client's auxv is created by copying and modifying our own one.
-   As a side effect of scanning our own auxv, some important bits of
-   info are collected:
-
-      VG_(cache_line_size_ppc32) // ppc32 only -- cache line size
-      VG_(have_altivec_ppc32)    // ppc32 only -- is Altivec supported?
-
-   ---------------------------------------------------------------- */
-
-static 
-Addr setup_client_stack( void*  init_sp,
-                         char** orig_envp, 
-                         const ExeInfo* info,
-                         UInt** client_auxv,
-                         Addr   clstack_end,
-                         SizeT  clstack_max_size )
-{
-   SysRes res;
-   char **cpp;
-   char *strtab;               /* string table */
-   char *stringbase;
-   Addr *ptr;
-   struct ume_auxv *auxv;
-   const struct ume_auxv *orig_auxv;
-   const struct ume_auxv *cauxv;
-   unsigned stringsize;                /* total size of strings in bytes */
-   unsigned auxsize;           /* total size of auxv in bytes */
-   Int argc;                   /* total argc */
-   Int envc;                   /* total number of env vars */
-   unsigned stacksize;         /* total client stack size */
-   Addr client_SP;             /* client stack base (initial SP) */
-   Addr clstack_start;
-   Int i;
-   Bool have_exename;
-
-   vg_assert(VG_IS_PAGE_ALIGNED(clstack_end+1));
-
-   /* use our own auxv as a prototype */
-   orig_auxv = VG_(find_auxv)(init_sp);
-
-   /* ==================== compute sizes ==================== */
-
-   /* first of all, work out how big the client stack will be */
-   stringsize   = 0;
-   have_exename = VG_(args_the_exename) != NULL;
-
-   /* paste on the extra args if the loader needs them (ie, the #! 
-      interpreter and its argument) */
-   argc = 0;
-   if (info->interp_name != NULL) {
-      argc++;
-      stringsize += VG_(strlen)(info->interp_name) + 1;
-   }
-   if (info->interp_args != NULL) {
-      argc++;
-      stringsize += VG_(strlen)(info->interp_args) + 1;
-   }
-
-   /* now scan the args we're given... */
-   if (have_exename)
-      stringsize += VG_(strlen)( VG_(args_the_exename) ) + 1;
-
-   for (i = 0; i < VG_(args_for_client).used; i++) {
-      argc++;
-      stringsize += VG_(strlen)( VG_(args_for_client).strs[i] ) + 1;
-   }
-
-   /* ...and the environment */
-   envc = 0;
-   for (cpp = orig_envp; cpp && *cpp; cpp++) {
-      envc++;
-      stringsize += VG_(strlen)(*cpp) + 1;
-   }
-
-   /* now, how big is the auxv? */
-   auxsize = sizeof(*auxv);    /* there's always at least one entry: AT_NULL */
-   for (cauxv = orig_auxv; cauxv->a_type != AT_NULL; cauxv++) {
-      if (cauxv->a_type == AT_PLATFORM)
-        stringsize += VG_(strlen)(cauxv->u.a_ptr) + 1;
-      auxsize += sizeof(*cauxv);
-   }
-
-#  if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
-   auxsize += 2 * sizeof(*cauxv);
-#  endif
-
-   /* OK, now we know how big the client stack is */
-   stacksize =
-      sizeof(Word) +                          /* argc */
-      (have_exename ? sizeof(char **) : 0) +  /* argc[0] == exename */
-      sizeof(char **)*argc +                  /* argv */
-      sizeof(char **) +                              /* terminal NULL */
-      sizeof(char **)*envc +                  /* envp */
-      sizeof(char **) +                              /* terminal NULL */
-      auxsize +                               /* auxv */
-      VG_ROUNDUP(stringsize, sizeof(Word));   /* strings (aligned) */
-
-   if (0) VG_(printf)("stacksize = %d\n", stacksize);
-
-   /* client_SP is the client's stack pointer */
-   client_SP = clstack_end - stacksize;
-   client_SP = VG_ROUNDDN(client_SP, 16); /* make stack 16 byte aligned */
-
-   /* base of the string table (aligned) */
-   stringbase = strtab = (char *)clstack_end 
-                         - VG_ROUNDUP(stringsize, sizeof(int));
-
-   clstack_start = VG_PGROUNDDN(client_SP);
-
-   /* The max stack size */
-   clstack_max_size = VG_PGROUNDUP(clstack_max_size);
-
-   /* Record stack extent -- needed for stack-change code. */
-   VG_(clstk_base) = clstack_start;
-   VG_(clstk_end)  = clstack_end;
-
-   if (0)
-      VG_(printf)("stringsize=%d auxsize=%d stacksize=%d maxsize=0x%x\n"
-                  "clstack_start %p\n"
-                  "clstack_end   %p\n",
-                 stringsize, auxsize, stacksize, (Int)clstack_max_size,
-                  (void*)clstack_start, (void*)clstack_end);
-
-   /* ==================== allocate space ==================== */
-
-   { SizeT anon_size   = clstack_end - clstack_start + 1;
-     SizeT resvn_size  = clstack_max_size - anon_size;
-     Addr  anon_start  = clstack_start;
-     Addr  resvn_start = anon_start - resvn_size;
-     SizeT inner_HACK  = 0;
-     Bool  ok;
-
-     /* So far we've only accounted for space requirements down to the
-        stack pointer.  If this target's ABI requires a redzone below
-        the stack pointer, we need to allocate an extra page, to
-        handle the worst case in which the stack pointer is almost at
-        the bottom of a page, and so there is insufficient room left
-        over to put the redzone in.  In this case the simple thing to
-        do is allocate an extra page, by shrinking the reservation by
-        one page and growing the anonymous area by a corresponding
-        page. */
-     vg_assert(VG_STACK_REDZONE_SZB >= 0);
-     vg_assert(VG_STACK_REDZONE_SZB < VKI_PAGE_SIZE);
-     if (VG_STACK_REDZONE_SZB > 0) {
-        vg_assert(resvn_size > VKI_PAGE_SIZE);
-        resvn_size -= VKI_PAGE_SIZE;
-        anon_start -= VKI_PAGE_SIZE;
-        anon_size += VKI_PAGE_SIZE;
-     }
-
-     vg_assert(VG_IS_PAGE_ALIGNED(anon_size));
-     vg_assert(VG_IS_PAGE_ALIGNED(resvn_size));
-     vg_assert(VG_IS_PAGE_ALIGNED(anon_start));
-     vg_assert(VG_IS_PAGE_ALIGNED(resvn_start));
-     vg_assert(resvn_start == clstack_end + 1 - clstack_max_size);
-
-#    ifdef ENABLE_INNER
-     inner_HACK = 1024*1024; // create 1M non-fault-extending stack
-#    endif
-
-     if (0)
-        VG_(printf)("%p 0x%x  %p 0x%x\n", 
-                    resvn_start, resvn_size, anon_start, anon_size);
-
-     /* Create a shrinkable reservation followed by an anonymous
-        segment.  Together these constitute a growdown stack. */
-     ok = VG_(am_create_reservation)(
-             resvn_start,
-             resvn_size -inner_HACK,
-             SmUpper, 
-             anon_size +inner_HACK
-          );
-     vg_assert(ok);
-     /* allocate a stack - mmap enough space for the stack */
-     res = VG_(am_mmap_anon_fixed_client)(
-              anon_start -inner_HACK,
-              anon_size +inner_HACK,
-             VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC
-          );
-     vg_assert(!res.isError); 
-   }
-
-   /* ==================== create client stack ==================== */
-
-   ptr = (Addr*)client_SP;
-
-   /* --- client argc --- */
-   *ptr++ = argc + (have_exename ? 1 : 0);
-
-   /* --- client argv --- */
-   if (info->interp_name) {
-      *ptr++ = (Addr)copy_str(&strtab, info->interp_name);
-      VG_(free)(info->interp_name);
-   }
-   if (info->interp_args) {
-      *ptr++ = (Addr)copy_str(&strtab, info->interp_args);
-      VG_(free)(info->interp_args);
-   }
-
-   if (have_exename)
-      *ptr++ = (Addr)copy_str(&strtab, VG_(args_the_exename));
-
-   for (i = 0; i < VG_(args_for_client).used; i++) {
-      *ptr++ = (Addr)copy_str(&strtab, VG_(args_for_client).strs[i]);
-   }
-   *ptr++ = 0;
-
-   /* --- envp --- */
-   VG_(client_envp) = (Char **)ptr;
-   for (cpp = orig_envp; cpp && *cpp; ptr++, cpp++)
-      *ptr = (Addr)copy_str(&strtab, *cpp);
-   *ptr++ = 0;
-
-   /* --- auxv --- */
-   auxv = (struct ume_auxv *)ptr;
-   *client_auxv = (UInt *)auxv;
-
-#  if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
-   auxv[0].a_type  = AT_IGNOREPPC;
-   auxv[0].u.a_val = AT_IGNOREPPC;
-   auxv[1].a_type  = AT_IGNOREPPC;
-   auxv[1].u.a_val = AT_IGNOREPPC;
-   auxv += 2;
-#  endif
-
-   for (; orig_auxv->a_type != AT_NULL; auxv++, orig_auxv++) {
-
-      /* copy the entry... */
-      *auxv = *orig_auxv;
-
-      /* ...and fix up / examine the copy */
-      switch(auxv->a_type) {
-
-         case AT_IGNORE:
-         case AT_PHENT:
-         case AT_PAGESZ:
-         case AT_FLAGS:
-         case AT_NOTELF:
-         case AT_UID:
-         case AT_EUID:
-         case AT_GID:
-         case AT_EGID:
-         case AT_CLKTCK:
-         case AT_FPUCW:
-            /* All these are pointerless, so we don't need to do
-               anything about them. */
-            break;
-
-         case AT_PHDR:
-            if (info->phdr == 0)
-               auxv->a_type = AT_IGNORE;
-            else
-               auxv->u.a_val = info->phdr;
-            break;
-
-         case AT_PHNUM:
-            if (info->phdr == 0)
-               auxv->a_type = AT_IGNORE;
-            else
-               auxv->u.a_val = info->phnum;
-            break;
-
-         case AT_BASE:
-            auxv->u.a_val = info->interp_base;
-            break;
-
-         case AT_PLATFORM:
-            /* points to a platform description string */
-            auxv->u.a_ptr = copy_str(&strtab, orig_auxv->u.a_ptr);
-            break;
-
-         case AT_ENTRY:
-            auxv->u.a_val = info->entry;
-            break;
-
-         case AT_HWCAP:
-            break;
-
-         case AT_DCACHEBSIZE:
-         case AT_ICACHEBSIZE:
-         case AT_UCACHEBSIZE:
-#           if defined(VGP_ppc32_linux)
-            /* acquire cache info */
-            if (auxv->u.a_val > 0) {
-               VG_(machine_ppc32_set_clszB)( auxv->u.a_val );
-               VG_(debugLog)(2, "main", 
-                                "PPC32 cache line size %u (type %u)\n", 
-                                (UInt)auxv->u.a_val, (UInt)auxv->a_type );
-            }
-#           elif defined(VGP_ppc64_linux)
-            /* acquire cache info */
-            if (auxv->u.a_val > 0) {
-               VG_(machine_ppc64_set_clszB)( auxv->u.a_val );
-               VG_(debugLog)(2, "main", 
-                                "PPC64 cache line size %u (type %u)\n", 
-                                (UInt)auxv->u.a_val, (UInt)auxv->a_type );
-            }
-#           endif
-            break;
-
-#        if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
-         case AT_IGNOREPPC:
-            break;
-#        endif
-
-         case AT_SECURE:
-            /* If this is 1, then it means that this program is
-               running suid, and therefore the dynamic linker should
-               be careful about LD_PRELOAD, etc.  However, since
-               stage1 (the thing the kernel actually execve's) should
-               never be SUID, and we need LD_PRELOAD to work for the
-               client, we set AT_SECURE to 0. */
-            auxv->u.a_val = 0;
-            break;
-
-         case AT_SYSINFO:
-#        if !defined(VGP_ppc32_linux) && !defined(VGP_ppc64_linux)
-         case AT_SYSINFO_EHDR:
-#        endif
-            /* Trash this, because we don't reproduce it */
-            auxv->a_type = AT_IGNORE;
-            break;
-
-         default:
-            /* stomp out anything we don't know about */
-            VG_(debugLog)(2, "main",
-                             "stomping auxv entry %lld\n", 
-                             (ULong)auxv->a_type);
-            auxv->a_type = AT_IGNORE;
-            break;
-      }
-   }
-   *auxv = *orig_auxv;
-   vg_assert(auxv->a_type == AT_NULL);
-
-   vg_assert((strtab-stringbase) == stringsize);
-
-   /* client_SP is pointing at client's argc/argv */
-
-   if (0) VG_(printf)("startup SP = %p\n", client_SP);
-   return client_SP;
-}
-
-
-/* Allocate the client data segment.  It is an expandable anonymous
-   mapping abutting a shrinkable reservation of size max_dseg_size.
-   The data segment starts at VG_(brk_base), which is page-aligned,
-   and runs up to VG_(brk_limit), which isn't. */
-
-static void setup_client_dataseg ( SizeT max_size )
-{
-   Bool   ok;
-   SysRes sres;
-   Addr   anon_start  = VG_(brk_base);
-   SizeT  anon_size   = VKI_PAGE_SIZE;
-   Addr   resvn_start = anon_start + anon_size;
-   SizeT  resvn_size  = max_size - anon_size;
-
-   vg_assert(VG_IS_PAGE_ALIGNED(anon_size));
-   vg_assert(VG_IS_PAGE_ALIGNED(resvn_size));
-   vg_assert(VG_IS_PAGE_ALIGNED(anon_start));
-   vg_assert(VG_IS_PAGE_ALIGNED(resvn_start));
-
-   /* Because there's been no brk activity yet: */
-   vg_assert(VG_(brk_base) == VG_(brk_limit));
-
-   /* Try to create the data seg and associated reservation where
-      VG_(brk_base) says. */
-   ok = VG_(am_create_reservation)( 
-           resvn_start, 
-           resvn_size, 
-           SmLower, 
-           anon_size
-        );
-
-   if (!ok) {
-      /* Hmm, that didn't work.  Well, let aspacem suggest an address
-         it likes better, and try again with that. */
-      anon_start = VG_(am_get_advisory_client_simple)
-                      ( 0/*floating*/, anon_size+resvn_size, &ok );
-      if (ok) {
-         resvn_start = anon_start + anon_size;
-         ok = VG_(am_create_reservation)( 
-                 resvn_start, 
-                 resvn_size, 
-                 SmLower, 
-                 anon_size
-              );
-         if (ok)
-            VG_(brk_base) = VG_(brk_limit) = anon_start;
-      }
-      /* that too might have failed, but if it has, we're hosed: there
-         is no Plan C. */
-   }
-   vg_assert(ok);
-
-   /* We make the data segment (heap) executable because LinuxThreads on
-      ppc32 creates trampolines in this area.  Also, on x86/Linux the data
-      segment is RWX natively, at least according to /proc/self/maps.
-      Also, having a non-executable data seg would kill any program which
-      tried to create code in the data seg and then run it. */
-   sres = VG_(am_mmap_anon_fixed_client)( 
-             anon_start, 
-             anon_size, 
-             VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC
-          );
-   vg_assert(!sres.isError);
-   vg_assert(sres.val == anon_start);
-}
-
-
-/*====================================================================*/
-/*=== Find executable                                              ===*/
-/*====================================================================*/
-
-/* Scan a colon-separated list, and call a function on each element.
-   The string must be mutable, because we insert a temporary '\0', but
-   the string will end up unmodified.  (*func) should return True if it
-   doesn't need to see any more.
-
-   This routine will return True if (*func) returns True and False if
-   it reaches the end of the list without that happening.
-*/
-static Bool scan_colsep(char *colsep, Bool (*func)(const char *))
-{
-   char *cp, *entry;
-   int end;
-
-   if (colsep == NULL ||
-       *colsep == '\0')
-      return False;
-
-   entry = cp = colsep;
-
-   do {
-      end = (*cp == '\0');
-
-      if (*cp == ':' || *cp == '\0') {
-        char save = *cp;
-
-        *cp = '\0';
-        if ((*func)(entry)) {
-            *cp = save;
-           return True;
-         }
-        *cp = save;
-        entry = cp+1;
-      }
-      cp++;
-   } while(!end);
-
-   return False;
-}
-
-/* Need a static copy because can't use dynamic mem allocation yet */
-static HChar executable_name_in [VKI_PATH_MAX];
-static HChar executable_name_out[VKI_PATH_MAX];
-
-static Bool match_executable(const char *entry) 
-{
-   HChar buf[VG_(strlen)(entry) + VG_(strlen)(executable_name_in) + 3];
-
-   /* empty PATH element means '.' */
-   if (*entry == '\0')
-      entry = ".";
-
-   VG_(snprintf)(buf, sizeof(buf), "%s/%s", entry, executable_name_in);
-
-   // Don't match directories
-   if (VG_(is_dir)(buf))
-      return False;
-
-   // If we match an executable, we choose that immediately.  If we find a
-   // matching non-executable we remember it but keep looking for an
-   // matching executable later in the path.
-   if (VG_(access)(buf, True/*r*/, False/*w*/, True/*x*/) == 0) {
-      VG_(strncpy)( executable_name_out, buf, VKI_PATH_MAX-1 );
-      executable_name_out[VKI_PATH_MAX-1] = 0;
-      return True;      // Stop looking
-   } else if (VG_(access)(buf, True/*r*/, False/*w*/, False/*x*/) == 0 
-           && VG_STREQ(executable_name_out, "")) 
-   {
-      VG_(strncpy)( executable_name_out, buf, VKI_PATH_MAX-1 );
-      executable_name_out[VKI_PATH_MAX-1] = 0;
-      return False;     // Keep looking
-   } else { 
-      return False;     // Keep looking
-   }
-}
-
-// Returns NULL if it wasn't found.
-static HChar* find_executable ( HChar* exec )
-{
-   vg_assert(NULL != exec);
-   if (VG_(strchr)(exec, '/')) {
-      // Has a '/' - use the name as is
-      VG_(strncpy)( executable_name_out, exec, VKI_PATH_MAX-1 );
-   } else {
-      // No '/' - we need to search the path
-      HChar* path;
-      VG_(strncpy)( executable_name_in,  exec, VKI_PATH_MAX-1 );
-      VG_(memset) ( executable_name_out, 0,    VKI_PATH_MAX );
-      path = VG_(getenv)("PATH");
-      scan_colsep(path, match_executable);
-   }
-   return VG_STREQ(executable_name_out, "") ? NULL : executable_name_out;
-}
-
-
-/*====================================================================*/
-/*=== Command line errors                                          ===*/
-/*====================================================================*/
-
-static void revert_to_stderr ( void )
-{
-   vg_assert( !VG_(logging_to_socket) );
-   VG_(clo_log_fd) = 2; /* stderr */
-}
-
-void VG_(bad_option) ( Char* opt )
-{
-   revert_to_stderr();
-   VG_(printf)("valgrind: Bad option '%s'; aborting.\n", opt);
-   VG_(printf)("valgrind: Use --help for more information.\n");
-   VG_(exit)(1);
-}
-
-static void missing_prog ( void  )
-{
-   revert_to_stderr();
-   VG_(printf)("valgrind: no program specified\n");
-   VG_(printf)("valgrind: Use --help for more information.\n");
-   VG_(exit)(1);
-}
-
-static void config_error ( Char* msg )
-{
-   revert_to_stderr();
-   VG_(printf)("valgrind: Startup or configuration error:\n   %s\n", msg);
-   VG_(printf)("valgrind: Unable to start up properly.  Giving up.\n");
-   VG_(exit)(1);
-}
-
-
-/*====================================================================*/
-/*=== Loading the client                                           ===*/
-/*====================================================================*/
-
-/* Load the client whose name is VG_(argv_the_exename). */
-
-static void load_client ( /*OUT*/ExeInfo* info, 
-                          /*OUT*/Addr*    client_ip,
-                         /*OUT*/Addr*    client_toc)
-{
-   HChar* exe_name;
-   Int    ret;
-   SysRes res;
-
-   vg_assert( VG_(args_the_exename) != NULL);
-   exe_name = find_executable( VG_(args_the_exename) );
-
-   if (!exe_name) {
-      VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename));
-      VG_(exit)(127);      // 127 is Posix NOTFOUND
-   }
-
-   VG_(memset)(info, 0, sizeof(*info));
-   info->exe_base = VG_(client_base);
-   info->exe_end  = VG_(client_end);
-
-   ret = VG_(do_exec)(exe_name, info);
-
-   // The client was successfully loaded!  Continue.
-
-   /* Get hold of a file descriptor which refers to the client
-      executable.  This is needed for attaching to GDB. */
-   res = VG_(open)(exe_name, VKI_O_RDONLY, VKI_S_IRUSR);
-   if (!res.isError)
-      VG_(cl_exec_fd) = res.val;
-
-   /* Copy necessary bits of 'info' that were filled in */
-   *client_ip  = info->init_ip;
-   *client_toc = info->init_toc;
-   VG_(brk_base) = VG_(brk_limit) = VG_PGROUNDUP(info->brkbase);
-}
-
-
 /*====================================================================*/
 /*=== Command-line: variables, processing, etc                     ===*/
 /*====================================================================*/
@@ -943,6 +162,7 @@ static void usage_NORETURN ( Bool debug_help )
 "    --trace-redir=no|yes      show redirection details? [no]\n"
 "    --trace-sched=no|yes      show thread scheduler details? [no]\n"
 "    --wait-for-gdb=yes|no     pause on startup to wait for gdb attach\n"
+"    --sym-offsets=yes|no      show syms in form 'name+offset' ? [no]\n"
 #if 0
 "    --model-pthreads=yes|no   model the pthreads library [no]\n"
 #endif
@@ -1067,8 +287,8 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
 
    /* Check for sane path in ./configure --prefix=... */
    if (VG_LIBDIR[0] != '/') 
-     config_error("Please use absolute paths in "
-                  "./configure --prefix=... or --libdir=...");
+      VG_(err_config_error)("Please use absolute paths in "
+                            "./configure --prefix=... or --libdir=...");
 
    for (i = 0; i < VG_(args_for_valgrind).used; i++) {
 
@@ -1203,7 +423,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
             VG_(message)(Vg_UserMsg, "Too many suppression files specified.");
             VG_(message)(Vg_UserMsg, 
                          "Increase VG_CLO_MAX_SFILES and recompile.");
-            VG_(bad_option)(arg);
+            VG_(err_bad_option)(arg);
          }
          VG_(clo_suppressions)[VG_(clo_n_suppressions)] = &arg[15];
          VG_(clo_n_suppressions)++;
@@ -1217,7 +437,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
          if (8 != VG_(strlen)(opt)) {
             VG_(message)(Vg_UserMsg, 
                          "--trace-flags argument must have 8 digits");
-            VG_(bad_option)(arg);
+            VG_(err_bad_option)(arg);
          }
          for (j = 0; j < 8; j++) {
             if      ('0' == opt[j]) { /* do nothing */ }
@@ -1225,7 +445,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
             else {
                VG_(message)(Vg_UserMsg, "--trace-flags argument can only "
                                         "contain 0s and 1s");
-               VG_(bad_option)(arg);
+               VG_(err_bad_option)(arg);
             }
          }
       }
@@ -1238,7 +458,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
          if (8 != VG_(strlen)(opt)) {
             VG_(message)(Vg_UserMsg, 
                          "--profile-flags argument must have 8 digits");
-            VG_(bad_option)(arg);
+            VG_(err_bad_option)(arg);
          }
          for (j = 0; j < 8; j++) {
             if      ('0' == opt[j]) { /* do nothing */ }
@@ -1246,7 +466,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
             else {
                VG_(message)(Vg_UserMsg, "--profile-flags argument can only "
                                         "contain 0s and 1s");
-               VG_(bad_option)(arg);
+               VG_(err_bad_option)(arg);
             }
          }
       }
@@ -1262,7 +482,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
 
       else if ( ! VG_(needs).command_line_options
              || ! VG_TDICT_CALL(tool_process_cmd_line_option, arg) ) {
-         VG_(bad_option)(arg);
+         VG_(err_bad_option)(arg);
       }
     skip_arg:
       if (arg != VG_(args_for_valgrind).strs[i]) {
@@ -1291,7 +511,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
          "--db-attach=yes conflicts with --trace-children=yes");
       VG_(message)(Vg_UserMsg, 
          "Please choose one or the other, but not both.");
-      VG_(bad_option)("--db-attach=yes and --trace-children=yes");
+      VG_(err_bad_option)("--db-attach=yes and --trace-children=yes");
    }
 
    if (VG_(clo_gen_suppressions) > 0 && 
@@ -1300,7 +520,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
                    "Can't use --gen-suppressions= with this tool,");
       VG_(message)(Vg_UserMsg, 
                    "as it doesn't generate errors.");
-      VG_(bad_option)("--gen-suppressions=");
+      VG_(err_bad_option)("--gen-suppressions=");
    }
 
    /* If we've been asked to emit XML, mash around various other
@@ -1398,7 +618,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
                  VG_(message)(Vg_UserMsg, 
                               "Can't create log file '%s' (%s); giving up!", 
                               logfilename, VG_(strerror)(sres.val));
-                 VG_(bad_option)(
+                 VG_(err_bad_option)(
                     "--log-file=<file> (didn't work out for some reason.)");
                   /*NOTREACHED*/
               }
@@ -1420,7 +640,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
             VG_(message)(Vg_UserMsg, 
                          "Can't create/open log file '%s'; giving up!", 
                          VG_(clo_log_name));
-            VG_(bad_option)(
+            VG_(err_bad_option)(
                "--log-file-exactly=<file> (didn't work out for some reason.)");
             /*NOTREACHED*/
         }
@@ -1436,7 +656,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
                "Invalid --log-socket=ipaddr or --log-socket=ipaddr:port spec"); 
             VG_(message)(Vg_UserMsg, 
                "of '%s'; giving up!", VG_(clo_log_name) );
-            VG_(bad_option)(
+            VG_(err_bad_option)(
                "--log-socket=");
             /*NOTREACHED*/
         }
@@ -1465,7 +685,7 @@ static Bool process_cmd_line_options( UInt* client_auxv, const char* toolname )
       VG_(clo_xml) = False;
       VG_(message)(Vg_UserMsg, 
          "%s does not support XML output.", VG_(details).name); 
-      VG_(bad_option)("--xml=yes");
+      VG_(err_bad_option)("--xml=yes");
       /*NOTREACHED*/
    }
 
@@ -1689,6 +909,7 @@ static void print_preamble(Bool logging_to_fd, const char* toolname)
 static void setup_file_descriptors(void)
 {
    struct vki_rlimit rl;
+   Bool show = False;
 
    /* Get the current file descriptor limits. */
    if (VG_(getrlimit)(VKI_RLIMIT_NOFILE, &rl) < 0) {
@@ -1696,6 +917,20 @@ static void setup_file_descriptors(void)
       rl.rlim_max = 1024;
    }
 
+   if (show)
+      VG_(printf)("fd limits: host, before: cur %u max %u\n", 
+                  rl.rlim_cur, rl.rlim_max);
+
+#  if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
+   /* I don't know why this kludge is needed; however if rl.rlim_cur
+      is RLIM_INFINITY, then VG_(safe_fd)'s attempts using VG_(fcntl)
+      to lift V's file descriptors above the threshold RLIM_INFINITY -
+      N_RESERVED_FDS fail.  So just use a relatively conservative
+      value in this case. */
+   if (rl.rlim_cur > 1024)
+      rl.rlim_cur = 1024;
+#  endif
+
    /* Work out where to move the soft limit to. */
    if (rl.rlim_cur + N_RESERVED_FDS <= rl.rlim_max) {
       rl.rlim_cur = rl.rlim_cur + N_RESERVED_FDS;
@@ -1710,95 +945,18 @@ static void setup_file_descriptors(void)
    /* Update the soft limit. */
    VG_(setrlimit)(VKI_RLIMIT_NOFILE, &rl);
 
+   if (show) {
+      VG_(printf)("fd limits: host,  after: cur %u max %u\n",
+                  rl.rlim_cur, rl.rlim_max);
+      VG_(printf)("fd limits: guest       : cur %u max %u\n",
+                  VG_(fd_soft_limit), VG_(fd_hard_limit));
+   }
+
    if (VG_(cl_exec_fd) != -1)
       VG_(cl_exec_fd) = VG_(safe_fd)( VG_(cl_exec_fd) );
 }
 
 
-/*====================================================================*/
-/*===  Initialise the first thread.                                ===*/
-/*====================================================================*/
-
-/* Given a pointer to the ThreadArchState for thread 1 (the root
-   thread), initialise the VEX guest state, and copy in essential
-   starting values.
-*/
-static void init_thread1state ( Addr client_ip, 
-                                Addr client_sp,
-                                Addr client_toc,
-                                /*inout*/ ThreadArchState* arch )
-{
-#if defined(VGA_x86)
-   vg_assert(0 == sizeof(VexGuestX86State) % 8);
-
-   /* Zero out the initial state, and set up the simulated FPU in a
-      sane way. */
-   LibVEX_GuestX86_initialise(&arch->vex);
-
-   /* Zero out the shadow area. */
-   VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestX86State));
-
-   /* Put essential stuff into the new state. */
-   arch->vex.guest_ESP = client_sp;
-   arch->vex.guest_EIP = client_ip;
-
-   /* initialise %cs, %ds and %ss to point at the operating systems
-      default code, data and stack segments */
-   asm volatile("movw %%cs, %0" : : "m" (arch->vex.guest_CS));
-   asm volatile("movw %%ds, %0" : : "m" (arch->vex.guest_DS));
-   asm volatile("movw %%ss, %0" : : "m" (arch->vex.guest_SS));
-
-#elif defined(VGA_amd64)
-   vg_assert(0 == sizeof(VexGuestAMD64State) % 8);
-
-   /* Zero out the initial state, and set up the simulated FPU in a
-      sane way. */
-   LibVEX_GuestAMD64_initialise(&arch->vex);
-
-   /* Zero out the shadow area. */
-   VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestAMD64State));
-
-   /* Put essential stuff into the new state. */
-   arch->vex.guest_RSP = client_sp;
-   arch->vex.guest_RIP = client_ip;
-
-#elif defined(VGA_ppc32)
-   vg_assert(0 == sizeof(VexGuestPPC32State) % 8);
-
-   /* Zero out the initial state, and set up the simulated FPU in a
-      sane way. */
-   LibVEX_GuestPPC32_initialise(&arch->vex);
-
-   /* Zero out the shadow area. */
-   VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestPPC32State));
-
-   /* Put essential stuff into the new state. */
-   arch->vex.guest_GPR1 = client_sp;
-   arch->vex.guest_CIA  = client_ip;
-
-#elif defined(VGA_ppc64)
-   vg_assert(0 == sizeof(VexGuestPPC64State) % 16);
-
-   /* Zero out the initial state, and set up the simulated FPU in a
-      sane way. */
-   LibVEX_GuestPPC64_initialise(&arch->vex);
-
-   /* Zero out the shadow area. */
-   VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestPPC64State));
-
-   /* Put essential stuff into the new state. */
-   arch->vex.guest_GPR1 = client_sp;
-   arch->vex.guest_GPR2 = client_toc;
-   arch->vex.guest_CIA  = client_ip;
-#else
-#  error Unknown arch
-#endif
-   // Tell the tool that we just wrote to the registers.
-   VG_TRACK( post_reg_write, Vg_CoreStartup, /*tid*/1, /*offset*/0,
-             sizeof(VexGuestArchState));
-}
-
-
 /*====================================================================*/
 /*=== BB profiling                                                 ===*/
 /*====================================================================*/
@@ -1943,22 +1101,23 @@ static Addr* get_seg_starts ( /*OUT*/Int* n_acquired )
 }
 
 
-
-Int main(Int argc, HChar **argv, HChar **envp)
+static
+Int valgrind_main ( Int argc, HChar **argv, HChar **envp,
+                    ULong* ppc_aix_initial_client_intregs37,
+                    void*  ppc_aix_bootblock,
+                    UInt   ppc_aix_adler32_for_compressed_page )
 {
    HChar*  toolname           = "memcheck";    // default to Memcheck
-   HChar** env                = NULL;
    Int     need_help          = 0; // 0 = no, 1 = --help, 2 = --help-debug
-   Addr    initial_client_IP  = 0;
-   Addr    initial_client_SP  = 0;
-   Addr    initial_client_TOC = 0;
    Addr    clstack_top        = 0;
    SizeT   clstack_max_size   = 0;
    UInt*   client_auxv        = NULL;
    Int     loglevel, i;
    Bool    logging_to_fd;
    struct vki_rlimit zero = { 0, 0 };
-   ExeInfo info;
+
+   ClientInitImgInfo ciii;
+   VG_(memset)(&ciii, 0, sizeof(ClientInitImgInfo));
 
    //============================================================
    //
@@ -1996,6 +1155,60 @@ Int main(Int argc, HChar **argv, HChar **envp)
    VG_(debugLog)(1, "main", "Welcome to Valgrind version " 
                             VERSION " debug logging\n");
 
+   //--------------------------------------------------------------
+   // AIX5 only: register the system call numbers
+   //   p: logging
+   //   p: that the initial few syscall numbers stated in the
+   //      bootblock have been installed (else we can't 
+   //      open/read/close).
+   //--------------------------------------------------------------
+#  if defined(VGO_aix5)
+   VG_(debugLog)(1, "main", "aix5: registering syscalls ..\n");
+   { UChar  sysent_name[50];
+     SysRes fd;
+     Bool   ok;
+     Int    n_unregd, sysent_used = 0;
+     prsysent_t* sysent_hdr;
+
+     VG_(sprintf)(sysent_name, "/proc/%d/sysent", VG_(getpid)());
+     fd = VG_(open)(sysent_name, VKI_O_RDONLY, 0);
+     if (fd.isError)
+        VG_(err_config_error)("aix5: can't open /proc/<pid>/sysent");
+
+     sysent_used = VG_(read)(fd.res, aix5_sysent_buf, VG_AIX5_SYSENT_SIZE);
+     if (sysent_used < 0)
+        VG_(err_config_error)("aix5: error reading /proc/<pid>/sysent");
+     if (sysent_used >= VG_AIX5_SYSENT_SIZE)
+        VG_(err_config_error)("aix5: VG_AIX5_SYSENT_SIZE is too low; "
+                              "increase and recompile");
+     VG_(close)(fd.res);
+
+     vg_assert(sysent_used > 0 && sysent_used < VG_AIX5_SYSENT_SIZE);
+
+     sysent_hdr = (prsysent_t*)&aix5_sysent_buf[0];
+
+     n_unregd = 0;
+     for (i = 0; i < sysent_hdr->pr_nsyscalls; i++) {
+        UChar* name = &aix5_sysent_buf[ sysent_hdr
+                                        ->pr_syscall[i].pr_nameoff ];
+        UInt   nmbr = sysent_hdr->pr_syscall[i].pr_number;
+        VG_(debugLog)(3, "main", "aix5: bind syscall %d to \"%s\"\n", 
+                                 nmbr, name);
+        ok = VG_(aix5_register_syscall)(nmbr, name);
+        if (!ok)
+           n_unregd++;
+       if (!ok)
+           VG_(debugLog)(3, "main", 
+                            "aix5: bind FAILED: %d to \"%s\"\n", 
+                            nmbr, name);
+     }
+     VG_(debugLog)(1, "main", "aix5: .. %d syscalls known, %d unknown\n",
+                      sysent_hdr->pr_nsyscalls - n_unregd, n_unregd );
+     VG_(debugLog)(1, "main", "aix5: __NR_AIX5_FAKE_SIGRETURN = %d\n",
+                      __NR_AIX5_FAKE_SIGRETURN );
+   }
+#  endif
+
    //--------------------------------------------------------------
    // Ensure we're on a plausible stack.
    //   p: logging
@@ -2150,72 +1363,35 @@ Int main(Int argc, HChar **argv, HChar **envp)
    // Load client executable, finding in $PATH if necessary
    //   p: get_helprequest_and_toolname()  [for 'exec', 'need_help']
    //   p: layout_remaining_space          [so there's space]
-   //--------------------------------------------------------------
-   if (!need_help) {
-      VG_(debugLog)(1, "main", "Loading client\n");
-
-      if (VG_(args_the_exename) == NULL)
-         missing_prog();
-
-      load_client(&info, &initial_client_IP, &initial_client_TOC);
-   }
-
-   //--------------------------------------------------------------
+   //
    // Set up client's environment
    //   p: set-libdir                   [for VG_(libdir)]
    //   p: get_helprequest_and_toolname [for toolname]
-   //--------------------------------------------------------------
-   if (!need_help) {
-      VG_(debugLog)(1, "main", "Setup client env\n");
-      env = setup_client_env(envp, toolname);
-   }
-
-   //--------------------------------------------------------------
+   //
    // Setup client stack, eip, and VG_(client_arg[cv])
    //   p: load_client()     [for 'info']
    //   p: fix_environment() [for 'env']
-   //--------------------------------------------------------------
-   if (!need_help) {
-      void* init_sp = argv - 1;
-      SizeT m1  = 1024 * 1024;
-      SizeT m16 = 16 * m1;
-      VG_(debugLog)(1, "main", "Setup client stack\n");
-      clstack_max_size = (SizeT)VG_(client_rlimit_stack).rlim_cur;
-      if (clstack_max_size < m1)  clstack_max_size = m1;
-      if (clstack_max_size > m16) clstack_max_size = m16;
-      clstack_max_size = VG_PGROUNDUP(clstack_max_size);
-
-      initial_client_SP
-         = setup_client_stack( init_sp, env, 
-                               &info, &client_auxv, 
-                               clstack_top, clstack_max_size );
-
-      VG_(free)(env);
-
-      VG_(debugLog)(2, "main",
-                       "Client info: "
-                       "initial_IP=%p initial_SP=%p initial_TOC=%p brk_base=%p\n",
-                       (void*)initial_client_IP, 
-                       (void*)initial_client_SP,
-                       (void*)initial_client_TOC,
-                       (void*)VG_(brk_base) );
-   }
-
-   //--------------------------------------------------------------
+   //
    // Setup client data (brk) segment.  Initially a 1-page segment
    // which abuts a shrinkable reservation. 
    //     p: load_client()     [for 'info' and hence VG_(brk_base)]
    //--------------------------------------------------------------
-   if (!need_help) { 
-      SizeT m1 = 1024 * 1024;
-      SizeT m8 = 8 * m1;
-      SizeT dseg_max_size = (SizeT)VG_(client_rlimit_data).rlim_cur;
-      VG_(debugLog)(1, "main", "Setup client data (brk) segment\n");
-      if (dseg_max_size < m1) dseg_max_size = m1;
-      if (dseg_max_size > m8) dseg_max_size = m8;
-      dseg_max_size = VG_PGROUNDUP(dseg_max_size);
-
-      setup_client_dataseg( dseg_max_size );
+   if (!need_help) {
+      VG_(debugLog)(1, "main", "Setting up initial image\n");
+      ciii = VG_(setup_client_initial_image)( 
+                argv, envp, toolname, 
+                clstack_top, clstack_max_size 
+             );
+#     if defined(VGO_aix5)
+      ciii.adler32_exp       = ppc_aix_adler32_for_compressed_page;
+      ciii.compressed_page   = VG_PGROUNDDN((Addr)ppc_aix_bootblock);
+      ciii.intregs37         = ppc_aix_initial_client_intregs37;
+      ciii.initial_client_SP = ciii.intregs37[1]; /* r1 */
+      /* Tell aspacem where the initial client stack is, so that it
+         can later produce a faked-up NSegment in response to
+         VG_(am_find_nsegment) for that address range, if asked. */
+      VG_(am_aix5_set_initial_client_sp)( ciii.initial_client_SP );
+#     endif
    }
 
    //==============================================================
@@ -2248,12 +1424,11 @@ Int main(Int argc, HChar **argv, HChar **envp)
       VG_(sprintf)(buf, "proc_%d_cmdline", VG_(getpid)());
       fd = VG_(mkstemp)( buf, buf2 );
       if (fd == -1)
-         config_error("Can't create client cmdline file in /tmp.");
+         VG_(err_config_error)("Can't create client cmdline file in /tmp.");
 
       nul[0] = 0;
       exename = VG_(args_the_exename) ? VG_(args_the_exename)
                                       : "unknown_exename";
-
       VG_(write)(fd, VG_(args_the_exename), 
                      VG_(strlen)( VG_(args_the_exename) ));
       VG_(write)(fd, nul, 1);
@@ -2271,7 +1446,7 @@ Int main(Int argc, HChar **argv, HChar **envp)
       /* Now delete it, but hang on to the fd. */
       r = VG_(unlink)( buf2 );
       if (r)
-         config_error("Can't delete client cmdline file in /tmp.");
+         VG_(err_config_error)("Can't delete client cmdline file in /tmp.");
 
       VG_(cl_cmdline_fd) = fd;
    }
@@ -2365,11 +1540,6 @@ Int main(Int argc, HChar **argv, HChar **envp)
       Long q, iters;
       VG_(debugLog)(1, "main", "Wait for GDB\n");
       VG_(printf)("pid=%d, entering delay loop\n", VG_(getpid)());
-      /* jrs 20050206: I don't understand why this works on x86.  On
-         amd64 the obvious analogues (jump *$rip or jump *$rcx) don't
-         work. */
-      /* do "jump *$eip" to skip this in gdb (x86) */
-      //VG_(do_syscall0)(__NR_pause);
 
 #     if defined(VGP_x86_linux)
       iters = 5;
@@ -2377,8 +1547,10 @@ Int main(Int argc, HChar **argv, HChar **envp)
       iters = 10;
 #     elif defined(VGP_ppc32_linux)
       iters = 5;
+#     elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
+      iters = 4;
 #     else
-#     error "Unknown plat"
+#       error "Unknown plat"
 #     endif
 
       iters *= 1000*1000*1000;
@@ -2402,11 +1574,12 @@ Int main(Int argc, HChar **argv, HChar **envp)
    //   p: probably: setup fds and process CLOs, so that logging works
    //--------------------------------------------------------------
    VG_(debugLog)(1, "main", "Load initial debug info\n");
+#  if defined(VGO_linux)
    { Addr* seg_starts;
      Int   n_seg_starts;
 
      seg_starts = get_seg_starts( &n_seg_starts );
-     vg_assert(seg_starts && n_seg_starts > 0);
+     vg_assert(seg_starts && n_seg_starts >= 0);
 
      /* show them all to the debug info reader.  allow_SkFileV has to
         be True here so that we read info from the valgrind executable
@@ -2416,6 +1589,38 @@ Int main(Int argc, HChar **argv, HChar **envp)
 
      VG_(free)( seg_starts );
    }
+#  elif defined(VGO_aix5)
+   { AixCodeSegChange* changes;
+     Int changes_size, changes_used;
+
+     /* Find out how many AixCodeSegChange records we will need,
+       and acquire them. */
+     changes_size = VG_(am_aix5_reread_procmap_howmany_directives)(); 
+     changes = VG_(malloc)(changes_size * sizeof(AixCodeSegChange));
+     vg_assert(changes);
+
+     /* Now re-read /proc/<pid>/map and acquire a change set */
+     VG_(am_aix5_reread_procmap)( changes, &changes_used );
+     vg_assert(changes_used >= 0 && changes_used <= changes_size);
+
+     /* And notify m_debuginfo of the changes. */
+     for (i = 0; i < changes_used; i++)
+        VG_(di_aix5_notify_segchange)(
+           changes[i].code_start,
+           changes[i].code_len,
+           changes[i].data_start,
+           changes[i].data_len,
+           changes[i].file_name,
+           changes[i].mem_name,
+           changes[i].is_mainexe,
+           changes[i].acquire
+        );
+
+     VG_(free)(changes);
+   }
+#  else
+#    error Unknown OS
+#  endif
 
    //--------------------------------------------------------------
    // Tell aspacem of ownership change of the asm helpers, so that
@@ -2446,14 +1651,14 @@ Int main(Int argc, HChar **argv, HChar **envp)
    VG_(debugLog)(1, "main", "Tell tool about initial permissions\n");
    { Addr*     seg_starts;
      Int       n_seg_starts;
-     NSegment* seg;
 
      seg_starts = get_seg_starts( &n_seg_starts );
-     vg_assert(seg_starts && n_seg_starts > 0);
+     vg_assert(seg_starts && n_seg_starts >= 0);
 
      /* show interesting ones to the tool */
      for (i = 0; i < n_seg_starts; i++) {
-        seg = VG_(am_find_nsegment)( seg_starts[i] );
+        NSegment const*const seg 
+           = VG_(am_find_nsegment)( seg_starts[i] );
         vg_assert(seg);
         if (seg->kind == SkFileC || seg->kind == SkAnonC) {
            VG_(debugLog)(2, "main", 
@@ -2470,22 +1675,31 @@ Int main(Int argc, HChar **argv, HChar **envp)
      VG_(free)( seg_starts );
 
      /* Also do the initial stack permissions. */
-     seg = VG_(am_find_nsegment)( initial_client_SP );
-     vg_assert(seg);
-     vg_assert(seg->kind == SkAnonC);
-     vg_assert(initial_client_SP >= seg->start);
-     vg_assert(initial_client_SP <= seg->end);
-
-     /* Stuff below the initial SP is unaddressable.  Take into
-       account any ABI-mandated space below the stack pointer that is
-       required (VG_STACK_REDZONE_SZB).  setup_client_stack() will
-       have allocated an extra page if a red zone is required, to be on 
-        the safe side. */
-     vg_assert(initial_client_SP - VG_STACK_REDZONE_SZB >= seg->start);
-     VG_TRACK( die_mem_stack, seg->start, initial_client_SP 
-                                          - VG_STACK_REDZONE_SZB - seg->start );
-     VG_(debugLog)(2, "main", "mark stack inaccessible %010lx-%010lx\n",
-                      seg->start, initial_client_SP-1 - VG_STACK_REDZONE_SZB);
+     { NSegment const*const seg 
+          = VG_(am_find_nsegment)( ciii.initial_client_SP );
+       vg_assert(seg);
+       vg_assert(seg->kind == SkAnonC);
+       vg_assert(ciii.initial_client_SP >= seg->start);
+       vg_assert(ciii.initial_client_SP <= seg->end);
+#      if defined(VGO_aix5)
+       VG_(clstk_base) = seg->start;
+       VG_(clstk_end) = seg->end;
+#      endif
+
+       /* Stuff below the initial SP is unaddressable.  Take into
+         account any ABI-mandated space below the stack pointer that
+         is required (VG_STACK_REDZONE_SZB).  setup_client_stack()
+         will have allocated an extra page if a red zone is required,
+         to be on the safe side. */
+       vg_assert(ciii.initial_client_SP - VG_STACK_REDZONE_SZB >= seg->start);
+       VG_TRACK( die_mem_stack, 
+                 seg->start, 
+                 ciii.initial_client_SP - VG_STACK_REDZONE_SZB 
+                                        - seg->start );
+       VG_(debugLog)(2, "main", "mark stack inaccessible %010lx-%010lx\n",
+                        seg->start, 
+                        ciii.initial_client_SP-1 - VG_STACK_REDZONE_SZB);
+     }
 
      /* Also the assembly helpers. */
      VG_TRACK( new_mem_startup,
@@ -2503,26 +1717,24 @@ Int main(Int argc, HChar **argv, HChar **envp)
    //   p: setup_client_stack
    //--------------------------------------------------------------
    VG_(debugLog)(1, "main", "Initialise scheduler\n");
-   { NSegment* seg = VG_(am_find_nsegment)( initial_client_SP );
+   { NSegment const*const seg 
+        = VG_(am_find_nsegment)( ciii.initial_client_SP );
      vg_assert(seg);
      vg_assert(seg->kind == SkAnonC);
-     vg_assert(initial_client_SP >= seg->start);
-     vg_assert(initial_client_SP <= seg->end);
+     vg_assert(ciii.initial_client_SP >= seg->start);
+     vg_assert(ciii.initial_client_SP <= seg->end);
      VG_(scheduler_init)( seg->end, clstack_max_size );
    }
 
    //--------------------------------------------------------------
-   // Initialise the pthread model
+   // Set up state for the root thread
    //   p: ?
-   //      load_client()          [for 'client_eip']
-   //      setup_client_stack()   [for 'sp_at_startup']
-   //      setup_scheduler()      [for the rest of state 1 stuff]
+   //      setup_scheduler()      [for sched-specific thread 1 stuff]
+   //      VG_(setup_client_initial_image)
+   //                             [for 'ciii' initial layout info]
    //--------------------------------------------------------------
-   VG_(debugLog)(1, "main", "Initialise thread 1's state\n");
-   init_thread1state( initial_client_IP, 
-                      initial_client_SP, 
-                      initial_client_TOC,
-                      &VG_(threads)[1].arch);
+   VG_(debugLog)(1, "main", "Finalise thread 1's state\n");
+   VG_(finalise_thread1state)( &VG_(threads)[1].arch, ciii );
 
    //--------------------------------------------------------------
    // Initialise the pthread model
@@ -2599,30 +1811,75 @@ Int main(Int argc, HChar **argv, HChar **envp)
    vg_assert(0);
 }
 
-/* Do everything which needs doing when the last thread exits. */
+/* Do everything which needs doing when the last thread exits or when
+   a thread exits requesting a complete process exit (exit on AIX).
+
+   We enter here holding The Lock.  For the case VgSrc_ExitProcess we
+   must never release it, because to do so would allow other threads
+   to continue after the system is ostensibly shut down.  So we must
+   go to our grave, so to speak, holding the lock. 
+
+   In fact, there is never any point in releasing the lock at this
+   point - we have it, we're shutting down the entire system, and
+   for the case VgSrc_ExitProcess doing so positively causes trouble.
+   So don't. 
+
+   The final_tidyup call makes a bit of a nonsense of the ExitProcess
+   case, since it will run the libc_freeres function, thus allowing
+   other lurking threads to run again.  Hmm. */
 
 static 
 void shutdown_actions_NORETURN( ThreadId tid, 
                                 VgSchedReturnCode tids_schedretcode )
 {
    VG_(debugLog)(1, "main", "entering VG_(shutdown_actions_NORETURN)\n");
+   VG_(am_show_nsegments)(1,"Memory layout at client shutdown");
 
-   vg_assert( VG_(count_living_threads)() == 1 );
    vg_assert(VG_(is_running_thread)(tid));
 
-   // Wait for all other threads to exit.
-   VG_(reap_threads)(tid);
+   // XXXXXXXXX REINSTATE
+   //vg_assert(tids_schedretcode == VgSrc_ExitThread
+   //          || tids_schedretcode == VgSrc_ExitProcess
+   //          || tids_schedretcode == VgSrc_FatalSig );
+
+   if (1 /*tids_schedretcode == VgSrc_ExitThread*/) {
+
+      // We are the last surviving thread.  Right?
+      vg_assert( VG_(count_living_threads)() == 1 );
+
+      // Wait for all other threads to exit.
+      // jrs: Huh?  but they surely are already gone
+      VG_(reap_threads)(tid);
+
+      VG_(clo_model_pthreads) = False;
+
+      // Clean the client up before the final report
+      // this causes the libc_freeres function to run
+      final_tidyup(tid);
+
+      /* be paranoid */
+      vg_assert(VG_(is_running_thread)(tid));
+      vg_assert(VG_(count_living_threads)() == 1);
 
-   VG_(clo_model_pthreads) = False;
+   } else {
+
+      // We may not be the last surviving thread.  However, we
+      // want to shut down the entire process.  We hold the lock
+      // and we need to keep hold of it all the way out, in order
+      // that none of the other threads ever run again.
+      vg_assert( VG_(count_living_threads)() >= 1 );
 
-   // Clean the client up before the final report
-   final_tidyup(tid);
+      VG_(clo_model_pthreads) = False;
 
-   // OK, done
-   VG_(exit_thread)(tid);
+      // Clean the client up before the final report
+      // this causes the libc_freeres function to run
+      // perhaps this is unsafe, as per comment above
+      final_tidyup(tid);
 
-   /* should be no threads left */
-   vg_assert(VG_(count_living_threads)() == 0);
+      /* be paranoid */
+      vg_assert(VG_(is_running_thread)(tid));
+      vg_assert(VG_(count_living_threads)() >= 1);
+   }
 
    VG_(threads)[tid].status = VgTs_Empty;
    //--------------------------------------------------------------
@@ -2685,10 +1942,9 @@ void shutdown_actions_NORETURN( ThreadId tid,
    VG_(debugLog)(1, "core_os", 
                     "VG_(terminate_NORETURN)(tid=%lld)\n", (ULong)tid);
 
-   vg_assert(VG_(count_living_threads)() == 0);
-
    switch (tids_schedretcode) {
-   case VgSrc_ExitSyscall: /* the normal way out */
+   case /*VgSrc_ExitThread*/ VgSrc_ExitSyscall:  /* the normal way out (Linux) */
+   //   case VgSrc_ExitProcess: /* the normal way out (AIX) */
       /* Change the application return code to user's return code,
          if an error was found */
       if (VG_(clo_error_exitcode) > 0 
@@ -2700,7 +1956,7 @@ void shutdown_actions_NORETURN( ThreadId tid,
          VG_(exit)( VG_(threads)[tid].os_state.exitcode );
       }
       /* NOT ALIVE HERE! */
-      VG_(core_panic)("entered the afterlife in main() -- ExitSyscall");
+      VG_(core_panic)("entered the afterlife in main() -- ExitT/P");
       break; /* what the hell :) */
 
    case VgSrc_FatalSig:
@@ -2734,6 +1990,9 @@ static void final_tidyup(ThreadId tid)
         !VG_(clo_run_libc_freeres) ||
         0 == __libc_freeres_wrapper )
       return;                  /* can't/won't do it */
+#  if defined(VGO_aix5)
+   return; /* inapplicable on non-Linux platforms */
+#  endif
 
 #  if defined(VGP_ppc64_linux)
    r2 = VG_(get_tocptr)( __libc_freeres_wrapper );
@@ -2787,9 +2046,11 @@ static void final_tidyup(ThreadId tid)
 
 
 /*====================================================================*/
-/*=== Getting to main() alive                                      ===*/
+/*=== Getting to main() alive: LINUX (for AIX5 see below)          ===*/
 /*====================================================================*/
 
+#if defined(VGO_linux)
+
 /* If linking of the final executables is done with glibc present,
    then Valgrind starts at main() above as usual, and all of the
    following code is irrelevant.
@@ -2811,12 +2072,12 @@ static void final_tidyup(ThreadId tid)
 
 /* ---------------- Requirement 1 ---------------- */
 
-void* memcpy(void *dest, const void *src, size_t n);
-void* memcpy(void *dest, const void *src, size_t n) {
+void* memcpy(void *dest, const void *src, SizeT n);
+void* memcpy(void *dest, const void *src, SizeT n) {
    return VG_(memcpy)(dest,src,n);
 }
-void* memset(void *s, int c, size_t n);
-void* memset(void *s, int c, size_t n) {
+void* memset(void *s, int c, SizeT n);
+void* memset(void *s, int c, SizeT n) {
   return VG_(memset)(s,c,n);
 }
 
@@ -2825,7 +2086,7 @@ void* memset(void *s, int c, size_t n) {
 /* Glibc's sysdeps/i386/elf/start.S has the following gem of a
    comment, which explains how the stack looks right at process start
    (when _start is jumped to).  Hence _start passes %esp to
-   _start_in_C, which extracts argc/argv/envp and starts up
+   _start_in_C_linux, which extracts argc/argv/envp and starts up
    correctly. */
 
 /* This is the canonical entry point, usually the first thing in the text
@@ -2848,7 +2109,7 @@ void* memset(void *s, int c, size_t n) {
 */
 
 /* The kernel hands control to _start, which extracts the initial
-   stack pointer and calls onwards to _start_in_C.  This also switches
+   stack pointer and calls onwards to _start_in_C_linux.  This also switches
    the new stack.  */
 #if defined(VGP_x86_linux)
 asm("\n"
@@ -2864,9 +2125,9 @@ asm("\n"
     "\tandl  $~15, %eax\n"
     /* install it, and collect the original one */
     "\txchgl %eax, %esp\n"
-    /* call _start_in_C, passing it the startup %esp */
+    /* call _start_in_C_linux, passing it the startup %esp */
     "\tpushl %eax\n"
-    "\tcall  _start_in_C\n"
+    "\tcall  _start_in_C_linux\n"
     "\thlt\n"
     ".previous\n"
 );
@@ -2883,8 +2144,8 @@ asm("\n"
     "\tandq  $~15, %rdi\n"
     /* install it, and collect the original one */
     "\txchgq %rdi, %rsp\n"
-    /* call _start_in_C, passing it the startup %rsp */
-    "\tcall  _start_in_C\n"
+    /* call _start_in_C_linux, passing it the startup %rsp */
+    "\tcall  _start_in_C_linux\n"
     "\thlt\n"
     ".previous\n"
 );
@@ -2907,10 +2168,10 @@ asm("\n"
     /* now r16 = &vgPlain_interim_stack + VG_STACK_GUARD_SZB +
        VG_STACK_ACTIVE_SZB rounded down to the nearest 16-byte
        boundary.  And r1 is the original SP.  Set the SP to r16 and
-       call _start_in_C, passing it the initial SP. */
+       call _start_in_C_linux, passing it the initial SP. */
     "\tmr 3,1\n"
     "\tmr 1,16\n"
-    "\tbl _start_in_C\n"
+    "\tbl _start_in_C_linux\n"
     "\ttrap\n"
     ".previous\n"
 );
@@ -2946,10 +2207,10 @@ asm("\n"
     /* now r16 = &vgPlain_interim_stack + VG_STACK_GUARD_SZB +
        VG_STACK_ACTIVE_SZB rounded down to the nearest 16-byte
        boundary.  And r1 is the original SP.  Set the SP to r16 and
-       call _start_in_C, passing it the initial SP. */
+       call _start_in_C_linux, passing it the initial SP. */
     "\tmr 3,1\n"
     "\tmr 1,16\n"
-    "\tbl ._start_in_C\n"
+    "\tbl ._start_in_C_linux\n"
     "\tnop\n"
     "\ttrap\n"
 );
@@ -2959,18 +2220,121 @@ asm("\n"
 
 /* Avoid compiler warnings: this fn _is_ used, but labelling it
    'static' causes gcc to complain it isn't. */
-void _start_in_C ( UWord* pArgc );
-void _start_in_C ( UWord* pArgc )
+void _start_in_C_linux ( UWord* pArgc );
+void _start_in_C_linux ( UWord* pArgc )
 {
    Int     r;
    Word    argc = pArgc[0];
    HChar** argv = (HChar**)&pArgc[1];
    HChar** envp = (HChar**)&pArgc[1+argc+1];
    sp_at_startup = (Addr)pArgc;
-   r = main( (Int)argc, argv, envp );
+   r = valgrind_main( (Int)argc, argv, envp, 
+                      NULL/*aix5-specific stuff*/,
+                      NULL/*aix5-specific stuff*/,
+                      0/*aix5-specific stuff*/ );
+   /* NOTREACHED */
    VG_(exit)(r);
 }
 
+#endif /* defined(VGO_linux) */
+
+
+/*====================================================================*/
+/*=== Getting to main() alive: AIX5                                ===*/
+/*====================================================================*/
+
+#if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
+
+/* This is somewhat simpler than the Linux case.  _start_valgrind
+   receives control from the magic piece of code created in this
+   process' address space by the launcher, via use of ptrace().  At
+   the point of entry:
+
+   - the initial client process image is in memory and ready to roll,
+     except that we've partially trashed its integer register state
+     in order to get this far.   So ..
+
+   - intregs37 holds the client's initial integer register state, so
+     we can restore it before starting the client on the VCPU.
+
+   - we're on the client's stack.  This is not good; therefore the
+     first order of business is to switch to our temporary stack.  
+
+   - the client's initial argc/v/envp is in r3/r4/r5 (32 bit mode) or
+     r14/r15/r16 (64 bit mode).  They are pulled out of the stashed
+     integer register state and passed to our main().
+
+   The launcher will have played some games with argv.  If the launcher
+   ($prefix/bin/valgrind) was started like this
+
+      valgrind [args-for-V] app [args-for-app]
+
+   then the launcher will have started the client as
+
+      app [args-for-V] app [args-for-app]
+
+   m_initimg will have to mess with the client's initial r4/r5
+   (32-bit) or r15/r16 (64-bit) so that it believes it was execd as
+   "app [args-for-app]".  Well, that's no big deal.
+*/
+
+#include "launcher-aix5-bootblock.h"
+
+void _start_in_C_aix5 ( AIX5Bootblock* bootblock );
+void _start_in_C_aix5 ( AIX5Bootblock* bootblock )
+{
+   Int     r;
+   ULong* intregs37;
+   UWord   argc, argv, envp;
+   __NR_getpid = bootblock->__NR_getpid;
+   __NR_write  = bootblock->__NR_write;
+   __NR_exit   = bootblock->__NR_exit;
+   __NR_open   = bootblock->__NR_open;
+   __NR_read   = bootblock->__NR_read;
+   __NR_close  = bootblock->__NR_close;
+   intregs37 = &bootblock->iregs_pc_cr_lr_ctr_xer[0];
+#  if defined(VGP_ppc32_aix5)
+   argc = (UWord)intregs37[3];  /* client's r3 == argc */
+   argv = (UWord)intregs37[4];
+   envp = (UWord)intregs37[5];
+#  else /* defined(VGP_ppc64_aix5) */
+   argc = (UWord)intregs37[14];  /* client's r14 == argc */
+   argv = (UWord)intregs37[15];
+   envp = (UWord)intregs37[16];
+#  endif
+   sp_at_startup = (Addr)0xDeadBeefDeadBeefULL; /* Not important on AIX. */
+   r = valgrind_main( (Int)argc, (HChar**)argv, (HChar**)envp, 
+                      intregs37, 
+                      (void*)bootblock,
+                      bootblock->adler32 );
+   /* NOTREACHED */
+   VG_(exit)(r);
+}
+
+/* THE ENTRY POINT */
+void _start_valgrind ( AIX5Bootblock* bootblock );
+void _start_valgrind ( AIX5Bootblock* bootblock )
+{
+   /* Switch immediately to our temporary stack, and continue.  This
+      is pretty dodgy in that it assumes that gcc does not place on
+      the stack, anything needed to form the _start_in_C_aix5 call,
+      since it will be on the old stack. */
+   register UWord new_r1;
+   new_r1  = (UWord)&VG_(interim_stack);
+   new_r1 += VG_STACK_GUARD_SZB;  /* step over lower guard page */
+   new_r1 += VG_STACK_ACTIVE_SZB; /* step to top of active area */
+   new_r1 -= 512; /* paranoia */
+   __asm__ __volatile__("mr 1,%0" :/*wr*/ 
+                                  :/*rd*/ "b"(new_r1) 
+                                  :/*trash*/"r1","memory");
+   _start_in_C_aix5(bootblock);
+   /*NOTREACHED*/
+   VG_(exit)(0);
+}
+
+#endif /* defined(VGP_ppc{32,64}_aix5) */
+
+
 /*--------------------------------------------------------------------*/
 /*--- end                                                          ---*/
 /*--------------------------------------------------------------------*/
diff --git a/coregrind/pub_core_initimg.h b/coregrind/pub_core_initimg.h
new file mode 100644 (file)
index 0000000..933cead
--- /dev/null
@@ -0,0 +1,139 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Create initial process image on for the client               ---*/
+/*---                                           pub_core_initimg.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2006-2006 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.
+*/
+
+#ifndef __PUB_CORE_INITIMG_H
+#define __PUB_CORE_INITIMG_H
+
+//--------------------------------------------------------------------
+// PURPOSE: Map the client executable into memory, then set up its
+// stack, environment and data section, ready for execution.  Quite a
+// lot of work on Linux (ELF) but nearly a no-op on AIX (XCOFF) since
+// the AIX kernel does most of the work for us.
+//--------------------------------------------------------------------
+
+#if defined(VGO_linux)
+typedef
+   struct {
+      Addr  initial_client_SP;
+      Addr  initial_client_IP;
+      Addr  initial_client_TOC;
+      UInt* client_auxv;
+   }
+   ClientInitImgInfo;
+
+#elif defined(VGO_aix5)
+typedef
+   struct {
+      /* NOTE: VG_(ppc32/64_aix5_do_preloads_then_start_client) has
+         these offsets hardwired in.  Do not change them without
+         changing it too. */
+      /* system call numbers */
+      /*   0 */ UInt nr_load; /* is __NR___loadx for 32-bit, 
+                                    __NR_kload for 64 */
+      /*   4 */ UInt nr_kwrite;
+      /*   8 */ UInt nr__exit;
+      /* offset/length of error message, if the preloads fail */
+      /*  12 */ UInt off_errmsg;
+      /*  16 */ UInt len_errmsg;
+      /* offsets from start of this struct to the the preload file
+         names */
+      /*  20 */ UInt off_preloadcorename;
+      /*  24 */ UInt off_preloadtoolname;
+      /*  28 */ UInt off_ld_preloadname;
+      /* Once the preloading is done, we'll need to restore the guest
+         state to what it needs to be at client startup.  Here's the
+         relevant info.  Are ULongs; for 32-bit the data is at the
+         lsb (high addressed) end. */
+      /*  32 */ ULong client_start;
+      /*  40 */ ULong r2;
+      /*  48 */ ULong r3;
+      /*  56 */ ULong r4;
+      /*  64 */ ULong r5;
+      /*  72 */ ULong r6;
+      /*  80 */ ULong r7;
+      /*  88 */ ULong r8;
+      /*  96 */ ULong r9;
+      /* 104 */ ULong r10;
+      /* If the loading fails, we'll want to call a diagnostic
+         function in C to figure out what happened.  Here's it's
+         function descriptor.  Note, this runs on the simd cpu
+         (another nasty AIX kludge) */
+      /* 112 */ void* p_diagnose_load_failure;
+   }
+   AIX5PreloadPage;
+
+typedef
+   struct {
+      /* Pointer to the preload page.  This is set up by
+         VG_(setup_client_initial_image). */
+      AIX5PreloadPage* preloadpage;
+      /* Initial values for guest int registers (GPR0 .. GPR31, PC,
+         CR, LR, CTR, XER).  Passed to us from the launcher. */
+      ULong* intregs37;
+      /* Initial value for SP (which is merely a copy of r1's value,
+         intregs37[1]). */
+      Addr initial_client_SP;
+      /* Address of the page compressed by the launcher. */
+      Addr compressed_page;
+      /* Adler32 checksum of uncompressed data of said page. */
+      UInt adler32_exp;
+   }
+   ClientInitImgInfo;
+
+#else
+#  error Unknown OS
+#endif
+
+/* Load the client, create the initial image (stack, etc), and
+   return a bundle of info in a ClientInitImgInfo. */
+extern ClientInitImgInfo
+          VG_(setup_client_initial_image)(
+             /*IN*/ HChar** argv,
+             /*IN*/ HChar** envp,
+             /*IN*/ HChar*  toolname,
+             /*IN*/ Addr    clstack_top,
+             /*IN*/ SizeT   clstack_max_size
+          );
+
+/* Make final adjustments to the initial image.  Also, initialise the
+   VEX guest state for thread 1 (the root thread) and copy in
+   essential starting values.  Is handed the ClientInitImgInfo created
+   by VG_(setup_client_initial_image).  Upon return, the client's
+   memory and register state should be ready to start the JIT. */
+extern 
+void VG_(finalise_thread1state)( /*MOD*/ThreadArchState* arch,
+                                 ClientInitImgInfo ciii );
+
+#endif   // __PUB_CORE_INITIMG_H
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                          ---*/
+/*--------------------------------------------------------------------*/