/*--------------------------------------------------------------------*/ /*--- Startup: create initial process image on Darwin ---*/ /*--- initimg-darwin.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2013 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. */ #if defined(VGO_darwin) #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_tooliface.h" /* VG_TRACK */ #include "pub_core_libcsetjmp.h" // to keep _threadstate.h happy #include "pub_core_threadstate.h" /* ThreadArchState */ #include "priv_initimg_pathscan.h" #include "pub_core_initimg.h" /* self */ /*====================================================================*/ /*=== Loading the client ===*/ /*====================================================================*/ /* Load the client whose name is VG_(argv_the_exename). */ static void load_client ( /*OUT*/ExeInfo* info, /*OUT*/Addr* client_ip) { const HChar* exe_name; Int ret; SysRes res; vg_assert( VG_(args_the_exename) != NULL); exe_name = ML_(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); // 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 (!sr_isError(res)) VG_(cl_exec_fd) = sr_Res(res); /* Copy necessary bits of 'info' that were filled in */ *client_ip = info->init_ip; } /*====================================================================*/ /*=== Setting up the client's environment ===*/ /*====================================================================*/ /* Prepare the client's environment. This is basically a copy of our environment, except: DYLD_INSERT_LIBRARIES=$VALGRIND_LIB/vgpreload_core-PLATFORM.so: ($VALGRIND_LIB/vgpreload_TOOL-PLATFORM.so:)? DYLD_INSERT_LIBRARIES If this is missing, then it is added. Also, remove any binding for VALGRIND_LAUNCHER=. The client should not be able to see this. Also, add DYLD_SHARED_REGION=avoid, because V doesn't know how to process the dyld shared cache file. Also, change VYLD_* (mangled by launcher) back to DYLD_*. 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) { const HChar* preload_core = "vgpreload_core"; const HChar* ld_preload = "DYLD_INSERT_LIBRARIES="; const HChar* dyld_cache = "DYLD_SHARED_REGION="; const HChar* dyld_cache_value= "avoid"; const HChar* v_launcher = VALGRIND_LAUNCHER "="; Int ld_preload_len = VG_(strlen)( ld_preload ); Int dyld_cache_len = VG_(strlen)( dyld_cache ); Int v_launcher_len = VG_(strlen)( v_launcher ); Bool ld_preload_done = False; Bool dyld_cache_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-darwin.sce.1", preload_string_len); vg_assert(preload_string); /* Determine if there's a vgpreload_<tool>_<platform>.so file, and setup preload_string. */ preload_tool_path = VG_(malloc)("initimg-darwin.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-darwin.sce.3", sizeof(HChar *) * (envc+2+1)); /* 2 new entries + 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-darwin.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; } if (VG_(memcmp)(*cpp, dyld_cache, dyld_cache_len) == 0) { Int len = dyld_cache_len + VG_(strlen)(dyld_cache_value) + 1; HChar *cp = VG_(malloc)("initimg-darwin.sce.4.2", len); vg_assert(cp); VG_(snprintf)(cp, len, "%s%s", dyld_cache, dyld_cache_value); *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-darwin.sce.5", len); vg_assert(cp); VG_(snprintf)(cp, len, "%s%s", ld_preload, preload_string); ret[envc++] = cp; } if (!dyld_cache_done) { Int len = dyld_cache_len + VG_(strlen)(dyld_cache_value) + 1; HChar *cp = VG_(malloc) ("initimg-darwin.sce.5.2", len); vg_assert(cp); VG_(snprintf)(cp, len, "%s%s", dyld_cache, dyld_cache_value); 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--; } /* Change VYLD_ to DYLD */ for (i = 0; i < envc; i++) { if (0 == VG_(strncmp)(ret[i], "VYLD_", 5)) { ret[i][0] = 'D'; } } VG_(free)(preload_string); ret[envc] = NULL; return ret; } /*====================================================================*/ /*=== Setting up the client's stack ===*/ /*====================================================================*/ /* Add a string onto the string table, and return its address */ static HChar *copy_str(HChar **tab, const HChar *str) { HChar *cp = *tab; HChar *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 on Darwin is: higher address +-----------------+ <- clstack_end | | : string table : | | +-----------------+ | NULL | +-----------------+ | executable_path | (first arg to execve()) +-----------------+ | NULL | - - | envp | +-----------------+ | NULL | - - | argv | +-----------------+ | argc | +-----------------+ | mach_header * | (dynamic only) 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. ---------------------------------------------------------------- */ static Addr setup_client_stack( void* init_sp, HChar** orig_envp, const ExeInfo* info, Addr clstack_end, SizeT clstack_max_size ) { HChar **cpp; HChar *strtab; /* string table */ HChar *stringbase; Addr *ptr; 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) ); /* ==================== compute sizes ==================== */ /* first of all, work out how big the client stack will be */ stringsize = 0; auxsize = 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; } /* Darwin executable_path + NULL */ auxsize += 2 * sizeof(Word); if (info->executable_path) { stringsize += 1 + VG_(strlen)(info->executable_path); } /* Darwin mach_header */ if (info->dynamic) auxsize += sizeof(Word); /* OK, now we know how big the client stack is */ stacksize = sizeof(Word) + /* argc */ (have_exename ? sizeof(HChar **) : 0) + /* argc[0] == exename */ sizeof(HChar **)*argc + /* argv */ sizeof(HChar **) + /* terminal NULL */ sizeof(HChar **)*envc + /* envp */ sizeof(HChar **) + /* 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, 32); /* make stack 32 byte aligned */ /* base of the string table (aligned) */ stringbase = strtab = (HChar *)clstack_end - VG_ROUNDUP(stringsize, sizeof(int)); /* The max stack size */ clstack_max_size = VG_PGROUNDUP(clstack_max_size); /* Darwin stack is chosen by the ume loader */ clstack_start = clstack_end - clstack_max_size; /* Record stack extent -- needed for stack-change code. */ /* GrP fixme really? */ 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 ==================== */ /* Stack was allocated by the ume loader. */ /* ==================== create client stack ==================== */ ptr = (Addr*)client_SP; /* --- mach_header --- */ if (info->dynamic) *ptr++ = info->text; /* --- client argc --- */ *ptr++ = (Addr)(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) = (HChar **)ptr; for (cpp = orig_envp; cpp && *cpp; ptr++, cpp++) *ptr = (Addr)copy_str(&strtab, *cpp); *ptr++ = 0; /* --- executable_path + NULL --- */ if (info->executable_path) *ptr++ = (Addr)copy_str(&strtab, info->executable_path); else *ptr++ = 0; *ptr++ = 0; 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; } /*====================================================================*/ /*=== Record system memory regions ===*/ /*====================================================================*/ static void record_system_memory(void) { /* Tell aspacem where the client's kernel commpage is */ #if defined(VGA_amd64) /* commpage 0x7fff:ffe00000+ - not in vm_region */ // GrP fixme check again VG_(am_notify_client_mmap)(0x7fffffe00000, 0x7ffffffff000-0x7fffffe00000, VKI_PROT_READ|VKI_PROT_EXEC, 0, -1, 0); #elif defined(VGA_x86) /* commpage 0xfffec000+ - not in vm_region */ // GrP fixme check again VG_(am_notify_client_mmap)(0xfffec000, 0xfffff000-0xfffec000, VKI_PROT_READ|VKI_PROT_EXEC, 0, -1, 0); #else # error unknown architecture #endif } /*====================================================================*/ /*=== TOP-LEVEL: VG_(ii_create_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); //-------------------------------------------------------------- // 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'] //-------------------------------------------------------------- iicii.clstack_top = info.stack_end - 1; iifii.clstack_max_size = info.stack_end - info.stack_start; iifii.initial_client_SP = setup_client_stack( iicii.argv - 1, env, &info, iicii.clstack_top, iifii.clstack_max_size ); VG_(free)(env); VG_(debugLog)(2, "initimg", "Client info: " "initial_IP=%p initial_SP=%p stack=%p..%p\n", (void*)(iifii.initial_client_IP), (void*)(iifii.initial_client_SP), (void*)(info.stack_start), (void*)(info.stack_end)); // Tell aspacem about commpage, etc record_system_memory(); return iifii; } /*====================================================================*/ /*=== TOP-LEVEL: VG_(ii_finalise_image) ===*/ /*====================================================================*/ /* 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; /* GrP fixme doesn't handle all registers from LC_THREAD or LC_UNIXTHREAD */ # if defined(VGP_x86_darwin) 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; # elif defined(VGP_amd64_darwin) 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; # 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)); } #endif // defined(VGO_darwin) /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/