2 * Copyright (c) 1999-2018 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/cdefs.h>
32 #include <sys/types.h>
34 #include <sys/param.h>
37 #include <mach/mach.h>
38 #include <mach-o/arch.h>
40 #include <sys/fcntl.h>
42 #include <CoreFoundation/CoreFoundation.h>
45 #if defined(__x86_64__)
46 #include <System/i386/cpu_capabilities.h>
50 #include <sys/types.h>
51 #include <sys/sysctl.h>
55 #define ARCH_PROG "arch"
58 #define MACHINE_PROG "machine"
61 #define kKeyExecPath "ExecutablePath"
62 #define kKeyPlistVersion "PropertyListVersion"
63 #define kKeyPrefOrder "PreferredOrder"
64 #define kPlistExtension ".plist"
65 #define kSettingsDir "archSettings"
67 static const char envname
[] = "ARCHPREFERENCE";
69 /* The CPU struct contains the argument buffer to posix_spawnattr_setbinpref_np */
73 cpu_subtype_t
*subtypes
;
82 cpu_subtype_t cpusubtype
;
85 static const CPUTypes knownArchs
[] = {
86 {"i386", CPU_TYPE_I386
, CPU_SUBTYPE_X86_ALL
},
87 {"x86_64", CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_ALL
},
88 {"x86_64h", CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_H
},
89 {"arm", CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_ALL
},
90 {"arm64", CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64_ALL
},
91 /* rdar://65725144 ("arch -arm64" command should launch arm64e slice for 2-way fat x86_64+arm64e binaries) */
92 /* This will modify the order if someone, for whatever reason, specifies -arch arm64 -arch x86_64 -arch arm64e */
93 {"arm64", CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64E
},
94 {"arm64e", CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64E
},
95 {"arm64_32", CPU_TYPE_ARM64_32
, CPU_SUBTYPE_ARM64_32_ALL
},
99 char **_copyenv(char **env
);
100 int _setenvp(const char *name
, const char *value
, int rewrite
, char ***envp
, void *state
);
101 int _unsetenvp(const char *name
, char ***envp
, void *state
);
103 /* copy of environment */
104 char **envCopy
= NULL
;
105 extern char **environ
;
108 * The native 32 and 64-bit architectures (this is relative to the architecture
109 * the arch command is running). NULL means unsupported.
115 #if defined(__i386__) || defined(__x86_64__)
117 #elif defined(__arm64__) && !defined(__LP64__)
119 #elif defined(__arm__)
129 #if defined(__x86_64__)
130 // FIXME: It is not clear if we should do this check, given the comment above which states "this is relative to the architecture the arch command is running".
131 if (((*(uint64_t*)_COMM_PAGE_CPU_CAPABILITIES64
) & kIsTranslated
))
134 #elif defined(__i386__)
136 #elif defined(__arm64__) && defined(__LP64__)
138 #elif defined(__arm64__)
146 isSupportedCPU(cpu_type_t cpu
)
148 #if defined(__x86_64__)
149 if (((*(uint64_t*)_COMM_PAGE_CPU_CAPABILITIES64
) & kIsTranslated
))
150 return cpu
== CPU_TYPE_X86_64
|| cpu
== CPU_TYPE_ARM64
;
151 return cpu
== CPU_TYPE_X86_64
;
152 #elif defined(__i386__)
153 return cpu
== CPU_TYPE_I386
|| cpu
== CPU_TYPE_X86_64
;
154 #elif defined(__arm64__) && defined(__LP64__) && TARGET_OS_OSX
155 return cpu
== CPU_TYPE_X86_64
|| cpu
== CPU_TYPE_ARM64
;
156 #elif defined(__arm64__) && defined(__LP64__)
157 return cpu
== CPU_TYPE_ARM64
;
158 #elif defined(__arm64__)
159 return cpu
== CPU_TYPE_ARM64_32
;
160 #elif defined(__arm__)
161 return cpu
== CPU_TYPE_ARM
;
163 #error "Unsupported architecture"
167 bool unrecognizednative32seen
= false;
168 bool unrecognizednative64seen
= false;
171 * arch - perform the original behavior of the arch and machine commands.
172 * The archcmd flag is non-zero for the arch command, zero for the machine
173 * command. This routine never returns.
178 const NXArchInfo
*arch
= NXGetLocalArchInfo();
181 errx(-1, "Unknown architecture.");
183 arch
= NXGetArchInfoFromCpuType(arch
->cputype
, CPU_SUBTYPE_MULTIPLE
);
185 errx(-1, "Unknown architecture.");
187 printf("%s%s", arch
->name
, (isatty(STDIN_FILENO
) ? "\n" : ""));
192 * spawnIt - run the posix_spawn command. cpu is the auto-sizing CPU structure.
193 * pflag is non-zero to call posix_spawnp; zero means to call posix_spawn.
194 * str is the name/path to pass to posix_spawn{,p}, and argv are
195 * the argument arrays to pass. This routine never returns.
198 spawnIt(CPU
*cpu
, int pflag
, const char *str
, char **argv
)
200 posix_spawnattr_t attr
;
204 size_t count
= cpu
->count
;
205 cpu_type_t
*prefs
= cpu
->types
;
206 cpu_subtype_t
*subprefs
= cpu
->subtypes
;
209 if(unrecognizednative32seen
)
210 warnx("Unsupported native 32-bit architecture");
211 if(unrecognizednative64seen
)
212 warnx("Unsupported native 64-bit architecture");
216 if(unrecognizednative32seen
)
217 fprintf(stderr
, "warning: unsupported native 32-bit architecture\n");
218 if(unrecognizednative64seen
)
219 fprintf(stderr
, "warning: unsupported native 64-bit architecture\n");
221 if((ret
= posix_spawnattr_init(&attr
)) != 0)
222 errc(1, ret
, "posix_spawnattr_init");
223 /* do the equivalent of exec, rather than creating a separate process */
224 if((ret
= posix_spawnattr_setflags(&attr
, POSIX_SPAWN_SETEXEC
)) != 0)
225 errc(1, ret
, "posix_spawnattr_setflags");
226 if((ret
= posix_spawnattr_setarchpref_np(&attr
, count
, prefs
, subprefs
, &copied
)) != 0)
227 errc(1, ret
, "posix_spawnattr_setbinpref_np");
230 for (size_t i
= 0; i
< copied
; i
++) {
231 if (prefs
[i
] == CPU_TYPE_ARM64
) {
233 size_t asize
= sizeof(affinity
);
234 sysctlbyname("kern.curproc_arch_affinity", NULL
, NULL
, &affinity
, asize
);
237 #endif /* TARGET_OS_OSX */
240 errx(1, "posix_spawnattr_setbinpref_np only copied %lu of %lu", copied
, count
);
242 ret
= posix_spawnp(&pid
, str
, NULL
, &attr
, argv
, envCopy
? envCopy
: environ
);
244 ret
= posix_spawn(&pid
, str
, NULL
, &attr
, argv
, envCopy
? envCopy
: environ
);
245 errc(1, ret
, "posix_spawn%s: %s", (pflag
? "p" : ""), str
);
249 * initCPU - initialize a CPU structure, a dynamically expanding CPU types
258 cpu
->types
= (cpu_type_t
*)malloc(cpu
->capacity
* sizeof(cpu_type_t
));
260 err(1, "Failed to malloc CPU type buffer");
261 cpu
->subtypes
= (cpu_subtype_t
*)malloc(cpu
->capacity
* sizeof(cpu_subtype_t
));
263 err(1, "Failed to malloc CPU subtype buffer");
267 * addCPU - add a new CPU type value to the CPU structure, expanding
268 * the array as necessary.
271 addCPU(CPU
*cpu
, cpu_type_t n
, cpu_subtype_t s
)
273 if(cpu
->count
== cpu
->capacity
) {
274 cpu_type_t
*newcpubuf
;
275 cpu_subtype_t
*newcpusubbuf
;
278 newcpubuf
= (cpu_type_t
*)realloc(cpu
->types
, cpu
->capacity
* sizeof(cpu_type_t
));
280 err(1, "Out of memory realloc-ing CPU types structure");
281 cpu
->types
= newcpubuf
;
282 newcpusubbuf
= (cpu_subtype_t
*)realloc(cpu
->subtypes
, cpu
->capacity
* sizeof(cpu_subtype_t
));
284 err(1, "Out of memory realloc-ing CPU subtypes structure");
285 cpu
->subtypes
= newcpusubbuf
;
288 cpu
->types
[cpu
->count
] = n
;
289 cpu
->subtypes
[cpu
->count
++] = s
;
293 * addCPUbyname - add a new CPU type, given by name, to the CPU structure,
294 * expanding the array as necessary. The name is converted to a type value
295 * by the ArchDict dictionary.
298 addCPUbyname(CPU
*cpu
, const char *name
)
301 size_t numKnownArchs
= sizeof(knownArchs
)/sizeof(knownArchs
[0]);
303 for (i
=0; i
< numKnownArchs
; i
++) {
304 if (!isSupportedCPU(knownArchs
[i
].cpu
))
307 /* rdar://65725144 ("arch -arm64" command should launch arm64e slice for 2-way fat x86_64+arm64e binaries) */
308 /* Add subtypes that closely match the specified name */
309 while ( i
< numKnownArchs
&&
310 0 == strcasecmp(name
, knownArchs
[i
].arch
) ) {
311 addCPU(cpu
, knownArchs
[i
].cpu
, knownArchs
[i
].cpusubtype
);
318 /* Didn't match a string in knownArchs */
319 warnx("Unknown architecture: %s", name
);
324 * useEnv - parse the environment variable for CPU preferences. Use name
325 * to look for program-specific preferences, and append any CPU types to cpu.
326 * Returns the number of CPU types. Returns any specified execute path in
329 * The environment variable ARCHPREFERENCE has the format:
331 * a semicolon separated list of specifiers. Each specifier has the format:
332 * [prog:[execpath:]]type[,type]...
333 * a comma separate list of CPU type names, optionally proceeded by a program
334 * name and an execpath. If program name exist, that types only apply to that
335 * program. If execpath is specified, it is returned. If no program name
336 * exists, then it applies to all programs. So ordering of the specifiers is
337 * important, as the default (no program name) specifier must be last.
340 useEnv(CPU
*cpu
, const char *name
, char **execpath
)
342 char *val
= getenv(envname
);
346 /* cp will point to the basename of name */
347 const char *cp
= strrchr(name
, '/');
351 errx(1, "%s: no name after last slash", name
);
354 /* make a copy of the environment variable value, so we can modify it */
357 err(1, "Can't copy environment %s", envname
);
360 /* for each specifier */
361 while((blk
= strsep(&str
, ";")) != NULL
) {
363 continue; /* two adjacent semicolons */
364 /* now split on colons */
365 char *n
= strsep(&blk
, ":");
367 char *p
= strsep(&blk
, ":");
368 if(!blk
) { /* there is only one colon, so no execpath */
371 } else if(!*p
) /* two consecutive colons, so no execpath */
374 continue; /* no cpu list, so skip */
375 /* if the name matches, or there is no name, process the cpus */
376 if(!*n
|| strcmp(n
, cp
) == 0) {
377 if(cpu
->count
== 0) { /* only if we haven't processed architectures */
379 while((t
= strsep(&blk
, ",")) != NULL
)
380 addCPUbyname(cpu
, t
);
382 *execpath
= (*n
? p
: NULL
); /* only use the exec path is name is set */
385 } else { /* no colons at all, so process as default */
386 if(cpu
->count
== 0) { /* only if we haven't processed architectures */
388 while((n
= strsep(&blk
, ",")) != NULL
)
389 addCPUbyname(cpu
, n
);
395 if(cpu
->errs
) /* errors during addCPUbyname are fatal */
397 return cpu
->count
; /* return count of architectures */
401 * spawnFromPreference - called when argv[0] is not "arch" or "machine", or
402 * argv[0] was arch, but no commandline architectures were specified.
403 * If the environment variable ARCHPREFERENCE is specified, and there is a
404 * match to argv[0], use the specified cpu preferences. If no exec path
405 * is specified in ARCHPREFERENCE, or no match is found in ARCHPREFERENCE,
406 * get any additional information from a .plist file with the name of argv[0].
407 * This routine never returns.
410 spawnFromPreferences(CPU
*cpu
, int needexecpath
, char **argv
)
413 char fpath
[PATH_MAX
];
414 char execpath2
[PATH_MAX
];
415 CFDictionaryRef plist
= NULL
;
416 sysdir_search_path_enumeration_state state
;
418 const char *prog
= strrchr(*argv
, '/');
425 errx(1, "Not program name specified");
427 /* check the environment variable first */
428 if((count
= useEnv(cpu
, prog
, &epath
)) > 0) {
429 /* if we were called as arch, use posix_spawnp */
431 spawnIt(cpu
, 1, (epath
? epath
: *argv
), argv
);
432 /* otherwise, if we have the executable path, call posix_spawn */
434 spawnIt(cpu
, 0, epath
, argv
);
437 state
= sysdir_start_search_path_enumeration(SYSDIR_DIRECTORY_LIBRARY
, SYSDIR_DOMAIN_MASK_ALL
);
438 while ((state
= sysdir_get_next_search_path_enumeration(state
, fpath
))) {
440 if (fpath
[0] == '~') {
444 bzero(&pglob
, sizeof(pglob
));
446 gret
= glob(fpath
, GLOB_TILDE
, NULL
, &pglob
);
449 for (i
=0; i
< pglob
.gl_pathc
; i
++) {
450 /* take the first glob expansion */
451 strlcpy(fpath
, pglob
.gl_pathv
[i
], sizeof(fpath
));
459 strlcat(fpath
, "/" kSettingsDir
"/", sizeof(fpath
));
460 strlcat(fpath
, prog
, sizeof(fpath
));
461 strlcat(fpath
, kPlistExtension
, sizeof(fpath
));
462 // printf("component: %s\n", fpath);
469 fd
= open(fpath
, O_RDONLY
, 0);
471 ret
= fstat(fd
, &sb
);
473 if (sb
.st_size
<= SIZE_T_MAX
) {
474 length
= (size_t)sb
.st_size
;
475 buffer
= malloc(length
); /* ownership transferred to CFData */
477 rsize
= read(fd
, buffer
, length
);
478 if (rsize
== length
) {
479 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, buffer
, length
, kCFAllocatorMalloc
);
482 plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, kCFPropertyListImmutable
, NULL
, NULL
);
501 if (CFGetTypeID(plist
) != CFDictionaryGetTypeID())
502 errx(1, "%s: plist not a dictionary", fpath
);
504 errx(1, "Can't find any plists for %s", prog
);
507 int errs
= 0; /* scan for all errors and fail later */
508 do { /* begin block */
509 /* check the plist version */
510 CFStringRef vers
= CFDictionaryGetValue(plist
, CFSTR(kKeyPlistVersion
));
512 warnx("%s: No key %s", fpath
, kKeyPlistVersion
);
514 } else if(CFGetTypeID(vers
) != CFStringGetTypeID()) {
515 warnx("%s: %s is not a string", fpath
, kKeyPlistVersion
);
517 } else if(!CFEqual(vers
, CFSTR("1.0"))) {
518 warnx("%s: %s not 1.0", fpath
, kKeyPlistVersion
);
521 /* get the execpath */
522 CFStringRef execpath
= CFDictionaryGetValue(plist
, CFSTR(kKeyExecPath
));
524 warnx("%s: No key %s", fpath
, kKeyExecPath
);
526 } else if(CFGetTypeID(execpath
) != CFStringGetTypeID()) {
527 warnx("%s: %s is not a string", fpath
, kKeyExecPath
);
530 if (!CFStringGetFileSystemRepresentation(execpath
, execpath2
, sizeof(execpath2
))) {
531 warnx("%s: could not get exec path", fpath
);
534 /* if we already got cpu preferences from ARCHPREFERENCE, we are done */
537 /* otherwise, parse the cpu preferences from the plist */
538 CFArrayRef p
= CFDictionaryGetValue(plist
, CFSTR(kKeyPrefOrder
));
540 warnx("%s: No key %s", fpath
, kKeyPrefOrder
);
542 } else if(CFGetTypeID(p
) != CFArrayGetTypeID()) {
543 warnx("%s: %s is not an array", fpath
, kKeyPrefOrder
);
545 } else if((count
= CFArrayGetCount(p
)) == 0) {
546 warnx("%s: no entries in %s", fpath
, kKeyPrefOrder
);
549 /* finally build the cpu type array */
550 for(i
= 0; i
< count
; i
++) {
551 CFStringRef a
= CFArrayGetValueAtIndex(p
, i
);
552 if(CFGetTypeID(a
) != CFStringGetTypeID()) {
553 warnx("%s: entry %lu of %s is not a string", fpath
, i
, kKeyPrefOrder
);
557 if (CFStringGetCString(a
, astr
, sizeof(astr
), kCFStringEncodingASCII
)) {
558 addCPUbyname(cpu
, astr
);
563 } while(0); /* end block */
564 if(errs
) /* exit if there were any reported errors */
569 /* call posix_spawn */
570 spawnIt(cpu
, 0, execpath2
, argv
);
578 " Display the machine's architecture type\n"
579 "Usage: %s {-arch_name | -arch arch_name} ... [-c] [-d envname] ... [-e envname=value] ... [-h] prog [arg ...]\n"
580 " Run prog with any arguments, using the given architecture\n"
581 " order. If no architectures are specified, use the\n"
582 " ARCHPREFERENCE environment variable, or a property list file.\n"
583 " -c will clear out all environment variables before running prog.\n"
584 " -d will delete the given environment variable before running prog.\n"
585 " -e will add the given environment variable/value before running prog.\n"
586 " -h will print usage message and exit.\n",
587 ARCH_PROG
, ARCH_PROG
);
592 * wrapped - check the path to see if it is a link to /usr/bin/arch.
595 wrapped(const char *name
)
601 char buf
[MAXPATHLEN
], rpbuf
[MAXPATHLEN
];
606 do { /* begin block */
607 /* If it's an absolute or relative path name, it's easy. */
608 if(index(name
, '/')) {
609 if(stat(name
, &sb
) == 0 && S_ISREG(sb
.st_mode
) && access(name
, X_OK
) == 0) {
613 errx(1, "%s isn't executable", name
);
616 /* search the PATH, looking for name */
617 if((path
= getenv("PATH")) == NULL
)
618 path
= _PATH_DEFPATH
;
620 cur
= alloca(strlen(path
) + 1);
624 while((p
= strsep(&cur
, ":")) != NULL
) {
626 * It's a SHELL path -- double, leading and trailing colons
627 * mean the current directory.
636 * If the path is too long complain. This is a possible
637 * security issue; given a way to make the path too long
638 * the user may execute the wrong program.
640 if(lp
+ ln
+ 2 > sizeof(buf
)) {
641 warn("%s: path too long", p
);
646 bcopy(name
, buf
+ lp
+ 1, ln
);
647 buf
[lp
+ ln
+ 1] = '\0';
648 if(stat(buf
, &sb
) == 0 && S_ISREG(sb
.st_mode
) && access(buf
, X_OK
) == 0) {
654 errx(1, "Can't find %s in PATH", name
);
655 } while(0); /* end block */
656 if(realpath(bp
, rpbuf
) == NULL
)
657 errx(1, "realpath failed on %s", bp
);
658 return (strcmp(rpbuf
, "/usr/bin/" ARCH_PROG
) == 0);
662 * spawnFromArgs - called when arch has arguments specified. The arch command
663 * line arguments are:
664 * % arch [[{-xxx | -arch xxx}]...] prog [arg]...
665 * where xxx is a cpu name, and the command to execute and its arguments follow.
666 * If no commandline cpu names are given, the environment variable
667 * ARCHPREFERENCE is used. This routine never returns.
670 #define MATCHARG(a,m) ({ \
671 const char *arg = *(a); \
672 if(arg[1] == '-') arg++; \
673 strcmp(arg, (m)) == 0; \
676 #define MATCHARGWITHVALUE(a,m,n,e) ({ \
677 const char *ret = NULL; \
678 const char *arg = *(a); \
679 if(arg[1] == '-') arg++; \
680 if(strcmp(arg, (m)) == 0) { \
681 if(*++(a) == NULL) { \
686 } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \
687 ret = arg + (n) + 1; \
692 #define MAKEENVCOPY(e) \
694 envCopy = _copyenv(environ); \
695 if(envCopy == NULL) \
700 spawnFromArgs(CPU
*cpu
, char **argv
)
702 const char *ap
, *ret
;
704 /* process arguments */
705 for(argv
++; *argv
&& **argv
== '-'; argv
++) {
706 if((ret
= MATCHARGWITHVALUE(argv
, "-arch", 5, "-arch without architecture"))) {
708 } else if(MATCHARG(argv
, "-32")) {
711 unrecognizednative32seen
= true;
714 } else if(MATCHARG(argv
, "-64")) {
717 unrecognizednative64seen
= true;
720 } else if(MATCHARG(argv
, "-c")) {
722 envCopy
= _copyenv(NULL
); // create empty environment
724 errx(1, "Out of memory processing -c");
726 } else if((ret
= MATCHARGWITHVALUE(argv
, "-d", 2, "-d without envname"))) {
727 MAKEENVCOPY("Out of memory processing -d");
728 _unsetenvp(ret
, &envCopy
, NULL
);
730 } else if((ret
= MATCHARGWITHVALUE(argv
, "-e", 2, "-e without envname=value"))) {
731 MAKEENVCOPY("Out of memory processing -e");
732 const char *cp
= strchr(ret
, '=');
734 warnx("-e %s: no equal sign", ret
);
737 cp
++; // skip to value
739 * _setenvp() only uses the name before any equal sign found in
740 * the first argument.
742 _setenvp(ret
, cp
, 1, &envCopy
, NULL
);
744 } else if(MATCHARG(argv
, "-h")) {
750 addCPUbyname(cpu
, ap
);
754 if(!*argv
|| !**argv
) {
755 warnx("No command to execute");
758 /* if the program is already a link to arch, then force execpath */
759 int needexecpath
= wrapped(*argv
);
762 * If we don't have any architecutures, try ARCHPREFERENCE and plist
765 if((cpu
->count
== 0) || needexecpath
)
766 spawnFromPreferences(cpu
, needexecpath
, argv
); /* doesn't return */
769 * Call posix_spawnp on the program name.
771 spawnIt(cpu
, 1, *argv
, argv
);
775 /* the main() routine */
777 main(int argc
, char **argv
)
779 const char *prog
= getprogname();
783 if(strcmp(prog
, MACHINE_PROG
) == 0) {
785 errx(-1, "no arguments accepted");
786 arch(0); /* the "machine" command was called */
787 } else if((my_name_is_arch
= (strcmp(prog
, ARCH_PROG
) == 0))) {
789 arch(1); /* the "arch" command with no arguments was called */
795 spawnFromArgs(&cpu
, argv
);
797 spawnFromPreferences(&cpu
, 1, argv
);
799 /* should never get here */
800 errx(1, "returned from spawn");