--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- 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 ---*/
+/*--------------------------------------------------------------------*/
#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"
#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
/*====================================================================*/
}
-/*====================================================================*/
-/*=== 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 ===*/
/*====================================================================*/
" --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
/* 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++) {
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)++;
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 */ }
else {
VG_(message)(Vg_UserMsg, "--trace-flags argument can only "
"contain 0s and 1s");
- VG_(bad_option)(arg);
+ VG_(err_bad_option)(arg);
}
}
}
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 */ }
else {
VG_(message)(Vg_UserMsg, "--profile-flags argument can only "
"contain 0s and 1s");
- VG_(bad_option)(arg);
+ VG_(err_bad_option)(arg);
}
}
}
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]) {
"--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 &&
"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
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*/
}
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*/
}
"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*/
}
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*/
}
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) {
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;
/* 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 ===*/
/*====================================================================*/
}
-
-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));
//============================================================
//
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
// 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
}
//==============================================================
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);
/* 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;
}
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;
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;
// 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
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
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",
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,
// 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
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;
//--------------------------------------------------------------
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
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:
!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 );
/*====================================================================*/
-/*=== 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.
/* ---------------- 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);
}
/* 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
*/
/* 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"
"\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"
);
"\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"
);
/* 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"
);
/* 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"
);
/* 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 ---*/
/*--------------------------------------------------------------------*/