diff options
Diffstat (limited to 'coregrind/m_initimg/.svn/text-base/initimg-linux.c.svn-base')
-rw-r--r-- | coregrind/m_initimg/.svn/text-base/initimg-linux.c.svn-base | 1074 |
1 files changed, 1074 insertions, 0 deletions
diff --git a/coregrind/m_initimg/.svn/text-base/initimg-linux.c.svn-base b/coregrind/m_initimg/.svn/text-base/initimg-linux.c.svn-base new file mode 100644 index 0000000..e80dc22 --- /dev/null +++ b/coregrind/m_initimg/.svn/text-base/initimg-linux.c.svn-base @@ -0,0 +1,1074 @@ + +/*--------------------------------------------------------------------*/ +/*--- Startup: create initial process image on Linux ---*/ +/*--- initimg-linux.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2009 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_xarray.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_syscall.h" +#include "pub_core_tooliface.h" /* VG_TRACK */ +#include "pub_core_threadstate.h" /* ThreadArchState */ +#include "pub_core_initimg.h" /* self */ + +/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ +#define _GNU_SOURCE +#define _FILE_OFFSET_BITS 64 +/* This is for ELF types etc, and also the AT_ constants. */ +#include <elf.h> +/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ + + +/*====================================================================*/ +/*=== Find executable ===*/ +/*====================================================================*/ + +/* Scan a colon-separated list, and call a function on each element. + The string must be mutable, because we insert a temporary '\0', but + the string will end up unmodified. (*func) should return True if it + doesn't need to see any more. + + This routine will return True if (*func) returns True and False if + it reaches the end of the list without that happening. +*/ +static Bool scan_colsep(char *colsep, Bool (*func)(const char *)) +{ + char *cp, *entry; + int end; + + if (colsep == NULL || + *colsep == '\0') + return False; + + entry = cp = colsep; + + do { + end = (*cp == '\0'); + + if (*cp == ':' || *cp == '\0') { + char save = *cp; + + *cp = '\0'; + if ((*func)(entry)) { + *cp = save; + return True; + } + *cp = save; + entry = cp+1; + } + cp++; + } while(!end); + + return False; +} + +/* Need a static copy because can't use dynamic mem allocation yet */ +static HChar executable_name_in [VKI_PATH_MAX]; +static HChar executable_name_out[VKI_PATH_MAX]; + +static Bool match_executable(const char *entry) +{ + HChar buf[VG_(strlen)(entry) + VG_(strlen)(executable_name_in) + 3]; + + /* empty PATH element means '.' */ + if (*entry == '\0') + entry = "."; + + VG_(snprintf)(buf, sizeof(buf), "%s/%s", entry, executable_name_in); + + // Don't match directories + if (VG_(is_dir)(buf)) + return False; + + // If we match an executable, we choose that immediately. If we find a + // matching non-executable we remember it but keep looking for an + // matching executable later in the path. + if (VG_(access)(buf, True/*r*/, False/*w*/, True/*x*/) == 0) { + VG_(strncpy)( executable_name_out, buf, VKI_PATH_MAX-1 ); + executable_name_out[VKI_PATH_MAX-1] = 0; + return True; // Stop looking + } else if (VG_(access)(buf, True/*r*/, False/*w*/, False/*x*/) == 0 + && VG_STREQ(executable_name_out, "")) + { + VG_(strncpy)( executable_name_out, buf, VKI_PATH_MAX-1 ); + executable_name_out[VKI_PATH_MAX-1] = 0; + return False; // Keep looking + } else { + return False; // Keep looking + } +} + +// Returns NULL if it wasn't found. +static HChar* find_executable ( HChar* exec ) +{ + vg_assert(NULL != exec); + if (VG_(strchr)(exec, '/')) { + // Has a '/' - use the name as is + VG_(strncpy)( executable_name_out, exec, VKI_PATH_MAX-1 ); + } else { + // No '/' - we need to search the path + HChar* path; + VG_(strncpy)( executable_name_in, exec, VKI_PATH_MAX-1 ); + VG_(memset) ( executable_name_out, 0, VKI_PATH_MAX ); + path = VG_(getenv)("PATH"); + scan_colsep(path, match_executable); + } + return VG_STREQ(executable_name_out, "") ? NULL : executable_name_out; +} + + +/*====================================================================*/ +/*=== Loading the client ===*/ +/*====================================================================*/ + +/* Load the client whose name is VG_(argv_the_exename). */ + +static void load_client ( /*OUT*/ExeInfo* info, + /*OUT*/Addr* client_ip, + /*OUT*/Addr* client_toc) +{ + HChar* exe_name; + Int ret; + SysRes res; + + vg_assert( VG_(args_the_exename) != NULL); + exe_name = find_executable( VG_(args_the_exename) ); + + if (!exe_name) { + VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename)); + VG_(exit)(127); // 127 is Posix NOTFOUND + } + + VG_(memset)(info, 0, sizeof(*info)); + ret = VG_(do_exec)(exe_name, info); + if (ret < 0) { + VG_(printf)("valgrind: could not execute '%s'\n", exe_name); + VG_(exit)(1); + } + + // 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.res; + + /* 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/vgpreload_core-PLATFORM.so: + ($VALGRIND_LIB/vgpreload_TOOL-PLATFORM.so:)? + $LD_PRELOAD + + If this is missing, then it is added. + + Also, remove any binding for VALGRIND_LAUNCHER=. The client should + not be able to see this. + + If this needs to handle any more variables it should be hacked + into something table driven. The copy is VG_(malloc)'d space. +*/ +static HChar** setup_client_env ( HChar** origenv, const HChar* toolname) +{ + HChar* preload_core = "vgpreload_core"; + HChar* ld_preload = "LD_PRELOAD="; + HChar* v_launcher = VALGRIND_LAUNCHER "="; + Int ld_preload_len = VG_(strlen)( ld_preload ); + Int v_launcher_len = VG_(strlen)( v_launcher ); + Bool ld_preload_done = False; + Int vglib_len = VG_(strlen)(VG_(libdir)); + + HChar** cpp; + HChar** ret; + HChar* preload_tool_path; + Int envc, i; + + /* Alloc space for the vgpreload_core.so path and vgpreload_<tool>.so + paths. We might not need the space for vgpreload_<tool>.so, but it + doesn't hurt to over-allocate briefly. The 16s are just cautious + slop. */ + Int preload_core_path_len = vglib_len + sizeof(preload_core) + + sizeof(VG_PLATFORM) + 16; + Int preload_tool_path_len = vglib_len + VG_(strlen)(toolname) + + sizeof(VG_PLATFORM) + 16; + Int preload_string_len = preload_core_path_len + preload_tool_path_len; + HChar* preload_string = VG_(malloc)("initimg-linux.sce.1", + preload_string_len); + vg_assert(origenv); + vg_assert(toolname); + vg_assert(preload_string); + + /* Determine if there's a vgpreload_<tool>_<platform>.so file, and setup + preload_string. */ + preload_tool_path = VG_(malloc)("initimg-linux.sce.2", preload_tool_path_len); + vg_assert(preload_tool_path); + VG_(snprintf)(preload_tool_path, preload_tool_path_len, + "%s/vgpreload_%s-%s.so", VG_(libdir), toolname, VG_PLATFORM); + 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), preload_core, VG_PLATFORM, preload_tool_path); + } else { + VG_(snprintf)(preload_string, preload_string_len, "%s/%s-%s.so", + VG_(libdir), preload_core, VG_PLATFORM); + } + 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) ("initimg-linux.sce.3", + 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)("initimg-linux.sce.4", 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) ("initimg-linux.sce.5", 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? + + ---------------------------------------------------------------- */ + +struct auxv +{ + Word a_type; + union { + void *a_ptr; + Word a_val; + } u; +}; + +static +struct auxv *find_auxv(UWord* sp) +{ + sp++; // skip argc (Nb: is word-sized, not int-sized!) + + while (*sp != 0) // skip argv + sp++; + sp++; + + while (*sp != 0) // skip env + sp++; + sp++; + +#if defined(VGA_ppc32) || defined(VGA_ppc64) +# if defined AT_IGNOREPPC + while (*sp == AT_IGNOREPPC) // skip AT_IGNOREPPC entries + sp += 2; +# endif +#endif + + return (struct auxv *)sp; +} + +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 auxv *auxv; + const struct auxv *orig_auxv; + const struct 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)); + vg_assert( VG_(args_for_client) ); + + /* use our own auxv as a prototype */ + orig_auxv = 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_(sizeXA)( VG_(args_for_client) ); i++) { + argc++; + stringsize += VG_(strlen)( * (HChar**) + VG_(indexXA)( VG_(args_for_client), 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)("%#lx 0x%lx %#lx 0x%lx\n", + resvn_start, resvn_size, anon_start, anon_size); + + /* Create a shrinkable reservation followed by an anonymous + segment. Together these constitute a growdown stack. */ + res = VG_(mk_SysRes_Error)(0); + ok = VG_(am_create_reservation)( + resvn_start, + resvn_size -inner_HACK, + SmUpper, + anon_size +inner_HACK + ); + if (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 + ); + } + if ((!ok) || res.isError) { + /* Allocation of the stack failed. We have to stop. */ + VG_(printf)("valgrind: " + "I failed to allocate space for the application's stack.\n"); + VG_(printf)("valgrind: " + "This may be the result of a very large --main-stacksize=\n"); + VG_(printf)("valgrind: setting. Cannot continue. Sorry.\n\n"); + VG_(exit)(0); + } + + vg_assert(ok); + 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_(sizeXA)( VG_(args_for_client) ); i++) { + *ptr++ = (Addr)copy_str( + &strtab, + * (HChar**) VG_(indexXA)( VG_(args_for_client), 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 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 = %#lx\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.res == anon_start); +} + + +/*====================================================================*/ +/*=== TOP-LEVEL: VG_(setup_client_initial_image) ===*/ +/*====================================================================*/ + +/* Create the client's initial memory image. */ +IIFinaliseImageInfo VG_(ii_create_image)( IICreateImageInfo iicii ) +{ + ExeInfo info; + HChar** env = NULL; + + IIFinaliseImageInfo iifii; + VG_(memset)( &iifii, 0, sizeof(iifii) ); + + //-------------------------------------------------------------- + // 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, &iifii.initial_client_IP, &iifii.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(iicii.envp, iicii.toolname); + + //-------------------------------------------------------------- + // Setup client stack, eip, and VG_(client_arg[cv]) + // p: load_client() [for 'info'] + // p: fix_environment() [for 'env'] + //-------------------------------------------------------------- + { + /* When allocating space for the client stack on Linux, take + notice of the --main-stacksize value. This makes it possible + to run programs with very large (primary) stack requirements + simply by specifying --main-stacksize. */ + /* Logic is as follows: + - by default, use the client's current stack rlimit + - if that exceeds 16M, clamp to 16M + - if a larger --main-stacksize value is specified, use that instead + - in all situations, the minimum allowed stack size is 1M + */ + void* init_sp = iicii.argv - 1; + SizeT m1 = 1024 * 1024; + SizeT m16 = 16 * m1; + SizeT szB = (SizeT)VG_(client_rlimit_stack).rlim_cur; + if (szB < m1) szB = m1; + if (szB > m16) szB = m16; + if (VG_(clo_main_stacksize) > 0) szB = VG_(clo_main_stacksize); + if (szB < m1) szB = m1; + szB = VG_PGROUNDUP(szB); + VG_(debugLog)(1, "initimg", + "Setup client stack: size will be %ld\n", szB); + + iifii.clstack_max_size = szB; + + iifii.initial_client_SP + = setup_client_stack( init_sp, env, + &info, &iifii.client_auxv, + iicii.clstack_top, iifii.clstack_max_size ); + + VG_(free)(env); + + VG_(debugLog)(2, "initimg", + "Client info: " + "initial_IP=%p initial_TOC=%p brk_base=%p\n", + (void*)(iifii.initial_client_IP), + (void*)(iifii.initial_client_TOC), + (void*)VG_(brk_base) ); + VG_(debugLog)(2, "initimg", + "Client info: " + "initial_SP=%p max_stack_size=%ld\n", + (void*)(iifii.initial_client_SP), + (SizeT)iifii.clstack_max_size ); + } + + //-------------------------------------------------------------- + // 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 iifii; +} + + +/*====================================================================*/ +/*=== TOP-LEVEL: VG_(finalise_thread1state) ===*/ +/*====================================================================*/ + +/* Just before starting the client, we may need to make final + adjustments to its initial image. Also we need to set up the VEX + guest state for thread 1 (the root thread) and copy in essential + starting values. This is handed the IIFinaliseImageInfo created by + VG_(ii_create_image). +*/ +void VG_(ii_finalise_image)( IIFinaliseImageInfo iifii ) +{ + ThreadArchState* arch = &VG_(threads)[1].arch; + + /* 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) % 16); + + /* Zero out the initial state, and set up the simulated FPU in a + sane way. */ + LibVEX_GuestX86_initialise(&arch->vex); + + /* Zero out the shadow areas. */ + VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestX86State)); + VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestX86State)); + + /* Put essential stuff into the new state. */ + arch->vex.guest_ESP = iifii.initial_client_SP; + arch->vex.guest_EIP = iifii.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) % 16); + + /* Zero out the initial state, and set up the simulated FPU in a + sane way. */ + LibVEX_GuestAMD64_initialise(&arch->vex); + + /* Zero out the shadow areas. */ + VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestAMD64State)); + VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestAMD64State)); + + /* Put essential stuff into the new state. */ + arch->vex.guest_RSP = iifii.initial_client_SP; + arch->vex.guest_RIP = iifii.initial_client_IP; + +# elif defined(VGP_ppc32_linux) + vg_assert(0 == sizeof(VexGuestPPC32State) % 16); + + /* Zero out the initial state, and set up the simulated FPU in a + sane way. */ + LibVEX_GuestPPC32_initialise(&arch->vex); + + /* Zero out the shadow areas. */ + VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestPPC32State)); + VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestPPC32State)); + + /* Put essential stuff into the new state. */ + arch->vex.guest_GPR1 = iifii.initial_client_SP; + arch->vex.guest_CIA = iifii.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 areas. */ + VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestPPC64State)); + VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestPPC64State)); + + /* Put essential stuff into the new state. */ + arch->vex.guest_GPR1 = iifii.initial_client_SP; + arch->vex.guest_GPR2 = iifii.initial_client_TOC; + arch->vex.guest_CIA = iifii.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 ---*/ +/*--------------------------------------------------------------------*/ |