1/*
2 * The assembler driver as and runs the assembler for the "-arch <arch_flag>"
3 * (if given) in ../libexec/as/<arch_flag>/as or
4 * ../local/libexec/as/<arch_flag>/as.  Or runs the assembler for the host
5 * architecture as returned by get_arch_from_host().  The driver only checks to
6 * make sure their are not multiple arch_flags and then passes all flags to the
7 * assembler it will run.
8 */
9#include "stdio.h"
10#include "stdlib.h"
11#include "string.h"
12#include "libc.h"
13#include <sys/file.h>
14#include <mach/mach.h>
15#include "stuff/arch.h"
16#include "stuff/errors.h"
17#include "stuff/execute.h"
18#include "stuff/allocate.h"
19#include <mach-o/dyld.h>
20
21/* used by error calls (exported) */
22char *progname = NULL;
23
24int
25main(
26int argc,
27char **argv,
28char **envp)
29{
30    const char *LIB = "../libexec/as/";
31    const char *LOCALLIB = "../local/libexec/as/";
32    const char *AS = "/as";
33
34    int i, j;
35    uint32_t count, verbose, run_clang;
36    char *p, c, *arch_name, *as, *as_local;
37    char **new_argv;
38    const char *CLANG = "clang";
39    char *prefix, buf[MAXPATHLEN], resolved_name[PATH_MAX];
40    uint32_t bufsize;
41    struct arch_flag arch_flag;
42    const struct arch_flag *arch_flags, *family_arch_flag;
43    enum bool oflag_specified, qflag, Qflag;
44
45	progname = argv[0];
46	arch_name = NULL;
47	verbose = 0;
48	run_clang = 0;
49	oflag_specified = FALSE;
50	qflag = FALSE;
51	Qflag = FALSE;
52	/*
53	 * Construct the prefix to the assembler driver.
54	 */
55	bufsize = MAXPATHLEN;
56	p = buf;
57	i = _NSGetExecutablePath(p, &bufsize);
58	if(i == -1){
59	    p = allocate(bufsize);
60	    _NSGetExecutablePath(p, &bufsize);
61	}
62	prefix = realpath(p, resolved_name);
63	if(realpath == NULL)
64	    system_fatal("realpath(3) for %s failed", p);
65	p = rindex(prefix, '/');
66	if(p != NULL)
67	    p[1] = '\0';
68	/*
69	 * Process the assembler flags exactly like the assembler would (except
70	 * let the assembler complain about multiple flags, bad combinations of
71	 * flags, unknown single letter flags and the like).  The main thing
72	 * here is to parse out the "-arch <arch_flag>" and to do so the
73	 * multiple argument and multiple character flags need to be known how
74	 * to be stepped over correctly.
75	 */
76	for(i = 1; i < argc; i++){
77	    /*
78	     * The assembler flags start with '-' except that "--" is recognized
79	     * as assemble from stdin and that flag "--" is not allowed to be
80	     * grouped with other flags (so "-a-" is not the same as "-a --").
81	     */
82	    if(argv[i][0] == '-' &&
83	       !(argv[i][1] == '-' && argv[i][2] == '0')){
84		/*
85		 * the assembler allows single letter flags to be grouped
86		 * together so "-abc" is the same as "-a -b -c".  So that
87		 * logic must be followed here.
88		 */
89		for(p = &(argv[i][1]); (c = *p); p++){
90		    /*
91		     * The assembler simply ignores the high bit of flag
92		     * characters and not treat them as different characters
93		     * as they are (but the argument following the flag
94		     * character is not treated this way).  So it's done
95		     * here as well to match it.
96		     */
97		    c &= 0x7F;
98		    switch(c){
99		    /*
100		     * Flags that take a single argument.  The argument is the
101		     * rest of the current argument if there is any or the it is
102		     * the next argument.  Again errors like missing arguments
103		     * are not handled here but left to the assembler.
104		     */
105		    case 'o':	/* -o name */
106			oflag_specified = TRUE;
107		    case 'I':	/* -I directory */
108		    case 'm':	/* -mc68000, -mc68010 and mc68020 */
109		    case 'N':	/* -NEXTSTEP-deployment-target */
110			if(p[1] == '\0')
111			    i++;
112			p = " "; /* Finished with this arg. */
113			break;
114	    	    case 'g':
115			if(strcmp(p, "gstabs") == 0 ||
116	    		   strcmp(p, "gdwarf2") == 0 ||
117			   strcmp(p, "gdwarf-2") == 0){
118			    p = " "; /* Finished with this arg. */
119			}
120			break;
121		    case 'd':
122			if(strcmp(p, "dynamic") == 0){
123			    p = " "; /* Finished with this arg. */
124			}
125			break;
126		    case 's':
127			if(strcmp(p, "static") == 0){
128			    p = " "; /* Finished with this arg. */
129			}
130			break;
131		    case 'a':
132		        if(strcmp(p, "arch_multiple") == 0){
133			    p = " "; /* Finished with this arg. */
134			}
135			if(strcmp(p, "arch") == 0){
136			    if(i + 1 >= argc)
137				fatal("missing argument to %s option", argv[i]);
138			    if(arch_name != NULL)
139				fatal("more than one %s option (not allowed, "
140				      "use cc(1) instead)", argv[i]);
141			    arch_name = argv[i+1];
142			    p = " "; /* Finished with this arg. */
143			    i++;
144			    break;
145			}
146			/* fall through for non "-arch" */
147		    case 'f':
148			if(strcmp(p, "force_cpusubtype_ALL") == 0){
149			    p = " "; /* Finished with this arg. */
150			    break;
151			}
152		    case 'k':
153		    case 'v':
154		    case 'W':
155		    case 'L':
156		    case 'l':
157		    default:
158			/* just recognize it, do nothing */
159			break;
160		    case 'q':
161			qflag = TRUE;
162			break;
163		    case 'Q':
164			Qflag = TRUE;
165			break;
166		    case 'V':
167			verbose = 1;
168			break;
169		    }
170		}
171	    }
172	}
173
174	/*
175	 * Construct the name of the assembler to run from the given -arch
176	 * <arch_flag> or if none then from the value returned from
177	 * get_arch_from_host().
178	 */
179	if(arch_name == NULL){
180	    if(get_arch_from_host(&arch_flag, NULL)){
181#if __LP64__
182		/*
183		 * If runing as a 64-bit binary and on an Intel x86 host
184		 * default to the 64-bit assember.
185		 */
186		if(arch_flag.cputype == CPU_TYPE_I386)
187		    arch_flag = *get_arch_family_from_cputype(CPU_TYPE_X86_64);
188#endif /* __LP64__ */
189		arch_name = arch_flag.name;
190	    }
191	    else
192		fatal("unknown host architecture (can't determine which "
193		      "assembler to run)");
194	}
195	else{
196	    /*
197	     * Convert a possible machine specific architecture name to a
198	     * family name to base the name of the assembler to run.
199	     */
200	    if(get_arch_from_flag(arch_name, &arch_flag) != 0){
201		family_arch_flag =
202			get_arch_family_from_cputype(arch_flag.cputype);
203		if(family_arch_flag != NULL)
204		    arch_name = (char *)(family_arch_flag->name);
205	    }
206
207	}
208
209	if(qflag == TRUE && Qflag == TRUE){
210	    printf("%s: can't specifiy both -q and -Q\n", progname);
211	    exit(1);
212	}
213	/*
214	 * If the environment variable AS_INTEGRATED_ASSEMBLER is set then set
215	 * the qflag to call clang(1) with -integrated-as unless the -Q flag is
216	 * set and do this for the supported architectures.
217	 */
218	if(Qflag == FALSE &&
219           getenv("AS_INTEGRATED_ASSEMBLER") != NULL &&
220	   (arch_flag.cputype == CPU_TYPE_X86_64 ||
221	    arch_flag.cputype == CPU_TYPE_I386 ||
222	    arch_flag.cputype == CPU_TYPE_ARM)){
223	    qflag = TRUE;
224	}
225	if(qflag == TRUE &&
226	   (arch_flag.cputype != CPU_TYPE_X86_64 &&
227	    arch_flag.cputype != CPU_TYPE_I386 &&
228	    arch_flag.cputype != CPU_TYPE_ARM)){
229	    printf("%s: can't specifiy -q with -arch %s\n", progname,
230		   arch_flag.name);
231	    exit(1);
232	}
233
234#ifdef notyet
235	/*
236	 * Use the x86 LLVM assembler as the default via running clang.
237	 * That is after rdar://11139995 "Qualify as(1) using the x86 LLVM
238	 * assembler as the default" is done.
239	 */
240	if(arch_flag.cputype == CPU_TYPE_X86_64 ||
241	   arch_flag.cputype == CPU_TYPE_I386)
242	    run_clang = 1;
243#endif /* notyet */
244
245	/*
246	 * Use the clang as the assembler if is the default or asked to with
247	 * the -q flag. But don't use it asked to use the system assembler
248	 * with the -Q flag.
249	 */
250	if((run_clang || qflag) && !Qflag &&
251	   (arch_flag.cputype == CPU_TYPE_X86_64 ||
252	    arch_flag.cputype == CPU_TYPE_I386 ||
253	    arch_flag.cputype == CPU_TYPE_ARM)){
254	    as = makestr(prefix, CLANG, NULL);
255	    if(access(as, F_OK) != 0){
256		printf("%s: assembler (%s) not installed\n", progname, as);
257		exit(1);
258	    }
259	    new_argv = allocate((argc + 5) * sizeof(char *));
260	    new_argv[0] = as;
261	    j = 1;
262	    for(i = 1; i < argc; i++){
263		/*
264		 * Do not pass command line argument that are Unknown to
265		 * to clang.
266		 */
267		if(strcmp(argv[i], "-V") != 0 &&
268		   strcmp(argv[i], "-q") != 0 &&
269		   strcmp(argv[i], "-Q") != 0){
270		    new_argv[j] = argv[i];
271		    j++;
272		}
273	    }
274	    /*
275	     * clang requires a "-o a.out" if not -o is specified.
276	     */
277	    if(oflag_specified == FALSE){
278		new_argv[j] = "-o";
279		j++;
280		new_argv[j] = "a.out";
281		j++;
282	    }
283	    /* Add -integrated-as or clang will run as(1). */
284	    new_argv[j] = "-integrated-as";
285	    j++;
286	    /* Add -c or clang will run ld(1). */
287	    new_argv[j] = "-c";
288	    j++;
289	    new_argv[j] = NULL;
290	    if(execute(new_argv, verbose))
291		exit(0);
292	    else
293		exit(1);
294	}
295
296	/*
297	 * If this assembler exist try to run it else print an error message.
298	 */
299	as = makestr(prefix, LIB, arch_name, AS, NULL);
300	new_argv = allocate((argc + 1) * sizeof(char *));
301	new_argv[0] = as;
302	j = 1;
303	for(i = 1; i < argc; i++){
304	    /*
305	     * Do not pass command line argument that are unknown to as.
306	     */
307	    if(strcmp(argv[i], "-q") != 0 &&
308	       strcmp(argv[i], "-Q") != 0){
309		new_argv[j] = argv[i];
310		j++;
311	    }
312	}
313	new_argv[j] = NULL;
314	if(access(as, F_OK) == 0){
315	    argv[0] = as;
316	    if(execute(new_argv, verbose))
317		exit(0);
318	    else
319		exit(1);
320	}
321	as_local = makestr(prefix, LOCALLIB, arch_name, AS, NULL);
322	new_argv[0] = as_local;
323	if(access(as_local, F_OK) == 0){
324	    argv[0] = as_local;
325	    if(execute(new_argv, verbose))
326		exit(0);
327	    else
328		exit(1);
329	}
330	printf("%s: assembler (%s or %s) for architecture %s not installed\n",
331	       progname, as, as_local, arch_name);
332	arch_flags = get_arch_flags();
333	count = 0;
334	for(i = 0; arch_flags[i].name != NULL; i++){
335	    as = makestr(prefix, LIB, arch_flags[i].name, AS, NULL);
336	    if(access(as, F_OK) == 0){
337		if(count == 0)
338		    printf("Installed assemblers are:\n");
339		printf("%s for architecture %s\n", as, arch_flags[i].name);
340		count++;
341	    }
342	    else{
343		as_local = makestr(prefix, LOCALLIB, arch_flags[i].name, AS,
344				   NULL);
345		if(access(as_local, F_OK) == 0){
346		    if(count == 0)
347			printf("Installed assemblers are:\n");
348		    printf("%s for architecture %s\n", as_local,
349			   arch_flags[i].name);
350		    count++;
351		}
352	    }
353	}
354	if(count == 0)
355	    printf("%s: no assemblers installed\n", progname);
356	exit(1);
357}
358