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