From: Julian Seward Date: Sun, 15 Oct 2006 01:26:40 +0000 (+0000) Subject: Move code that creates the initial Linux memory image (stack, env, X-Git-Tag: svn/VALGRIND_3_3_0~641 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8d33663fc8fee8247fd200385914f885b8caaed5;p=thirdparty%2Fvalgrind.git Move code that creates the initial Linux memory image (stack, env, etc) from m_main into m_initimg. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6234 --- diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 35e610006e..812d7b098a 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -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 index 0000000000..ddef6c1a62 --- /dev/null +++ b/coregrind/m_initimg/initimg-linux.c @@ -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 +/* --- !!! --- 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_.so + paths. We might not need the space for vgpreload_.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_.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 ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_main.c b/coregrind/m_main.c index 207c4edf0b..ddef8db48f 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -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" @@ -53,11 +55,19 @@ #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//sysent files */ +#if defined(VGO_aix5) + /* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ +# include /* 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_.so - paths. We might not need the space for vgpreload_.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_.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= (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= (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//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//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//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 index 0000000000..933ceade79 --- /dev/null +++ b/coregrind/pub_core_initimg.h @@ -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 ---*/ +/*--------------------------------------------------------------------*/