static int exec_mach_imgact(struct image_params *imgp) {
struct mach_header *mach_header = (struct mach_header *)imgp->ip_vdata; //........ /* * 首先确保这是个Mach-O 1.0 或者Mach-O 2.0二进制文件 * make sure it's a Mach-O 1.0 or Mach-O 2.0 binary; the difference * is a reserved field on the end, so for the most part, we can * treat them as if they were identical. * magic检查 */ if ((mach_header->magic != MH_MAGIC/*32位架构*/) && (mach_header->magic != MH_MAGIC_64/*64位架构*/)) { error = -1; goto bad; }
// 如果文件类型为MH_DYLIB 或者MH_BUNDLE 返回错误 //为什么MH_DYLIB,MH_BUNDLE 要认定为error switch (mach_header->filetype) { case MH_DYLIB: case MH_BUNDLE: error = -1; goto bad; }
/* * NOTE: An error after this point indicates we have potentially * destroyed or overwrote some process state while attempting an * execve() following a vfork(), which is an unrecoverable condition. */
/* * We reset the task to 64-bit (or not) here. It may have picked up * a new map, and we need that to reflect its true 64-bit nature. */
/* * Set up the system reserved areas in the new address space. */ vm_map_exec(get_task_map(task), task, (void *) p->p_fd->fd_rdir, cpu_type()); //....... }
/* * 检查是否是正确的计算机类型 * Check to see if right machine type. */ if (((cpu_type_t)(header->cputype & ~CPU_ARCH_MASK) != cpu_type()) || !grade_binary(header->cputype, header->cpusubtype & ~CPU_SUBTYPE_MASK)) return(LOAD_BADARCH);
//主要是用来对Mach-O做检测,会检测Mach-O头部,解析其架构、检查imgp等内容, //并拒绝接受Dylib和Bundle这样的文件,这些文件会由dyld负责加载 switch (header->filetype) { case MH_OBJECT: case MH_EXECUTE: case MH_PRELOAD: //第一次的时候这里会走通 if (depth != 1) { return (LOAD_FAILURE); } break; case MH_FVMLIB: case MH_DYLIB: if (depth == 1) { return (LOAD_FAILURE); } break; case MH_DYLINKER: //第二次的时候这里会走通 if (depth != 2) { return (LOAD_FAILURE); } break; default: return (LOAD_FAILURE); } //..... /* * Round size of Mach-O commands up to page boundry. */ size = round_page(mach_header_sz + header->sizeofcmds); if (size <= 0) return(LOAD_BADMACHO);
/* * 将加载命令映射到内核地址 * Map the load commands into kernel memory. */
//.......
/* * 扫描每个命令,处理每个命令 * Scan through the commands, processing each one as necessary. */ for (pass = 1; pass <= 2; pass++) { /* * Loop through each of the load_commands indicated by the * Mach-O header; if an absurd value is provided, we just * run off the end of the reserved section by incrementing * the offset too far, so we are implicitly fail-safe. */ offset = mach_header_sz; //加载命令数目 ncmds = header->ncmds; while (ncmds--) { /* * 获取指向命令的地址 * Get a pointer to the command. */ lcp = (struct load_command *)(addr + offset); oldoffset = offset; offset += lcp->cmdsize; /* * Perform prevalidation of the struct load_command * before we attempt to use its contents. Invalid * values are ones which result in an overflow, or * which can not possibly be valid commands, or which * straddle or exist past the reserved section at the * start of the image. */ if (oldoffset > offset || lcp->cmdsize < sizeof(struct load_command) || offset > header->sizeofcmds + mach_header_sz) { ret = LOAD_BADMACHO; break; } /* * Act on struct load_command's for which kernel * intervention is required. */ switch(lcp->cmd) { /*加载64位segment*/ case LC_SEGMENT_64: if (pass != 1) break; ret = load_segment_64( (struct segment_command_64 *)lcp, pager, file_offset, macho_size, ubc_getsize(vp), map, result); break; /*加载32位segment*/ case LC_SEGMENT: if (pass != 1) break; ret = load_segment( (struct segment_command *) lcp, pager, file_offset, macho_size, ubc_getsize(vp), map, result); break; /*加载线程数据*/ case LC_THREAD: if (pass != 2) break; ret = load_thread((struct thread_command *)lcp, thread, result); break; /*加载unix线程数据*/ case LC_UNIXTHREAD: if (pass != 2) break; ret = load_unixthread( (struct thread_command *) lcp, thread, result); break; /*加载动态加载器 程序需要的dyld的路径*/ case LC_LOAD_DYLINKER: if (pass != 2) break; if ((depth == 1) && (dlp == 0)) { dlp = (struct dylinker_command *)lcp; dlarchbits = (header->cputype & CPU_ARCH_MASK); } else { ret = LOAD_FAILURE; } break; /*加载代码签名加载器*/ case LC_CODE_SIGNATURE: /* CODE SIGNING */ if (pass != 2) break; /* pager -> uip -> load signatures & store in uip set VM object "signed_pages" */ ret = load_code_signature( (struct linkedit_data_command *) lcp, vp, file_offset, macho_size, header->cputype, (depth == 1) ? result : NULL); if (ret != LOAD_SUCCESS) { printf("proc %d: load code signature error %d " "for file \"%s\"\n", p->p_pid, ret, vp->v_name); ret = LOAD_SUCCESS; /* ignore error */ } else { got_code_signatures = TRUE; } break; default: /* Other commands are ignored by the kernel */ ret = LOAD_SUCCESS; break; } if (ret != LOAD_SUCCESS) break; } if (ret != LOAD_SUCCESS) break; } //加载成功 if (ret == LOAD_SUCCESS) { if (! got_code_signatures) { structcs_blob *blob; /* no embedded signatures: look for detached ones */ blob = ubc_cs_blob_get(vp, -1, file_offset); if (blob != NULL) { /* get flags to be applied to the process */ result->csflags |= blob->csb_flags; } } //加载动态链接器dlp为从上面获取到的动态dyliner的路径 if (dlp != 0) //加载dylinker ret = load_dylinker(dlp, dlarchbits, map, thread, depth, result, abi64); //...... } //..... return(ret); }
switch (header->filetype) { case MH_OBJECT: case MH_EXECUTE: case MH_PRELOAD: //depth=1 时候这里会走通 if (depth != 1) { return (LOAD_FAILURE); } break; case MH_FVMLIB: case MH_DYLIB: //depth=1 时候这里会失败 if (depth == 1) { return (LOAD_FAILURE); } break; case MH_DYLINKER: //depth=1 时候这里会失败 if (depth != 2) { return (LOAD_FAILURE); } break; default: return (LOAD_FAILURE); }
这时候如果Mach O 文件类型为 MH_OBJECT,MH_EXECUTE,MH_PRELOAD 就会走到下面的流程,下面的流程会先执行LC_SEGMENT_64,LC_SEGMENT将某些可执行的代码映射到指定的内存区域。然后再执行LC_THREAD,LC_UNIXTHREAD,LC_LOAD_DYLINKER,LC_CODE_SIGNATURE。这里最为关键的命令是LC_LOAD_DYLINKER,它会对dyld进行赋值。在最后会调用:
ret = load_dylinker(dlp, dlarchbits, map, thread, depth, result, abi64);
static load_return_t load_dylinker( struct dylinker_command *lcp, integer_t archbits, vm_map_t map, thread_t thread, int depth, load_result_t *result, boolean_t is_64bit ) { //.......... /* * 首先直接映射dyld * First try to map dyld in directly. This should work most of * the time since there shouldn't normally be something already * mapped to its address. */ // 解析dyld ret = parse_machfile(vp, map, thread, &header, file_offset, macho_size, depth, &myresult); /* * If it turned out something was in the way, then we'll take * take this longer path to map dyld into a temporary map and * copy it into destination map at a different address. */ //如果加载成功设置会返回entry_point if (ret == LOAD_SUCCESS) { result->dynlinker = TRUE; result->entry_point = myresult.entry_point; (void)ubc_map(vp, PROT_READ | PROT_EXEC); } out: vnode_put(vp); return (ret); }
static load_return_t load_threadentry( thread_t thread, unsignedlong *ts, unsignedlong total_size, mach_vm_offset_t *entry_point ) { //...... /* * Set the thread state. */ *entry_point = MACH_VM_MIN_ADDRESS; while (total_size > 0) { flavor = *ts++; size = *ts++; entry_size = (size+2)*sizeof(unsignedlong); if (entry_size > total_size) return(LOAD_BADMACHO); total_size -= entry_size; /* * Third argument is a kernel space pointer; it gets cast * to the appropriate type in thread_entrypoint() based on * the value of flavor. */ ret = thread_entrypoint(thread, flavor, (thread_state_t)ts, size, entry_point); if (ret != KERN_SUCCESS) { return(LOAD_FAILURE); } ts += size; /* ts is a (unsigned long *) */ } return(LOAD_SUCCESS); }
kern_return_t thread_entrypoint( __unused thread_t thread, int flavor, thread_state_t tstate, __unused unsignedint count, mach_vm_offset_t *entry_point ) { /* * Set a default. */ if (*entry_point == 0) *entry_point = VM_MIN_ADDRESS;
switch (flavor) { case x86_THREAD_STATE32: { x86_thread_state32_t *state25;