/* * execute() does an execvp using the argv passed to it. If the parameter * verbose is non-zero the command is printed to stderr. A non-zero return * value indicates success zero indicates failure. */ __private_extern__ int execute( char **argv, long verbose) { char *name, **p; int forkpid, waitpid, termsig; #ifndef __OPENSTEP__ int waitstatus; #else union wait waitstatus; #endif name = argv[0]; if(verbose){ fprintf(stderr, "+ %s ", name); p = &(argv[1]); while(*p != (char *)0) fprintf(stderr, "%s ", *p++); fprintf(stderr, "\n"); } forkpid = fork(); if(forkpid == -1) system_fatal("can't fork a new process to execute: %s", name); if(forkpid == 0){ if(execvp(name, argv) == -1) system_fatal("can't find or exec: %s", name); return(1); /* can't get here, removes a warning from the compiler */ } else{ waitpid = wait(&waitstatus); if(waitpid == -1) system_fatal("wait on forked process %d failed", forkpid); #ifndef __OPENSTEP__ termsig = WTERMSIG(waitstatus); #else termsig = waitstatus.w_termsig; #endif if(termsig != 0 && termsig != SIGINT) fatal("fatal error in %s", name); return( #ifndef __OPENSTEP__ WEXITSTATUS(waitstatus) == 0 && #else waitstatus.w_retcode == 0 && #endif termsig == 0); } }
/* * final_output_flush() flushes the last part of the last page of the object * file if it does not round out to exactly a page. */ static void final_output_flush(void) { struct block *block; unsigned long write_offset, write_size; kern_return_t r; #ifdef DEBUG /* The compiler "warning: `write_offset' may be used uninitialized in */ /* this function" can safely be ignored */ write_offset = 0; if((debug & (1 << 11)) || (debug & (1 << 10))){ print("final_output_flush block_list:\n"); print_block_list(); } #endif /* DEBUG */ write_size = 0; block = output_blocks; if(block != NULL){ if(block->offset != 0) fatal("internal error: first block not at offset 0"); if(block->written_size != 0){ if(block->written_offset != 0) fatal("internal error: first block written_offset not 0"); write_offset = block->written_size; write_size = block->size - block->written_size; } else{ write_offset = block->offset; write_size = block->size; } if(block->next != NULL) fatal("internal error: more than one block in final list"); } if(write_size != 0){ #ifdef DEBUG if((debug & (1 << 11)) || (debug & (1 << 10))) print(" writing (write_offset = %lu write_size = %lu)\n", write_offset, write_size); #endif /* DEBUG */ lseek(fd, write_offset, L_SET); if(write(fd, output_addr + write_offset, write_size) != (int)write_size) system_fatal("can't write to output file"); if((r = vm_deallocate(mach_task_self(), (vm_address_t)(output_addr + write_offset), write_size)) != KERN_SUCCESS) mach_fatal(r, "can't vm_deallocate() buffer for output file"); } }
/* * create() takes the file name of a dynamic library (dylib) and creates a * gmon.out file (gmon_out). It checks to see the dylib file is correct and * creates the gmon.out file proportional to the size of the (__TEXT,__text) * section of the dynamic library. */ static void create( char *dylib, char *gmon_out) { struct arch_flag host_arch_flag; struct ofile ofile; unsigned long i, j, size; struct load_command *lc; struct segment_command *sg; struct section *s, *text_section; kern_return_t r; struct phdr *phdr; char *pcsample_buffer; int fd; /* * Open and map in the dylib file and check it for correctness. */ if(get_arch_from_host(&host_arch_flag, NULL) == 0) fatal("can't determine the host architecture"); if(ofile_map(dylib, &host_arch_flag, NULL, &ofile, FALSE) == FALSE) exit(EXIT_FAILURE); if(ofile.mh == NULL || (ofile.mh->filetype != MH_DYLIB && ofile.mh->filetype != MH_DYLINKER)) fatal("file: %s is not a Mach-O dynamic shared library file", dylib); /* * Get the text section for dynamic library. */ text_section = NULL; lc = ofile.load_commands; for(i = 0; i < ofile.mh->ncmds && text_section == NULL; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; s = (struct section *) ((char *)sg + sizeof(struct segment_command)); for(j = 0; j < sg->nsects; j++){ if(strcmp(s->sectname, SECT_TEXT) == 0 && strcmp(s->segname, SEG_TEXT) == 0){ text_section = s; break; } s++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } if(text_section == NULL) fatal("file: %s does not have a (" SEG_TEXT "," SECT_TEXT ") section", dylib); /* * Create a pcsample buffer for the text section. */ size = text_section->size / HASHFRACTION + sizeof(struct phdr); size = round(size, sizeof(unsigned short)); r = vm_allocate(mach_task_self(), (vm_address_t *)&pcsample_buffer, (vm_size_t)size, TRUE); if(r != KERN_SUCCESS) mach_fatal(r, "can't vm_allocate pcsample buffer of size: %lu", size); /* * Create and write the pcsample file. See comments in gmon.h for the * values of the profile header (struct phdr). */ phdr = (struct phdr *)pcsample_buffer; phdr->lpc = (char *)(text_section->addr); phdr->hpc = (char *)(text_section->addr + text_section->size); phdr->ncnt = (int)size; if((fd = open(gmon_out, O_WRONLY | O_CREAT | O_TRUNC, 0777)) == -1) system_fatal("can't create gmon.out file: %s", gmon_out); if(write(fd, pcsample_buffer, size) != (int)size) system_fatal("can't write gmon.out file: %s", gmon_out); if(close(fd) == -1) system_fatal("can't close gmon.out file: %s", gmon_out); }
/* * output_flush() takes an offset and a size of part of the output file, known * in the comments as the new area, and causes any fully flushed pages to be * written to the output file the new area in combination with previous areas * creates. The data structure output_blocks has ordered blocks of areas that * have been flushed which are maintained by this routine. Any area can only * be flushed once and an error will result is the new area overlaps with a * previously flushed area. * * The goal of this is to again minimize the number of dirty pages the link * editor has and hopfully improve performance in a memory starved system and * to prevent these pages to be written to the swap area when they could just be * written to the output file (if only external pagers worked well ...). */ extern void output_flush( unsigned long offset, unsigned long size) { unsigned long write_offset, write_size; struct block **p, *block, *before, *after; kern_return_t r; if(flush == FALSE) return; /* if(offset == 588824 && size != 0) printf("in output_flush() offset = %lu size = %lu\n", offset, size); */ if(offset + size > output_size) fatal("internal error: output_flush(offset = %lu, size = %lu) out " "of range for output_size = %lu", offset, size, output_size); #ifdef DEBUG if(debug & (1 << 12)) print_block_list(); if(debug & (1 << 11)) print("output_flush(offset = %lu, size %lu)", offset, size); #endif /* DEBUG */ if(size == 0){ #ifdef DEBUG if(debug & (1 << 11)) print("\n"); #endif /* DEBUG */ return; } /* * Search through the ordered output blocks to find the block before the * new area and after the new area if any exist. */ before = NULL; after = NULL; p = &(output_blocks); while(*p){ block = *p; if(offset < block->offset){ after = block; break; } else{ before = block; } p = &(block->next); } /* * Check for overlap of the new area with the block before and after the * new area if there are such blocks. */ if(before != NULL){ if(before->offset + before->size > offset){ warning("internal error: output_flush(offset = %lu, size = %lu) " "overlaps with flushed block(offset = %lu, size = %lu)", offset, size, before->offset, before->size); printf("calling abort()\n"); abort(); } } if(after != NULL){ if(offset + size > after->offset){ warning("internal error: output_flush(offset = %lu, size = %lu) " "overlaps with flushed block(offset = %lu, size = %lu)", offset, size, after->offset, after->size); printf("calling abort()\n"); abort(); } } /* * Now see how the new area fits in with the blocks before and after it * (that is does it touch both, one or the other or neither blocks). * For each case first the offset and size to write (write_offset and * write_size) are set for the area of full pages that can now be * written from the block. Then the area written in the block * (->written_offset and ->written_size) are set to reflect the total * area in the block now written. Then offset and size the block * refers to (->offset and ->size) are set to total area of the block. * Finally the links to others blocks in the list are adjusted if a * block is added or removed. * * See if there is a block before the new area and the new area * starts at the end of that block. */ if(before != NULL && before->offset + before->size == offset){ /* * See if there is also a block after the new area and the new area * ends at the start of that block. */ if(after != NULL && offset + size == after->offset){ /* * This is the case where the new area exactly fill the area * between two existing blocks. The total area is folded into * the block before the new area and the block after the new * area is removed from the list. */ if(before->offset == 0 && before->written_size == 0){ write_offset = 0; before->written_offset = 0; } else write_offset =before->written_offset + before->written_size; if(after->written_size == 0) write_size = trunc(after->offset + after->size - write_offset, host_pagesize); else write_size = trunc(after->written_offset - write_offset, host_pagesize); if(write_size != 0){ before->written_size += write_size; } if(after->written_size != 0) before->written_size += after->written_size; before->size += size + after->size; /* remove the block after the new area */ before->next = after->next; remove_block(after); } else{ /* * This is the case where the new area starts at the end of the * block just before it but does not end where the block after * it (if any) starts. The new area is folded into the block * before the new area. */ write_offset = before->written_offset + before->written_size; write_size = trunc(offset + size - write_offset, host_pagesize); if(write_size != 0) before->written_size += write_size; before->size += size; } } /* * See if the new area and the new area ends at the start of the block * after it (if any). */ else if(after != NULL && offset + size == after->offset){ /* * This is the case where the new area ends at the begining of the * block just after it but does not start where the block before it. * (if any) ends. The new area is folded into this block after the * new area. */ write_offset = round(offset, host_pagesize); if(after->written_size == 0) write_size = trunc(after->offset + after->size - write_offset, host_pagesize); else write_size = trunc(after->written_offset - write_offset, host_pagesize); if(write_size != 0){ after->written_offset = write_offset; after->written_size += write_size; } else if(write_offset != after->written_offset){ after->written_offset = write_offset; } after->offset = offset; after->size += size; } else{ /* * This is the case where the new area neither starts at the end of * the block just before it (if any) or ends where the block after * it (if any) starts. A new block is created and the new area is * is placed in it. */ write_offset = round(offset, host_pagesize); write_size = trunc(offset + size - write_offset, host_pagesize); block = get_block(); block->offset = offset; block->size = size; block->written_offset = write_offset; block->written_size = write_size; /* * Insert this block in the ordered list in the correct place. */ if(before != NULL){ block->next = before->next; before->next = block; } else{ block->next = output_blocks; output_blocks = block; } } /* * Now if there are full pages to write write them to the output file. */ if(write_size != 0){ #ifdef DEBUG if((debug & (1 << 11)) || (debug & (1 << 10))) print(" writing (write_offset = %lu write_size = %lu)\n", write_offset, write_size); #endif /* DEBUG */ lseek(fd, write_offset, L_SET); if(write(fd, output_addr + write_offset, write_size) != (int)write_size) system_fatal("can't write to output file"); if((r = vm_deallocate(mach_task_self(), (vm_address_t)(output_addr + write_offset), write_size)) != KERN_SUCCESS) mach_fatal(r, "can't vm_deallocate() buffer for output file"); } #ifdef DEBUG else{ if(debug & (1 << 11)) print(" no write\n"); } #endif /* DEBUG */ }
/* * pass2() creates the output file and the memory buffer to create the file * into. It drives the process to get everything copied into the buffer for * the output file. It then writes the output file and deallocates the buffer. */ extern void pass2(void) { unsigned long i, j, section_type; struct object_list *object_list, **p; #ifndef RLD int mode; struct stat stat_buf; kern_return_t r; /* * In UNIX standard conformance mode we are not allowed to replace * a file that is not writeable. */ if(get_unix_standard_mode() == TRUE && access(outputfile, F_OK) == 0 && access(outputfile, W_OK) == -1) system_fatal("can't write output file: %s", outputfile); /* * Create the output file. The unlink() is done to handle the problem * when the outputfile is not writable but the directory allows the * file to be removed (since the file may not be there the return code * of the unlink() is ignored). */ (void)unlink(outputfile); if((fd = open(outputfile, O_WRONLY | O_CREAT | O_TRUNC, 0777)) == -1) system_fatal("can't create output file: %s", outputfile); #ifdef F_NOCACHE /* tell filesystem to NOT cache the file when reading or writing */ (void)fcntl(fd, F_NOCACHE, 1); #endif if(fstat(fd, &stat_buf) == -1) system_fatal("can't stat file: %s", outputfile); /* * Turn the execute bits on or off depending if there are any undefined * symbols in the output file. If the file existed before the above * open() call the creation mode in that call would have been ignored * so it has to be set explicitly in any case. */ if(output_mach_header.flags & MH_NOUNDEFS || (has_dynamic_linker_command && output_for_dyld)) mode = (stat_buf.st_mode & 0777) | (0111 & ~umask(0)); else mode = (stat_buf.st_mode & 0777) & ~0111; if(fchmod(fd, mode) == -1) system_fatal("can't set execution permissions output file: %s", outputfile); /* * Create the buffer to copy the parts of the output file into. */ if((r = vm_allocate(mach_task_self(), (vm_address_t *)&output_addr, output_size, TRUE)) != KERN_SUCCESS) mach_fatal(r, "can't vm_allocate() buffer for output file of size " "%lu", output_size); /* * Set up for flushing pages to the output file as they fill up. */ if(flush) setup_output_flush(); /* * Make sure pure_instruction sections are padded with nop's. */ nop_pure_instruction_scattered_sections(); #endif /* !defined(RLD) */ /* * The strings indexes for the merged string blocks need to be set * before the dylib tables are output because the module names are in * them as well as the merged symbol names. */ set_merged_string_block_indexes(); #ifndef RLD /* * Copy the dylib tables into the output file. This is done before the * sections are outputted so that the indexes to the local and external * relocation entries for each object can be used as running indexes as * each section in the object is outputted. */ if(filetype == MH_DYLIB) output_dylib_tables(); #endif /* !defined(RLD) */ /* * Create the array of pointers to merged sections in the output file * so the relocation routines can use it to set the 'referenced' fields * in the merged section structures. */ create_output_sections_array(); /* * Copy the merged literal sections and the sections created from files * into the output object file. */ output_literal_sections(); #ifndef RLD output_sections_from_files(); #endif /* !defined(RLD) */ /* * For each non-literal content section in each object file loaded * relocate it into the output file (along with the relocation entries). * Then relocate local symbols into the output file for the loaded * objects. */ for(p = &objects; *p; p = &(object_list->next)){ object_list = *p; for(i = 0; i < object_list->used; i++){ cur_obj = &(object_list->object_files[i]); /* print the object file name if tracing */ if(trace){ print_obj_name(cur_obj); print("\n"); } if(cur_obj->dylib) continue; if(cur_obj->bundle_loader) continue; if(cur_obj->dylinker) continue; if(cur_obj != base_obj){ for(j = 0; j < cur_obj->nsection_maps; j++){ if(cur_obj->section_maps[j].s->flags & S_ATTR_DEBUG) continue; #ifdef RLD if(cur_obj->set_num == cur_set) #endif /* RLD */ { section_type = (cur_obj->section_maps[j].s->flags & SECTION_TYPE); if(section_type == S_REGULAR || section_type == S_SYMBOL_STUBS || section_type == S_NON_LAZY_SYMBOL_POINTERS || section_type == S_LAZY_SYMBOL_POINTERS || section_type == S_COALESCED || section_type == S_MOD_INIT_FUNC_POINTERS || section_type == S_MOD_TERM_FUNC_POINTERS){ output_section(&(cur_obj->section_maps[j])); } } } } output_local_symbols(); #if defined(VM_SYNC_DEACTIVATE) && !defined(_POSIX_C_SOURCE) && !defined(__CYGWIN__) vm_msync(mach_task_self(), (vm_address_t)cur_obj->obj_addr, (vm_size_t)cur_obj->obj_size, VM_SYNC_DEACTIVATE); #endif /* VM_SYNC_DEACTIVATE */ } } /* * If there were errors in output_section() then return as so not * to cause later internal errors. */ if(errors != 0) return; #ifdef RLD /* * For each content section clean up the data structures not needed * after rld is run. This must be done after ALL the sections are * output'ed because the fine relocation entries could be used by any * of the sections. */ for(p = &objects; *p; p = &(object_list->next)){ object_list = *p; for(i = 0; i < object_list->used; i++){ cur_obj = &(object_list->object_files[i]); for(j = 0; j < cur_obj->nsection_maps; j++){ if(cur_obj->section_maps[j].nfine_relocs != 0){ free(cur_obj->section_maps[j].fine_relocs); cur_obj->section_maps[j].fine_relocs = NULL; cur_obj->section_maps[j].nfine_relocs = 0; } } if(cur_obj->nundefineds != 0){ free(cur_obj->undefined_maps); cur_obj->undefined_maps = NULL; cur_obj->nundefineds = 0; } } } #endif /* RLD */ /* * Set the SG_NORELOC flag in the segments that had no relocation to * or for them. */ set_SG_NORELOC_flags(); #ifndef SA_RLD /* * Copy the indirect symbol table into the output file. */ output_indirect_symbols(); #endif /* SA_RLD */ /* * Copy the merged symbol table into the output file. */ output_merged_symbols(); /* * Copy the headers into the output file. */ output_headers(); #ifndef RLD if(flush){ /* * Flush the sections that have been scatter loaded. */ flush_scatter_copied_sections(); /* * flush the remaining part of the object file that is not a full * page. */ final_output_flush(); } else{ /* * Write the entire object file. */ if(write(fd, output_addr, output_size) != (int)output_size) system_fatal("can't write output file"); if((r = vm_deallocate(mach_task_self(), (vm_address_t)output_addr, output_size)) != KERN_SUCCESS) mach_fatal(r, "can't vm_deallocate() buffer for output file"); } #ifdef F_NOCACHE /* re-enable caching of file reads/writes */ (void)fcntl(fd, F_NOCACHE, 0); #endif if(close(fd) == -1) system_fatal("can't close output file"); #endif /* RLD */ }
int main( int argc, char **argv, char **envp) { const char *LIB = "../libexec/as/"; const char *LOCALLIB = "../local/libexec/as/"; const char *AS = "/as"; int i, j; uint32_t count, verbose, run_clang; char *p, c, *arch_name, *as, *as_local; char **new_argv; const char *CLANG = "clang"; char *prefix, buf[MAXPATHLEN], resolved_name[PATH_MAX]; uint32_t bufsize; struct arch_flag arch_flag; const struct arch_flag *arch_flags, *family_arch_flag; enum bool oflag_specified, qflag, Qflag, some_input_files; progname = argv[0]; arch_name = NULL; verbose = 0; run_clang = 0; oflag_specified = FALSE; qflag = FALSE; Qflag = FALSE; some_input_files = FALSE; /* * Construct the prefix to the assembler driver. */ bufsize = MAXPATHLEN; p = buf; i = _NSGetExecutablePath(p, &bufsize); if(i == -1){ p = allocate(bufsize); _NSGetExecutablePath(p, &bufsize); } prefix = realpath(p, resolved_name); if(realpath == NULL) system_fatal("realpath(3) for %s failed", p); p = rindex(prefix, '/'); if(p != NULL) p[1] = '\0'; /* * Process the assembler flags exactly like the assembler would (except * let the assembler complain about multiple flags, bad combinations of * flags, unknown single letter flags and the like). The main thing * here is to parse out the "-arch <arch_flag>" and to do so the * multiple argument and multiple character flags need to be known how * to be stepped over correctly. */ for(i = 1; i < argc; i++){ /* * The assembler flags start with '-' except that "--" is recognized * as assemble from stdin and that flag "--" is not allowed to be * grouped with other flags (so "-a-" is not the same as "-a --"). */ if(argv[i][0] == '-' && !(argv[i][1] == '-' && argv[i][2] == '\0')){ /* * Treat a single "-" as reading from stdin input also. */ if(argv[i][1] == '\0') some_input_files = TRUE; /* * the assembler allows single letter flags to be grouped * together so "-abc" is the same as "-a -b -c". So that * logic must be followed here. */ for(p = &(argv[i][1]); (c = *p); p++){ /* * The assembler simply ignores the high bit of flag * characters and not treat them as different characters * as they are (but the argument following the flag * character is not treated this way). So it's done * here as well to match it. */ c &= 0x7F; switch(c){ /* * Flags that take a single argument. The argument is the * rest of the current argument if there is any or the it is * the next argument. Again errors like missing arguments * are not handled here but left to the assembler. */ case 'o': /* -o name */ oflag_specified = TRUE; case 'I': /* -I directory */ case 'm': /* -mc68000, -mc68010 and mc68020 */ case 'N': /* -NEXTSTEP-deployment-target */ /* * We want to skip the next argv if the value is not * contained in this argv eg: -I dir . */ if(p[1] == '\0') i++; /* * And in case the value is contained in this argv * (eg: -Idir), skip the rest. */ while(p[1]) p++; p = " "; /* Finished with this arg. */ break; case 'g': if(strcmp(p, "gstabs") == 0 || strcmp(p, "gdwarf2") == 0 || strcmp(p, "gdwarf-2") == 0){ p = " "; /* Finished with this arg. */ } break; case 'd': if(strcmp(p, "dynamic") == 0){ p = " "; /* Finished with this arg. */ } break; case 's': if(strcmp(p, "static") == 0){ p = " "; /* Finished with this arg. */ } break; case 'a': if(strcmp(p, "arch_multiple") == 0){ p = " "; /* Finished with this arg. */ } if(strcmp(p, "arch") == 0){ if(i + 1 >= argc) fatal("missing argument to %s option", argv[i]); if(arch_name != NULL) fatal("more than one %s option (not allowed, " "use cc(1) instead)", argv[i]); arch_name = argv[i+1]; p = " "; /* Finished with this arg. */ i++; break; } /* fall through for non "-arch" */ case 'f': if(strcmp(p, "force_cpusubtype_ALL") == 0){ p = " "; /* Finished with this arg. */ break; } case 'k': case 'v': case 'W': case 'L': case 'l': default: /* just recognize it, do nothing */ break; case 'q': qflag = TRUE; break; case 'Q': Qflag = TRUE; break; case 'V': verbose = 1; break; } } } else{ some_input_files = TRUE; } } /* * Construct the name of the assembler to run from the given -arch * <arch_flag> or if none then from the value returned from * get_arch_from_host(). */ if(arch_name == NULL){ if(get_arch_from_host(&arch_flag, NULL)){ #if __LP64__ /* * If runing as a 64-bit binary and on an Intel x86 host * default to the 64-bit assember. */ if(arch_flag.cputype == CPU_TYPE_I386) arch_flag = *get_arch_family_from_cputype(CPU_TYPE_X86_64); #endif /* __LP64__ */ arch_name = arch_flag.name; } else fatal("unknown host architecture (can't determine which " "assembler to run)"); } else{ /* * Convert a possible machine specific architecture name to a * family name to base the name of the assembler to run. */ if(get_arch_from_flag(arch_name, &arch_flag) != 0){ family_arch_flag = get_arch_family_from_cputype(arch_flag.cputype); if(family_arch_flag != NULL) arch_name = (char *)(family_arch_flag->name); } } if(qflag == TRUE && Qflag == TRUE){ printf("%s: can't specifiy both -q and -Q\n", progname); exit(1); } /* * If the environment variable AS_INTEGRATED_ASSEMBLER is set then set * the qflag to call clang(1) with -integrated-as unless the -Q flag is * set and do this for the supported architectures. */ if(Qflag == FALSE && getenv("AS_INTEGRATED_ASSEMBLER") != NULL && (arch_flag.cputype == CPU_TYPE_X86_64 || arch_flag.cputype == CPU_TYPE_I386 || arch_flag.cputype == CPU_TYPE_ARM64 || arch_flag.cputype == CPU_TYPE_ARM)){ qflag = TRUE; } if(qflag == TRUE && (arch_flag.cputype != CPU_TYPE_X86_64 && arch_flag.cputype != CPU_TYPE_I386 && arch_flag.cputype != CPU_TYPE_ARM64 && arch_flag.cputype != CPU_TYPE_ARM)){ printf("%s: can't specifiy -q with -arch %s\n", progname, arch_flag.name); exit(1); } /* * When the target assembler is for arm64, for now: * rdar://8913781 ARM64: cctools 'as' driver should invoke clang * for ARM64 assembly files * use clang. Later for: * rdar://8928193 ARM64: Standalone 'as' driver * when there is and llvm-mc based standalone 'as' driver and it is * in the usual place as the other target assemblers this use of clang * will be removed. */ if(arch_flag.cputype == CPU_TYPE_ARM64){ if(Qflag == TRUE){ printf("%s: can't specifiy -Q with -arch arm64\n", progname); exit(1); } run_clang = 1; } #if 0 /* * See rdar://9801003 where this will be changed before before NMOs and NMiOS. */ /* * Use the LLVM integrated assembler as the default with the as(1) * driver for Intel (64-bit & 32-bit) as well as ARM for 32-bit too * (64-bit ARM handled above) via running clang. */ if(arch_flag.cputype == CPU_TYPE_X86_64 || arch_flag.cputype == CPU_TYPE_I386 || arch_flag.cputype == CPU_TYPE_ARM) run_clang = 1; #endif /* * Use the clang as the assembler if is the default or asked to with * the -q flag. But don't use it asked to use the system assembler * with the -Q flag. */ if((run_clang || qflag) && !Qflag && (arch_flag.cputype == CPU_TYPE_X86_64 || arch_flag.cputype == CPU_TYPE_I386 || arch_flag.cputype == CPU_TYPE_ARM64 || arch_flag.cputype == CPU_TYPE_ARM)){ as = makestr(prefix, CLANG, NULL); if(access(as, F_OK) != 0){ printf("%s: assembler (%s) not installed\n", progname, as); exit(1); } new_argv = allocate((argc + 8) * sizeof(char *)); new_argv[0] = as; j = 1; /* * Add "-x assembler" in case the input does not end in .s this must * come before "-" or the clang driver will issue an error: * "error: -E or -x required when input is from standard input" */ new_argv[j] = "-x"; j++; new_argv[j] = "assembler"; j++; /* * If we have not seen some some_input_files or a "-" or "--" to * indicate we are assembling stdin add a "-" so clang will * assemble stdin as as(1) would. */ if(some_input_files == FALSE){ new_argv[j] = "-"; j++; } for(i = 1; i < argc; i++){ /* * Translate as(1) use of "--" for stdin to clang's use of "-". */ if(strcmp(argv[i], "--") == 0){ new_argv[j] = "-"; j++; } /* * Do not pass command line argument that are Unknown to * to clang. */ else if(strcmp(argv[i], "-V") != 0 && strcmp(argv[i], "-q") != 0 && strcmp(argv[i], "-Q") != 0){ new_argv[j] = argv[i]; j++; } } /* * clang requires a "-o a.out" if not -o is specified. */ if(oflag_specified == FALSE){ new_argv[j] = "-o"; j++; new_argv[j] = "a.out"; j++; } /* Add -integrated-as or clang will run as(1). */ new_argv[j] = "-integrated-as"; j++; /* Add -c or clang will run ld(1). */ new_argv[j] = "-c"; j++; new_argv[j] = NULL; if(execute(new_argv, verbose)) exit(0); else exit(1); } /* * If this assembler exist try to run it else print an error message. */ as = makestr(prefix, LIB, arch_name, AS, NULL); new_argv = allocate((argc + 1) * sizeof(char *)); new_argv[0] = as; j = 1; for(i = 1; i < argc; i++){ /* * Do not pass command line argument that are unknown to as. */ if(strcmp(argv[i], "-q") != 0 && strcmp(argv[i], "-Q") != 0){ new_argv[j] = argv[i]; j++; } } new_argv[j] = NULL; if(access(as, F_OK) == 0){ argv[0] = as; if(execute(new_argv, verbose)) exit(0); else exit(1); } as_local = makestr(prefix, LOCALLIB, arch_name, AS, NULL); new_argv[0] = as_local; if(access(as_local, F_OK) == 0){ argv[0] = as_local; if(execute(new_argv, verbose)) exit(0); else exit(1); } printf("%s: assembler (%s or %s) for architecture %s not installed\n", progname, as, as_local, arch_name); arch_flags = get_arch_flags(); count = 0; for(i = 0; arch_flags[i].name != NULL; i++){ as = makestr(prefix, LIB, arch_flags[i].name, AS, NULL); if(access(as, F_OK) == 0){ if(count == 0) printf("Installed assemblers are:\n"); printf("%s for architecture %s\n", as, arch_flags[i].name); count++; } else{ as_local = makestr(prefix, LOCALLIB, arch_flags[i].name, AS, NULL); if(access(as_local, F_OK) == 0){ if(count == 0) printf("Installed assemblers are:\n"); printf("%s for architecture %s\n", as_local, arch_flags[i].name); count++; } } } if(count == 0) printf("%s: no assemblers installed\n", progname); exit(1); }
/* * The ctf_insert(1) tool has the following usage: * * ctf_insert input -arch arch ctf_file ... -o output * * Where the input is a Mach-O file that is the ctf_file(s) are to be inserted * into and output is the file to be created. */ int main( int argc, char **argv, char **envp) { uint32_t i; char *input, *output, *contents; struct arch *archs; uint32_t narchs; struct stat stat_buf; int fd; progname = argv[0]; input = NULL; output = NULL; archs = NULL; narchs = 0; for(i = 1; i < argc; i++){ if(strcmp(argv[i], "-o") == 0){ if(i + 1 == argc){ error("missing argument to: %s option", argv[i]); usage(); } if(output != NULL){ error("more than one: %s option specified", argv[i]); usage(); } output = argv[i+1]; i++; } else if(strcmp(argv[i], "-arch") == 0){ if(i + 2 == argc){ error("missing argument(s) to: %s option", argv[i]); usage(); } else{ arch_ctfs = reallocate(arch_ctfs, (narch_ctfs + 1) * sizeof(struct arch_ctf)); if(get_arch_from_flag(argv[i+1], &(arch_ctfs[narch_ctfs].arch_flag)) == 0){ error("unknown architecture specification flag: " "%s %s %s", argv[i], argv[i+1], argv[i+2]); arch_usage(); usage(); } if((fd = open(argv[i+2], O_RDONLY, 0)) == -1) system_fatal("can't open file: %s", argv[i+2]); if(fstat(fd, &stat_buf) == -1) system_fatal("can't stat file: %s", argv[i+2]); /* * For some reason mapping files with zero size fails * so it has to be handled specially. */ contents = NULL; if(stat_buf.st_size != 0){ contents = mmap(0, stat_buf.st_size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0); if((intptr_t)contents == -1) system_error("can't map file : %s", argv[i+2]); } arch_ctfs[narch_ctfs].filename = argv[i+2]; arch_ctfs[narch_ctfs].contents = contents; arch_ctfs[narch_ctfs].size = stat_buf.st_size; arch_ctfs[narch_ctfs].arch_found = FALSE; narch_ctfs++; i += 2; } } else{ if(input != NULL){ error("more than one input file file: %s specified", input); usage(); } input = argv[i]; } } if(input == NULL || output == NULL || narch_ctfs == 0) usage(); breakout(input, &archs, &narchs, FALSE); if(errors) exit(EXIT_FAILURE); checkout(archs, narchs); process(archs, narchs); for(i = 0; i < narch_ctfs; i++){ if(arch_ctfs[i].arch_found == FALSE) fatal("input file: %s does not contain a matching architecture " "for specified '-arch %s %s' option", input, arch_ctfs[i].arch_flag.name, arch_ctfs[i].filename); } writeout(archs, narchs, output, 0777, TRUE, FALSE, FALSE, FALSE, NULL); if(errors) return(EXIT_FAILURE); else return(EXIT_SUCCESS); }
int main( int argc, char **argv, char **envp) { const char *LIB = "../libexec/as/"; const char *LOCALLIB = "../local/libexec/as/"; const char *AS = "/as"; const char *LLVM_MC = "llvm-mc"; int i, j; uint32_t count, verbose, run_llvm_mc; char *p, c, *arch_name, *as, *as_local; char **llvm_mc_argv; char *prefix, buf[MAXPATHLEN], resolved_name[PATH_MAX]; unsigned long bufsize; struct arch_flag arch_flag; const struct arch_flag *arch_flags, *family_arch_flag; enum bool oflag_specified; progname = argv[0]; arch_name = NULL; verbose = 0; run_llvm_mc = 0; oflag_specified = FALSE; /* * Construct the prefix to the assembler driver. */ bufsize = MAXPATHLEN; p = buf; i = _NSGetExecutablePath(p, &bufsize); if(i == -1){ p = allocate(bufsize); _NSGetExecutablePath(p, &bufsize); } prefix = realpath(p, resolved_name); if(realpath == NULL) system_fatal("realpath(3) for %s failed", p); p = rindex(prefix, '/'); if(p != NULL) p[1] = '\0'; /* * Process the assembler flags exactly like the assembler would (except * let the assembler complain about multiple flags, bad combinations of * flags, unknown single letter flags and the like). The main thing * here is to parse out the "-arch <arch_flag>" and to do so the * multiple argument and multiple character flags need to be known how * to be stepped over correctly. */ for(i = 1; i < argc; i++){ /* * The assembler flags start with '-' except that "--" is recognized * as assemble from stdin and that flag "--" is not allowed to be * grouped with other flags (so "-a-" is not the same as "-a --"). */ if(argv[i][0] == '-' && !(argv[i][1] == '-' && argv[i][2] == '0')){ /* * the assembler allows single letter flags to be grouped * together so "-abc" is the same as "-a -b -c". So that * logic must be followed here. */ for(p = &(argv[i][1]); (c = *p); p++){ /* * The assembler simply ignores the high bit of flag * characters and not treat them as different characters * as they are (but the argument following the flag * character is not treated this way). So it's done * here as well to match it. */ c &= 0x7F; switch(c){ /* * Flags that take a single argument. The argument is the * rest of the current argument if there is any or the it is * the next argument. Again errors like missing arguments * are not handled here but left to the assembler. */ case 'o': /* -o name */ oflag_specified = TRUE; case 'I': /* -I directory */ case 'm': /* -mc68000, -mc68010 and mc68020 */ case 'N': /* -NEXTSTEP-deployment-target */ if(p[1] == '\0') i++; break; case 'a': if(strcmp(p, "arch") == 0){ if(i + 1 >= argc) fatal("missing argument to %s option", argv[i]); if(arch_name != NULL) fatal("more than one %s option (not allowed, " "use cc(1) instead)", argv[i]); arch_name = argv[i+1]; break; } /* fall through for non "-arch" */ case 'f': case 'k': case 'g': case 'v': case 'W': case 'L': default: /* just recognize it, do nothing */ break; case 'l': if(strcmp(p, "llvm-mc") == 0) run_llvm_mc = i; /* also just recognize 'l' and do nothing */ break; case 'V': verbose = 1; break; } } } } /* * If the -llvm-mc flag was specified then run llvm-mc from the same * directory as the driver. */ if(run_llvm_mc != 0){ as = makestr(prefix, LLVM_MC, NULL); if(access(as, F_OK) != 0){ printf("%s: assembler (%s) not installed\n", progname, as); exit(1); } llvm_mc_argv = allocate(argc + 3); llvm_mc_argv[0] = as; j = 1; for(i = 1; i < argc; i++){ /* * Do not pass -llvm-mc */ if(i != run_llvm_mc){ /* * Do not pass command line argument that are Unknown to * to llvm-mc. */ if(strcmp(argv[i], "-v") != 0 && strcmp(argv[i], "-V") != 0 && strcmp(argv[i], "-force_cpusubtype_ALL") != 0){ llvm_mc_argv[j] = argv[i]; j++; } } } /* * Add -filetype=obj or llvm-mc will write to stdout. */ llvm_mc_argv[j] = "-filetype=obj"; j++; /* * llvm-mc requires a "-o a.out" if not -o is specified. */ if(oflag_specified == FALSE){ llvm_mc_argv[j] = "-o"; j++; llvm_mc_argv[j] = "a.out"; j++; } llvm_mc_argv[j] = NULL; if(execute(llvm_mc_argv, verbose)) exit(0); else exit(1); } /* * Construct the name of the assembler to run from the given -arch * <arch_flag> or if none then from the value returned from * get_arch_from_host(). */ if(arch_name == NULL){ if(get_arch_from_host(&arch_flag, NULL)){ #if __LP64__ /* * If runing as a 64-bit binary and on an Intel x86 host * default to the 64-bit assember. */ if(arch_flag.cputype == CPU_TYPE_I386) arch_flag = *get_arch_family_from_cputype(CPU_TYPE_X86_64); #endif /* __LP64__ */ arch_name = arch_flag.name; } else fatal("unknown host architecture (can't determine which " "assembler to run)"); } else{ /* * Convert a possible machine specific architecture name to a * family name to base the name of the assembler to run. */ if(get_arch_from_flag(arch_name, &arch_flag) != 0){ family_arch_flag = get_arch_family_from_cputype(arch_flag.cputype); if(family_arch_flag != NULL) arch_name = (char *)(family_arch_flag->name); } } /* * If this assembler exist try to run it else print an error message. */ as = makestr(prefix, LIB, arch_name, AS, NULL); if(access(as, F_OK) == 0){ argv[0] = as; if(execute(argv, verbose)) exit(0); else exit(1); } as_local = makestr(prefix, LOCALLIB, arch_name, AS, NULL); if(access(as_local, F_OK) == 0){ argv[0] = as_local; if(execute(argv, verbose)) exit(0); else exit(1); } printf("%s: assembler (%s or %s) for architecture %s not installed\n", progname, as, as_local, arch_name); arch_flags = get_arch_flags(); count = 0; for(i = 0; arch_flags[i].name != NULL; i++){ as = makestr(prefix, LIB, arch_flags[i].name, AS, NULL); if(access(as, F_OK) == 0){ if(count == 0) printf("Installed assemblers are:\n"); printf("%s for architecture %s\n", as, arch_flags[i].name); count++; } else{ as_local = makestr(prefix, LOCALLIB, arch_flags[i].name, AS, NULL); if(access(as_local, F_OK) == 0){ if(count == 0) printf("Installed assemblers are:\n"); printf("%s for architecture %s\n", as_local, arch_flags[i].name); count++; } } } if(count == 0) printf("%s: no assemblers installed\n", progname); exit(1); }
static void read_dyld_state( uint32_t type, int fd, char *filename, uint32_t nbytes, uint32_t magic) { uint32_t i, offset, sizeof_vmaddr, vmaddr32; uint64_t vmaddr64; char *buf; buf = malloc(nbytes); if(buf == NULL) fatal("no room for dyld state (malloc failed)"); if(read(fd, buf, nbytes) != (int)nbytes) system_fatal("malformed gmon.out file: %s (can't read dyld state)", filename); offset = 0; if(offset + sizeof(uint32_t) > nbytes) fatal("truncated or malformed gmon.out file: %s (image count " "extends past the end of the dyld state)", filename); memcpy(&image_count, buf + offset, sizeof(uint32_t)); offset += sizeof(uint32_t); dyld_images = (struct dyld_image *)malloc(sizeof(struct dyld_image) * image_count); if(dyld_images == NULL) fatal("no room for dyld images (malloc failed)"); if(magic == GMON_MAGIC) sizeof_vmaddr = sizeof(uint32_t); else sizeof_vmaddr = sizeof(uint64_t); for(i = 0; i < image_count; i++){ if(offset + sizeof_vmaddr > nbytes) fatal("truncated or malformed gmon.out file: %s (vmaddr " "for image %u extends past the end of the dyld state)", filename, i); if(magic == GMON_MAGIC){ memcpy(&vmaddr32, buf + offset, sizeof_vmaddr); vmaddr64 = vmaddr32; } else{ memcpy(&vmaddr64, buf + offset, sizeof_vmaddr); } offset += sizeof_vmaddr; if(type == GMONTYPE_DYLD_STATE){ dyld_images[i].vmaddr_slide = vmaddr64; dyld_images[i].image_header = 0; } else{ dyld_images[i].image_header = vmaddr64; dyld_images[i].vmaddr_slide = 0; } dyld_images[i].name = buf + offset; while(buf[offset] != '\0' && offset < nbytes) offset++; if(buf[offset] == '\0' && offset < nbytes) offset++; if(offset > nbytes) fatal("truncated or malformed gmon.out file: %s (name " "for image %u extends past the end of the dyld state)", filename, i); } #ifdef DEBUG if(debug & DYLDDEBUG){ printf("image_count = %u\n", image_count); for(i = 0; i < image_count; i++){ printf("dyld_image[%u].name = %s\n", i, dyld_images[i].name); printf("dyld_image[%u].vmaddr_slide = 0x%llx\n", i, dyld_images[i].vmaddr_slide); printf("dyld_image[%u].image_header = 0x%llx\n", i, dyld_images[i].image_header); } } #endif }
static void read_rld_state( int fd, char *filename, uint32_t nbytes) { uint32_t i, j, size, size_read, str_size; char *strings; size_read = 0; size = sizeof(uint32_t); if(read(fd, &grld_nloaded_states, size) != size) system_fatal("malformed gmon.out file: %s (can't read number of " "rld states)", filename); size_read += size; grld_loaded_state = (struct rld_loaded_state *) malloc(grld_nloaded_states * sizeof(struct rld_loaded_state)); if(grld_loaded_state == NULL) fatal("no room for rld states (malloc failed)"); for(i = 0; i < grld_nloaded_states; i++){ size = sizeof(uint32_t); if(read(fd, &(grld_loaded_state[i].header_addr), size) != size) system_fatal("malformed gmon.out file: %s (can't read header " "address of rld state %lu)", filename, i); size_read += size; size = sizeof(uint32_t); if(read(fd, &(grld_loaded_state[i].nobject_filenames), size) != size) system_fatal("malformed gmon.out file: %s (can't read number " "of object file names of rld state %lu)", filename, i); size_read += size; grld_loaded_state[i].object_filenames = (char **) malloc((grld_loaded_state[i].nobject_filenames + 1) * sizeof(char *)); if(grld_loaded_state[i].object_filenames == NULL) fatal("no room for rld state %lu object file names (malloc " "failed)", i); size = grld_loaded_state[i].nobject_filenames * sizeof(char *); if(read(fd, grld_loaded_state[i].object_filenames, size) != size) system_fatal("malformed gmon.out file: %s (can't read offsets " "to file names of rld state %lu)", filename, i); size_read += size; size = sizeof(uint32_t); if(read(fd, &str_size, size) != size) system_fatal("malformed gmon.out file: %s (can't read string " "size of rld state %lu)", filename, i); size_read += size; strings = (char *)malloc(str_size); if(strings == NULL) fatal("no room for rld state %lu object file names (malloc " "failed)", i); size = str_size; if(read(fd, strings, size) != size) system_fatal("malformed gmon.out file: %s (can't read strings " "of file names of rld state %lu)", filename, i); size_read += size; for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++){ if((uint32_t)(grld_loaded_state[i].object_filenames[j]) > str_size) fatal("malformed gmon.out file: %s (bad offset to object " "filename %lu of rld state %lu)", filename, j, i); grld_loaded_state[i].object_filenames[j] = strings + (uint32_t)(grld_loaded_state[i].object_filenames[j]); } grld_loaded_state[i].object_filenames[j] = NULL; } #ifdef DEBUG if(debug & RLDDEBUG){ printf("grld_nloaded_states = %lu\n", grld_nloaded_states); printf("grld_loaded_state 0x%x\n", (unsigned int)grld_loaded_state); for(i = 0; i < grld_nloaded_states; i++){ printf("state %lu\n\tnobject_filenames %lu\n\tobject_filenames " "0x%x\n\theader_addr 0x%x\n", i, grld_loaded_state[i].nobject_filenames, (unsigned int)(grld_loaded_state[i].object_filenames), (unsigned int)(grld_loaded_state[i].header_addr)); for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++) printf("\t\t%s\n", grld_loaded_state[i].object_filenames[j]); } } #endif }
static void getpfile( char *filename) { uint32_t magic, left; gmon_data_t data; int fd; struct stat stat; if((fd = open(filename, O_RDONLY)) == -1) system_fatal("can't open: %s", filename); if(fstat(fd, &stat) == -1) system_fatal("can't stat: %s", filename); /* * See if this gmon.out file is an old format or new format by looking * for the magic number of the new format. */ if(read(fd, &magic, sizeof(uint32_t)) != sizeof(uint32_t)) system_fatal("malformed gmon.out file: %s (can't read magic " "number)", filename); if(magic == GMON_MAGIC || magic == GMON_MAGIC_64){ #ifdef DEBUG_GMON_OUT if(magic == GMON_MAGIC) printf("GMON_MAGIC\n"); else printf("GMON_MAGIC_64\n"); #endif /* * This is a new format gmon.out file. After the magic number comes * any number of pairs of gmon_data structs and some typed data. */ left = stat.st_size - sizeof(uint32_t); while(left >= sizeof(struct gmon_data)){ if(read(fd, &data, sizeof(struct gmon_data)) != sizeof(struct gmon_data)) system_fatal("malformed gmon.out file: %s (can't read " "gmon_data struct)", filename); left -= sizeof(struct gmon_data); if(left < data.size) fatal("truncated or malformed gmon.out file: %s (value in " "size field in gmon_data struct more than left in " "file)", filename); #ifdef DEBUG_GMON_OUT printf("gmon_data struct: type = %u size = %u\n", data.type, data.size); #endif switch(data.type){ case GMONTYPE_SAMPLES: #ifdef DEBUG_GMON_OUT printf("GMONTYPE_SAMPLES\n"); #endif new_sample_set(fd, filename, data.size, FALSE, magic); break; case GMONTYPE_RAWARCS: #ifdef DEBUG_GMON_OUT printf("GMONTYPE_RAWARCS\n"); #endif if(magic == GMON_MAGIC) readarcs(fd, filename, data.size); else readarcs_64(fd, filename, data.size); break; case GMONTYPE_ARCS_ORDERS: #ifdef DEBUG_GMON_OUT printf("GMONTYPE_ARCS_ORDERS\n"); #endif if(magic == GMON_MAGIC) readarcs_orders(fd, filename, data.size); else readarcs_orders_64(fd, filename, data.size); break; #ifdef __OPENSTEP__ case GMONTYPE_RLD_STATE: #ifdef DEBUG_GMON_OUT printf("GMONTYPE_RLD_STATE\n"); #endif if(grld_nloaded_states == 0){ read_rld_state(fd, filename, data.size); get_rld_state_symbols(); } else{ warning("can't process more than one rld loaded state " "(ignoring rld state from: %s)", filename); lseek(fd, data.size, L_INCR); } break; #endif case GMONTYPE_DYLD_STATE: #ifdef DEBUG_GMON_OUT printf("GMONTYPE_DYLD_STATE\n"); #endif goto setup_dyld_state; case GMONTYPE_DYLD2_STATE: #ifdef DEBUG_GMON_OUT printf("GMONTYPE_DYLD2_STATE\n"); #endif setup_dyld_state: if(image_count == 0){ read_dyld_state(data.type, fd, filename, data.size, magic); get_dyld_state_symbols(); } else{ warning("can't process more than one dyld state " "(ignoring dyld state from: %s)", filename); lseek(fd, data.size, L_INCR); } break; default: #ifdef DEBUG_GMON_OUT printf("Unknown data.type = %u\n", data.type); #endif fatal("truncated or malformed gmon.out file: %s (value in " "type field in gmon_data struct unknown)", filename); } left -= data.size; } if(left != 0) fatal("truncated or malformed gmon.out file: %s (file end in " "the middle of a gmon_data struct %u)", filename, left); } else{ /* * This is an old format gmon.out file. It has a profile header * then * an array of sampling hits within pc ranges, and then arcs. */ lseek(fd, 0, L_SET); left = stat.st_size; left -= new_sample_set(fd, filename, left, TRUE, GMON_MAGIC); readarcs(fd, filename, left); } close(fd); }