1/* 2 * Copyright (c) 2008-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <sys/cdefs.h> 25 26#include <spawn.h> 27#include <errno.h> 28#include <crt_externs.h> 29#include <mach/mach.h> 30#include <mach-o/loader.h> 31#include <mach-o/dyld.h> 32#include <sys/sysctl.h> 33#include <stdlib.h> 34#include <stdio.h> 35 36#include "libutil.h" 37 38static cpu_type_t current_program_arch(void); 39static cpu_type_t current_kernel_arch(void); 40static int reexec(cpu_type_t cputype, const char *guardenv); 41 42#define kReExecToMatchKernel "REEXEC_TO_MATCH_KERNEL" 43#define kReExecToMatchLP64 "REEXEC_TO_MATCH_LP64NESS" 44 45int reexec_to_match_kernel(void) 46{ 47 cpu_type_t kernarch, progarch; 48 char *alreadyenv; 49 50 alreadyenv = getenv(kReExecToMatchKernel); 51 if (alreadyenv) { 52 /* we've done this at least once, assume 53 another try won't help */ 54 return 0; 55 } 56 57 kernarch = current_kernel_arch(); 58 progarch = current_program_arch(); 59 60 if (kernarch == 0) { 61 /* could not determine kernel arch */ 62 errno = EINVAL; 63 return -1; 64 } 65 66 if (kernarch == progarch) { 67 /* nothing to do here */ 68 return 0; 69 } 70 71 /* Now we need to re-exec */ 72 return reexec(kernarch, kReExecToMatchKernel); 73} 74 75int reexec_to_match_lp64ness(bool isLP64) 76{ 77 cpu_type_t kernarch, progarch, targetarch; 78 char *alreadyenv; 79 80 alreadyenv = getenv(kReExecToMatchLP64); 81 if (alreadyenv) { 82 /* we've done this at least once, assume 83 another try won't help */ 84 return 0; 85 } 86 87 kernarch = current_kernel_arch(); 88 progarch = current_program_arch(); 89 90 if (kernarch == 0) { 91 /* could not determine kernel arch */ 92 errno = EINVAL; 93 return -1; 94 } 95 96 if (isLP64) { 97 targetarch = kernarch | CPU_ARCH_ABI64; 98 } else { 99 targetarch = kernarch & ~CPU_ARCH_ABI64; 100 } 101 102 if (targetarch == progarch) { 103 /* nothing to do here */ 104 return 0; 105 } 106 107 /* Now we need to re-exec */ 108 return reexec(targetarch, kReExecToMatchLP64); 109} 110 111static cpu_type_t current_program_arch(void) 112{ 113 cpu_type_t current_arch = (_NSGetMachExecuteHeader())->cputype; 114 115 return current_arch; 116} 117 118static cpu_type_t current_kernel_arch(void) 119{ 120 struct host_basic_info hi; 121 unsigned int size; 122 kern_return_t kret; 123 cpu_type_t current_arch; 124 int ret, mib[4]; 125 size_t len; 126 struct kinfo_proc kp; 127 128 size = sizeof(hi)/sizeof(int); 129 kret = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hi, &size); 130 if (kret != KERN_SUCCESS) { 131 return 0; 132 } 133 134 current_arch = hi.cpu_type; 135 136 /* Now determine if the kernel is running in 64-bit mode */ 137 mib[0] = CTL_KERN; 138 mib[1] = KERN_PROC; 139 mib[2] = KERN_PROC_PID; 140 mib[3] = 0; /* kernproc, pid 0 */ 141 len = sizeof(kp); 142 ret = sysctl(mib, sizeof(mib)/sizeof(mib[0]), &kp, &len, NULL, 0); 143 if (ret == -1) { 144 return 0; 145 } 146 147 if (kp.kp_proc.p_flag & P_LP64) { 148 current_arch |= CPU_ARCH_ABI64; 149 } 150 151 return current_arch; 152} 153 154static int reexec(cpu_type_t cputype, const char *guardenv) 155{ 156 posix_spawnattr_t attr; 157 int ret, envcount; 158 size_t copied = 0; 159 char **argv, **oldenvp, **newenvp; 160 char execpath[MAXPATHLEN+1]; 161 uint32_t execsize; 162 char guardstr[32]; 163 164 argv = *_NSGetArgv(); 165 oldenvp = *_NSGetEnviron(); 166 for (envcount = 0; oldenvp[envcount]; envcount++); 167 // if there are 4 elements and a NULL, envcount will be 4 168 169 newenvp = calloc(envcount+2, sizeof(newenvp[0])); 170 for (envcount = 0; oldenvp[envcount]; envcount++) { 171 newenvp[envcount] = oldenvp[envcount]; 172 } 173 174 snprintf(guardstr, sizeof(guardstr), "%s=1", guardenv); 175 newenvp[envcount++] = guardstr; 176 newenvp[envcount] = NULL; 177 178 execsize = (uint32_t)sizeof(execpath); 179 ret = _NSGetExecutablePath(execpath, &execsize); 180 if (ret != 0) { 181 return -1; 182 } 183 184 ret = posix_spawnattr_init(&attr); 185 if (ret != 0) { 186 return -1; 187 } 188 ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC); 189 if (ret != 0) { 190 return -1; 191 } 192 ret = posix_spawnattr_setbinpref_np(&attr, 1, &cputype, &copied); 193 if (ret != 0 || copied != 1) { 194 return -1; 195 } 196 197#if 0 198 fprintf(stderr, "reexec: %s (arch=%d)\n", execpath, cputype); 199 for (envcount=0; newenvp[envcount]; envcount++) { 200 fprintf(stderr, "env[%d] = %s\n", envcount, newenvp[envcount]); 201 } 202 for (envcount=0; argv[envcount]; envcount++) { 203 fprintf(stderr, "argv[%d] = %s\n", envcount, argv[envcount]); 204 } 205#endif 206 207 ret = posix_spawn(NULL, execpath, NULL, &attr, argv, newenvp); 208 if (ret != 0) { 209 errno = ret; 210 return -1; 211 } 212 213 /* should not be reached */ 214 return 0; 215} 216