#include "../idaldr.h" #include "common.h" #include #include #include #include #include "../ar/ar.hpp" #include // note that this code is used when building idapyswitch for mac. // we must avoid including irrxml junk for idapyswitch because it will ultimately be published // with the public idapython source, which must not depend on irrxml (which we don't publish). #ifndef BUILD_IDAPYSWITCH #include #include using namespace irr; using namespace io; #endif //-------------------------------------------------------------------------- bool isPair(const struct mach_header_64 *mh, int type) { return (mh->cputype == CPU_TYPE_ARM && type == ARM_RELOC_PAIR) || (mh->cputype == CPU_TYPE_MC680x0 && type == GENERIC_RELOC_PAIR) || (mh->cputype == CPU_TYPE_I386 && type == GENERIC_RELOC_PAIR) || (mh->cputype == CPU_TYPE_MC88000 && type == M88K_RELOC_PAIR) || ((mh->cputype == CPU_TYPE_POWERPC || mh->cputype == CPU_TYPE_POWERPC64 || mh->cputype == CPU_TYPE_VEO) && type == PPC_RELOC_PAIR) || (mh->cputype == CPU_TYPE_HPPA && type == HPPA_RELOC_PAIR) || (mh->cputype == CPU_TYPE_SPARC && type == SPARC_RELOC_PAIR) || (mh->cputype == CPU_TYPE_I860 && type == I860_RELOC_PAIR); } //-------------------------------------------------------------------------- bool isSectdiff(const struct mach_header_64 *mh, int type) { return (mh->cputype == CPU_TYPE_MC680x0 && (type == GENERIC_RELOC_SECTDIFF || type == GENERIC_RELOC_LOCAL_SECTDIFF)) || (mh->cputype == CPU_TYPE_I386 && (type == GENERIC_RELOC_SECTDIFF || type == GENERIC_RELOC_LOCAL_SECTDIFF)) || (mh->cputype == CPU_TYPE_ARM && (type == ARM_RELOC_SECTDIFF || type == ARM_RELOC_LOCAL_SECTDIFF || type == ARM_RELOC_HALF_SECTDIFF)) || (mh->cputype == CPU_TYPE_MC88000 && type == M88K_RELOC_SECTDIFF) || ((mh->cputype == CPU_TYPE_POWERPC || mh->cputype == CPU_TYPE_POWERPC64 || mh->cputype == CPU_TYPE_VEO) && (type == PPC_RELOC_SECTDIFF || type == PPC_RELOC_LOCAL_SECTDIFF || type == PPC_RELOC_HI16_SECTDIFF || type == PPC_RELOC_LO16_SECTDIFF || type == PPC_RELOC_LO14_SECTDIFF || type == PPC_RELOC_HA16_SECTDIFF)) || (mh->cputype == CPU_TYPE_I860 && type == I860_RELOC_SECTDIFF) || (mh->cputype == CPU_TYPE_HPPA && (type == HPPA_RELOC_SECTDIFF || type == HPPA_RELOC_HI21_SECTDIFF || type == HPPA_RELOC_LO14_SECTDIFF)) || (mh->cputype == CPU_TYPE_SPARC && (type == SPARC_RELOC_SECTDIFF || type == SPARC_RELOC_HI22_SECTDIFF || type == SPARC_RELOC_LO10_SECTDIFF)); } //-------------------------------------------------------------------------- static void swap_mach_header_64(struct mach_header_64 *mh) { mh->magic = swap32(mh->magic); mh->cputype = swap32(mh->cputype); mh->cpusubtype = swap32(mh->cpusubtype); mh->filetype = swap32(mh->filetype); mh->ncmds = swap32(mh->ncmds); mh->sizeofcmds = swap32(mh->sizeofcmds); mh->flags = swap32(mh->flags); mh->reserved = swap32(mh->reserved); } //-------------------------------------------------------------------------- static void swap_load_command(load_command *lc) { lc->cmd = swap32(lc->cmd); lc->cmdsize = swap32(lc->cmdsize); } //-------------------------------------------------------------------------- static void swap_segment_command(segment_command *sg) { /* segname[16] */ sg->cmd = swap32(sg->cmd); sg->cmdsize = swap32(sg->cmdsize); sg->vmaddr = swap32(sg->vmaddr); sg->vmsize = swap32(sg->vmsize); sg->fileoff = swap32(sg->fileoff); sg->filesize = swap32(sg->filesize); sg->maxprot = swap32(sg->maxprot); sg->initprot = swap32(sg->initprot); sg->nsects = swap32(sg->nsects); sg->flags = swap32(sg->flags); } static void swap_segment_command(segment_command_64 *sg) { /* segname[16] */ sg->cmd = swap32(sg->cmd); sg->cmdsize = swap32(sg->cmdsize); sg->vmaddr = swap64(sg->vmaddr); sg->vmsize = swap64(sg->vmsize); sg->fileoff = swap64(sg->fileoff); sg->filesize = swap64(sg->filesize); sg->maxprot = swap32(sg->maxprot); sg->initprot = swap32(sg->initprot); sg->nsects = swap32(sg->nsects); sg->flags = swap32(sg->flags); } //-------------------------------------------------------------------------- static void swap_section(section *s, uint32 nsects) { for ( uint32 i = 0; i < nsects; i++ ) { /* sectname[16] */ /* segname[16] */ s[i].addr = swap32(s[i].addr); s[i].size = swap32(s[i].size); s[i].offset = swap32(s[i].offset); s[i].align = swap32(s[i].align); s[i].reloff = swap32(s[i].reloff); s[i].nreloc = swap32(s[i].nreloc); s[i].flags = swap32(s[i].flags); s[i].reserved1 = swap32(s[i].reserved1); s[i].reserved2 = swap32(s[i].reserved2); } } //-------------------------------------------------------------------------- static void swap_section(section_64 *s, uint32 nsects) { for ( uint32 i = 0; i < nsects; i++ ) { /* sectname[16] */ /* segname[16] */ s[i].addr = swap64(s[i].addr); s[i].size = swap64(s[i].size); s[i].offset = swap32(s[i].offset); s[i].align = swap32(s[i].align); s[i].reloff = swap32(s[i].reloff); s[i].nreloc = swap32(s[i].nreloc); s[i].flags = swap32(s[i].flags); s[i].reserved1 = swap32(s[i].reserved1); s[i].reserved2 = swap32(s[i].reserved2); } } //-------------------------------------------------------------------------- static void swap_symtab_command(symtab_command *st) { st->cmd = swap32(st->cmd); st->cmdsize = swap32(st->cmdsize); st->symoff = swap32(st->symoff); st->nsyms = swap32(st->nsyms); st->stroff = swap32(st->stroff); st->strsize = swap32(st->strsize); } //-------------------------------------------------------------------------- static void swap_dysymtab_command(dysymtab_command *dyst) { dyst->cmd = swap32(dyst->cmd); dyst->cmdsize = swap32(dyst->cmdsize); dyst->ilocalsym = swap32(dyst->ilocalsym); dyst->nlocalsym = swap32(dyst->nlocalsym); dyst->iextdefsym = swap32(dyst->iextdefsym); dyst->nextdefsym = swap32(dyst->nextdefsym); dyst->iundefsym = swap32(dyst->iundefsym); dyst->nundefsym = swap32(dyst->nundefsym); dyst->tocoff = swap32(dyst->tocoff); dyst->ntoc = swap32(dyst->ntoc); dyst->modtaboff = swap32(dyst->modtaboff); dyst->nmodtab = swap32(dyst->nmodtab); dyst->extrefsymoff = swap32(dyst->extrefsymoff); dyst->nextrefsyms = swap32(dyst->nextrefsyms); dyst->indirectsymoff = swap32(dyst->indirectsymoff); dyst->nindirectsyms = swap32(dyst->nindirectsyms); dyst->extreloff = swap32(dyst->extreloff); dyst->nextrel = swap32(dyst->nextrel); dyst->locreloff = swap32(dyst->locreloff); dyst->nlocrel = swap32(dyst->nlocrel); } //-------------------------------------------------------------------------- static void swap_symseg_command(symseg_command *ss) { ss->cmd = swap32(ss->cmd); ss->cmdsize = swap32(ss->cmdsize); ss->offset = swap32(ss->offset); ss->size = swap32(ss->size); } //-------------------------------------------------------------------------- static void swap_fvmlib_command(fvmlib_command *fl) { fl->cmd = swap32(fl->cmd); fl->cmdsize = swap32(fl->cmdsize); fl->fvmlib.name.offset = swap32(fl->fvmlib.name.offset); fl->fvmlib.minor_version = swap32(fl->fvmlib.minor_version); fl->fvmlib.header_addr = swap32(fl->fvmlib.header_addr); } //-------------------------------------------------------------------------- static void swap_thread_command(thread_command *tc) { tc->cmd = swap32(tc->cmd); tc->cmdsize = swap32(tc->cmdsize); } //-------------------------------------------------------------------------- static void swap_dylib_command(dylib_command *dl) { dl->cmd = swap32(dl->cmd); dl->cmdsize = swap32(dl->cmdsize); dl->dylib.name.offset = swap32(dl->dylib.name.offset); dl->dylib.timestamp = swap32(dl->dylib.timestamp); dl->dylib.current_version = swap32(dl->dylib.current_version); dl->dylib.compatibility_version = swap32(dl->dylib.compatibility_version); } //-------------------------------------------------------------------------- static void swap_sub_framework_command(sub_framework_command *sub) { sub->cmd = swap32(sub->cmd); sub->cmdsize = swap32(sub->cmdsize); sub->umbrella.offset = swap32(sub->umbrella.offset); } //-------------------------------------------------------------------------- static void swap_sub_umbrella_command(sub_umbrella_command *usub) { usub->cmd = swap32(usub->cmd); usub->cmdsize = swap32(usub->cmdsize); usub->sub_umbrella.offset = swap32(usub->sub_umbrella.offset); } //-------------------------------------------------------------------------- static void swap_sub_library_command(struct sub_library_command *lsub) { lsub->cmd = swap32(lsub->cmd); lsub->cmdsize = swap32(lsub->cmdsize); lsub->sub_library.offset = swap32(lsub->sub_library.offset); } //-------------------------------------------------------------------------- static void swap_sub_client_command(sub_client_command *csub) { csub->cmd = swap32(csub->cmd); csub->cmdsize = swap32(csub->cmdsize); csub->client.offset = swap32(csub->client.offset); } //-------------------------------------------------------------------------- static void swap_prebound_dylib_command(prebound_dylib_command *pbdylib) { pbdylib->cmd = swap32(pbdylib->cmd); pbdylib->cmdsize = swap32(pbdylib->cmdsize); pbdylib->name.offset = swap32(pbdylib->name.offset); pbdylib->nmodules = swap32(pbdylib->nmodules); pbdylib->linked_modules.offset = swap32(pbdylib->linked_modules.offset); } //-------------------------------------------------------------------------- static void swap_dylinker_command(dylinker_command *dyld) { dyld->cmd = swap32(dyld->cmd); dyld->cmdsize = swap32(dyld->cmdsize); dyld->name.offset = swap32(dyld->name.offset); } //-------------------------------------------------------------------------- static void swap_fvmfile_command(fvmfile_command *ff) { ff->cmd = swap32(ff->cmd); ff->cmdsize = swap32(ff->cmdsize); ff->name.offset = swap32(ff->name.offset); ff->header_addr = swap32(ff->header_addr); } //-------------------------------------------------------------------------- /* #ifndef EFD_COMPILE static void swap_thread_command(thread_command *ut) { ut->cmd = swap32(ut->cmd); ut->cmdsize = swap32(ut->cmdsize); } #endif // EFD_COMPILE */ //-------------------------------------------------------------------------- /* static void swap_m68k_thread_state_regs(struct m68k_thread_state_regs *cpu) { uint32 i; for ( i = 0; i < 8; i++ ) cpu->dreg[i] = swap32(cpu->dreg[i]); for ( i = 0; i < 8; i++ ) cpu->areg[i] = swap32(cpu->areg[i]); cpu->pad0 = swap16(cpu->pad0); cpu->sr = swap16(cpu->sr); cpu->pc = swap32(cpu->pc); } //-------------------------------------------------------------------------- static void swap_m68k_thread_state_68882(struct m68k_thread_state_68882 *fpu) { uint32 i, tmp; for ( i = 0; i < 8; i++ ) { tmp = swap32(fpu->regs[i].fp[0]); fpu->regs[i].fp[1] = swap32(fpu->regs[i].fp[1]); fpu->regs[i].fp[0] = swap32(fpu->regs[i].fp[2]); fpu->regs[i].fp[2] = tmp; } fpu->cr = swap32(fpu->cr); fpu->sr = swap32(fpu->sr); fpu->iar = swap32(fpu->iar); fpu->state = swap32(fpu->state); } //-------------------------------------------------------------------------- static void swap_m68k_thread_state_user_reg(struct m68k_thread_state_user_reg *user_reg) { user_reg->user_reg = swap32(user_reg->user_reg); } //-------------------------------------------------------------------------- static void swap_m88k_thread_state_grf_t(m88k_thread_state_grf_t *cpu) { cpu->r1 = swap32(cpu->r1); cpu->r2 = swap32(cpu->r2); cpu->r3 = swap32(cpu->r3); cpu->r4 = swap32(cpu->r4); cpu->r5 = swap32(cpu->r5); cpu->r6 = swap32(cpu->r6); cpu->r7 = swap32(cpu->r7); cpu->r8 = swap32(cpu->r8); cpu->r9 = swap32(cpu->r9); cpu->r10 = swap32(cpu->r10); cpu->r11 = swap32(cpu->r11); cpu->r12 = swap32(cpu->r12); cpu->r13 = swap32(cpu->r13); cpu->r14 = swap32(cpu->r14); cpu->r15 = swap32(cpu->r15); cpu->r16 = swap32(cpu->r16); cpu->r17 = swap32(cpu->r17); cpu->r18 = swap32(cpu->r18); cpu->r19 = swap32(cpu->r19); cpu->r20 = swap32(cpu->r20); cpu->r21 = swap32(cpu->r21); cpu->r22 = swap32(cpu->r22); cpu->r23 = swap32(cpu->r23); cpu->r24 = swap32(cpu->r24); cpu->r25 = swap32(cpu->r25); cpu->r26 = swap32(cpu->r26); cpu->r27 = swap32(cpu->r27); cpu->r28 = swap32(cpu->r28); cpu->r29 = swap32(cpu->r29); cpu->r30 = swap32(cpu->r30); cpu->r31 = swap32(cpu->r31); cpu->xip = swap32(cpu->xip); cpu->xip_in_bd = swap32(cpu->xip_in_bd); cpu->nip = swap32(cpu->nip); } //-------------------------------------------------------------------------- static void swap_m88k_thread_state_xrf_t(m88k_thread_state_xrf_t *fpu) { struct swapped_m88k_fpsr { union { struct { unsigned afinx:BIT_WIDTH(0); unsigned afovf:BIT_WIDTH(1); unsigned afunf:BIT_WIDTH(2); unsigned afdvz:BIT_WIDTH(3); unsigned afinv:BIT_WIDTH(4); unsigned :BITS_WIDTH(15,5); unsigned xmod :BIT_WIDTH(16); unsigned :BITS_WIDTH(31,17); } fields; uint32 word; } u; } ssr; struct swapped_m88k_fpcr { union { struct { unsigned efinx:BIT_WIDTH(0); unsigned efovf:BIT_WIDTH(1); unsigned efunf:BIT_WIDTH(2); unsigned efdvz:BIT_WIDTH(3); unsigned efinv:BIT_WIDTH(4); unsigned :BITS_WIDTH(13,5); m88k_fpcr_rm_t rm:BITS_WIDTH(15,14); unsigned :BITS_WIDTH(31,16); } fields; uint32 word; } u; } scr; fpu->x1.x[0] = swap32(fpu->x1.x[0]); fpu->x1.x[1] = swap32(fpu->x1.x[1]); fpu->x1.x[2] = swap32(fpu->x1.x[2]); fpu->x1.x[3] = swap32(fpu->x1.x[3]); fpu->x2.x[0] = swap32(fpu->x2.x[0]); fpu->x2.x[1] = swap32(fpu->x2.x[1]); fpu->x2.x[2] = swap32(fpu->x2.x[2]); fpu->x2.x[3] = swap32(fpu->x2.x[3]); fpu->x3.x[0] = swap32(fpu->x3.x[0]); fpu->x3.x[1] = swap32(fpu->x3.x[1]); fpu->x3.x[2] = swap32(fpu->x3.x[2]); fpu->x3.x[3] = swap32(fpu->x3.x[3]); fpu->x4.x[0] = swap32(fpu->x4.x[0]); fpu->x4.x[1] = swap32(fpu->x4.x[1]); fpu->x4.x[2] = swap32(fpu->x4.x[2]); fpu->x4.x[3] = swap32(fpu->x4.x[3]); fpu->x5.x[0] = swap32(fpu->x5.x[0]); fpu->x5.x[1] = swap32(fpu->x5.x[1]); fpu->x5.x[2] = swap32(fpu->x5.x[2]); fpu->x5.x[3] = swap32(fpu->x5.x[3]); fpu->x6.x[0] = swap32(fpu->x6.x[0]); fpu->x6.x[1] = swap32(fpu->x6.x[1]); fpu->x6.x[2] = swap32(fpu->x6.x[2]); fpu->x6.x[3] = swap32(fpu->x6.x[3]); fpu->x7.x[0] = swap32(fpu->x7.x[0]); fpu->x7.x[1] = swap32(fpu->x7.x[1]); fpu->x7.x[2] = swap32(fpu->x7.x[2]); fpu->x7.x[3] = swap32(fpu->x7.x[3]); fpu->x8.x[0] = swap32(fpu->x8.x[0]); fpu->x8.x[1] = swap32(fpu->x8.x[1]); fpu->x8.x[2] = swap32(fpu->x8.x[2]); fpu->x8.x[3] = swap32(fpu->x8.x[3]); fpu->x9.x[0] = swap32(fpu->x9.x[0]); fpu->x9.x[1] = swap32(fpu->x9.x[1]); fpu->x9.x[2] = swap32(fpu->x9.x[2]); fpu->x9.x[3] = swap32(fpu->x9.x[3]); fpu->x10.x[0] = swap32(fpu->x10.x[0]); fpu->x10.x[1] = swap32(fpu->x10.x[1]); fpu->x10.x[2] = swap32(fpu->x10.x[2]); fpu->x10.x[3] = swap32(fpu->x10.x[3]); fpu->x11.x[0] = swap32(fpu->x11.x[0]); fpu->x11.x[1] = swap32(fpu->x11.x[1]); fpu->x11.x[2] = swap32(fpu->x11.x[2]); fpu->x11.x[3] = swap32(fpu->x11.x[3]); fpu->x12.x[0] = swap32(fpu->x12.x[0]); fpu->x12.x[1] = swap32(fpu->x12.x[1]); fpu->x12.x[2] = swap32(fpu->x12.x[2]); fpu->x12.x[3] = swap32(fpu->x12.x[3]); fpu->x13.x[0] = swap32(fpu->x13.x[0]); fpu->x13.x[1] = swap32(fpu->x13.x[1]); fpu->x13.x[2] = swap32(fpu->x13.x[2]); fpu->x13.x[3] = swap32(fpu->x13.x[3]); fpu->x14.x[0] = swap32(fpu->x14.x[0]); fpu->x14.x[1] = swap32(fpu->x14.x[1]); fpu->x14.x[2] = swap32(fpu->x14.x[2]); fpu->x14.x[3] = swap32(fpu->x14.x[3]); fpu->x15.x[0] = swap32(fpu->x15.x[0]); fpu->x15.x[1] = swap32(fpu->x15.x[1]); fpu->x15.x[2] = swap32(fpu->x15.x[2]); fpu->x15.x[3] = swap32(fpu->x15.x[3]); fpu->x16.x[0] = swap32(fpu->x16.x[0]); fpu->x16.x[1] = swap32(fpu->x16.x[1]); fpu->x16.x[2] = swap32(fpu->x16.x[2]); fpu->x16.x[3] = swap32(fpu->x16.x[3]); fpu->x17.x[0] = swap32(fpu->x17.x[0]); fpu->x17.x[1] = swap32(fpu->x17.x[1]); fpu->x17.x[2] = swap32(fpu->x17.x[2]); fpu->x17.x[3] = swap32(fpu->x17.x[3]); fpu->x18.x[0] = swap32(fpu->x18.x[0]); fpu->x18.x[1] = swap32(fpu->x18.x[1]); fpu->x18.x[2] = swap32(fpu->x18.x[2]); fpu->x18.x[3] = swap32(fpu->x18.x[3]); fpu->x19.x[0] = swap32(fpu->x19.x[0]); fpu->x19.x[1] = swap32(fpu->x19.x[1]); fpu->x19.x[2] = swap32(fpu->x19.x[2]); fpu->x19.x[3] = swap32(fpu->x19.x[3]); fpu->x20.x[0] = swap32(fpu->x20.x[0]); fpu->x20.x[1] = swap32(fpu->x20.x[1]); fpu->x20.x[2] = swap32(fpu->x20.x[2]); fpu->x20.x[3] = swap32(fpu->x20.x[3]); fpu->x21.x[0] = swap32(fpu->x21.x[0]); fpu->x21.x[1] = swap32(fpu->x21.x[1]); fpu->x21.x[2] = swap32(fpu->x21.x[2]); fpu->x21.x[3] = swap32(fpu->x21.x[3]); fpu->x22.x[0] = swap32(fpu->x22.x[0]); fpu->x22.x[1] = swap32(fpu->x22.x[1]); fpu->x22.x[2] = swap32(fpu->x22.x[2]); fpu->x22.x[3] = swap32(fpu->x22.x[3]); fpu->x23.x[0] = swap32(fpu->x23.x[0]); fpu->x23.x[1] = swap32(fpu->x23.x[1]); fpu->x23.x[2] = swap32(fpu->x23.x[2]); fpu->x23.x[3] = swap32(fpu->x23.x[3]); fpu->x24.x[0] = swap32(fpu->x24.x[0]); fpu->x24.x[1] = swap32(fpu->x24.x[1]); fpu->x24.x[2] = swap32(fpu->x24.x[2]); fpu->x24.x[3] = swap32(fpu->x24.x[3]); fpu->x25.x[0] = swap32(fpu->x25.x[0]); fpu->x25.x[1] = swap32(fpu->x25.x[1]); fpu->x25.x[2] = swap32(fpu->x25.x[2]); fpu->x25.x[3] = swap32(fpu->x25.x[3]); fpu->x26.x[0] = swap32(fpu->x26.x[0]); fpu->x26.x[1] = swap32(fpu->x26.x[1]); fpu->x26.x[2] = swap32(fpu->x26.x[2]); fpu->x26.x[3] = swap32(fpu->x26.x[3]); fpu->x27.x[0] = swap32(fpu->x27.x[0]); fpu->x27.x[1] = swap32(fpu->x27.x[1]); fpu->x27.x[2] = swap32(fpu->x27.x[2]); fpu->x27.x[3] = swap32(fpu->x27.x[3]); fpu->x28.x[0] = swap32(fpu->x28.x[0]); fpu->x28.x[1] = swap32(fpu->x28.x[1]); fpu->x28.x[2] = swap32(fpu->x28.x[2]); fpu->x28.x[3] = swap32(fpu->x28.x[3]); fpu->x29.x[0] = swap32(fpu->x29.x[0]); fpu->x29.x[1] = swap32(fpu->x29.x[1]); fpu->x29.x[2] = swap32(fpu->x29.x[2]); fpu->x29.x[3] = swap32(fpu->x29.x[3]); fpu->x30.x[0] = swap32(fpu->x30.x[0]); fpu->x30.x[1] = swap32(fpu->x30.x[1]); fpu->x30.x[2] = swap32(fpu->x30.x[2]); fpu->x30.x[3] = swap32(fpu->x30.x[3]); fpu->x31.x[0] = swap32(fpu->x31.x[0]); fpu->x31.x[1] = swap32(fpu->x31.x[1]); fpu->x31.x[2] = swap32(fpu->x31.x[2]); fpu->x31.x[3] = swap32(fpu->x31.x[3]); if ( !mf ) { memcpy(&ssr, &(fpu->fpsr), sizeof(struct swapped_m88k_fpsr)); ssr.u.word = swap32(ssr.u.word); fpu->fpsr.afinx = ssr.u.fields.afinx; fpu->fpsr.afovf = ssr.u.fields.afovf; fpu->fpsr.afunf = ssr.u.fields.afunf; fpu->fpsr.afdvz = ssr.u.fields.afdvz; fpu->fpsr.afinv = ssr.u.fields.afinv; fpu->fpsr.xmod = ssr.u.fields.xmod; memcpy(&scr, &(fpu->fpcr), sizeof(struct swapped_m88k_fpcr)); scr.u.word = swap32(scr.u.word); fpu->fpcr.efinx = scr.u.fields.efinx; fpu->fpcr.efovf = scr.u.fields.efovf; fpu->fpcr.efunf = scr.u.fields.efunf; fpu->fpcr.efdvz = scr.u.fields.efdvz; fpu->fpcr.efinv = scr.u.fields.efinv; fpu->fpcr.rm = scr.u.fields.rm; } else { ssr.u.fields.afinx = fpu->fpsr.afinx; ssr.u.fields.afovf = fpu->fpsr.afovf; ssr.u.fields.afunf = fpu->fpsr.afunf; ssr.u.fields.afdvz = fpu->fpsr.afdvz; ssr.u.fields.afinv = fpu->fpsr.afinv; ssr.u.fields.xmod = fpu->fpsr.xmod; ssr.u.word = swap32(ssr.u.word); memcpy(&(fpu->fpsr), &ssr, sizeof(struct swapped_m88k_fpsr)); scr.u.fields.efinx = fpu->fpcr.efinx; scr.u.fields.efovf = fpu->fpcr.efovf; scr.u.fields.efunf = fpu->fpcr.efunf; scr.u.fields.efdvz = fpu->fpcr.efdvz; scr.u.fields.efinv = fpu->fpcr.efinv; scr.u.fields.rm = fpu->fpcr.rm; scr.u.word = swap32(scr.u.word); memcpy(&(fpu->fpcr), &scr, sizeof(struct swapped_m88k_fpcr)); } } //-------------------------------------------------------------------------- static void swap_m88k_thread_state_user_t(m88k_thread_state_user_t *user) { user->user = swap32(user->user); } //-------------------------------------------------------------------------- static void swap_m88110_thread_state_impl_t(m88110_thread_state_impl_t *spu) { uint32 i; struct swapped_m88110_bp_ctrl { union { struct { unsigned v:BIT_WIDTH(0); m88110_match_t addr_match:BITS_WIDTH(12,1); unsigned :BITS_WIDTH(26,13); unsigned rwm:BIT_WIDTH(27); unsigned rw:BIT_WIDTH(28); unsigned :BITS_WIDTH(31,29); } fields; uint32 word; } u; } sbpc; struct swap_m88110_psr { union { struct { unsigned :BITS_WIDTH(1,0); unsigned mxm_dis:BIT_WIDTH(2); unsigned sfu1dis:BIT_WIDTH(3); unsigned :BITS_WIDTH(22,4); unsigned trace :BIT_WIDTH(23); unsigned :BIT_WIDTH(24); unsigned sm :BIT_WIDTH(25); unsigned sgn_imd:BIT_WIDTH(26); unsigned :BIT_WIDTH(27); unsigned c :BIT_WIDTH(28); unsigned se :BIT_WIDTH(29); unsigned le :BIT_WIDTH(30); unsigned supr :BIT_WIDTH(31); } fields; uint32 word; } u; } spsr; struct swapped_m88110_fp_trap_status { union { struct { unsigned efinx:BIT_WIDTH(0); unsigned efovf:BIT_WIDTH(1); unsigned efunf:BIT_WIDTH(2); unsigned efdvz:BIT_WIDTH(3); unsigned efinv:BIT_WIDTH(4); unsigned priv:BIT_WIDTH(5); unsigned unimp:BIT_WIDTH(6); unsigned int:BIT_WIDTH(7); unsigned sfu1_disabled:BIT_WIDTH(8); unsigned :BITS_WIDTH(13,9); m88110_iresult_size_t iresult_size:BITS_WIDTH(15,14); unsigned :BITS_WIDTH(31,16); } fields; uint32 word; } u; } sfps; if ( !mf ) { for ( i = 0; i < M88110_N_DATA_BP; i++ ) { spu->data_bp[i].addr = swap32(spu->data_bp[i].addr); memcpy(&sbpc, &(spu->data_bp[i].ctrl), sizeof(struct swapped_m88110_bp_ctrl)); sbpc.u.word = swap32(sbpc.u.word); spu->data_bp[i].ctrl.v = sbpc.u.fields.v; spu->data_bp[i].ctrl.addr_match = sbpc.u.fields.addr_match; spu->data_bp[i].ctrl.rwm = sbpc.u.fields.rwm; spu->data_bp[i].ctrl.rw = sbpc.u.fields.rw; } memcpy(&spsr, &(spu->psr), sizeof(struct swap_m88110_psr)); spsr.u.word = swap32(spsr.u.word); spu->psr.mxm_dis = spsr.u.fields.mxm_dis; spu->psr.sfu1dis = spsr.u.fields.sfu1dis; spu->psr.trace = spsr.u.fields.trace; spu->psr.sm = spsr.u.fields.sm; spu->psr.sgn_imd = spsr.u.fields.sgn_imd; spu->psr.c = spsr.u.fields.c; spu->psr.se = spsr.u.fields.se; spu->psr.le = spsr.u.fields.le; spu->psr.supr = spsr.u.fields.supr; memcpy(&sfps, &(spu->fp_trap_status), sizeof(struct swapped_m88110_fp_trap_status)); sfps.u.word = swap32(sfps.u.word); spu->fp_trap_status.efinx = sfps.u.fields.efinx; spu->fp_trap_status.efovf = sfps.u.fields.efovf; spu->fp_trap_status.efunf = sfps.u.fields.efunf; spu->fp_trap_status.efdvz = sfps.u.fields.efdvz; spu->fp_trap_status.efinv = sfps.u.fields.efinv; spu->fp_trap_status.priv = sfps.u.fields.priv; spu->fp_trap_status.unimp = sfps.u.fields.unimp; spu->fp_trap_status.sfu1_disabled = sfps.u.fields.sfu1_disabled; spu->fp_trap_status.iresult_size = sfps.u.fields.iresult_size; } else { for ( i = 0; i < M88110_N_DATA_BP; i++ ) { spu->data_bp[i].addr = swap32(spu->data_bp[i].addr); sbpc.u.fields.v = spu->data_bp[i].ctrl.v; sbpc.u.fields.addr_match = spu->data_bp[i].ctrl.addr_match; sbpc.u.fields.rwm = spu->data_bp[i].ctrl.rwm; sbpc.u.fields.rw = spu->data_bp[i].ctrl.rw; sbpc.u.word = swap32(sbpc.u.word); memcpy(&(spu->data_bp[i].ctrl), &sbpc, sizeof(struct swapped_m88110_bp_ctrl)); } spsr.u.fields.mxm_dis = spu->psr.mxm_dis; spsr.u.fields.sfu1dis = spu->psr.sfu1dis; spsr.u.fields.trace = spu->psr.trace; spsr.u.fields.sm = spu->psr.sm; spsr.u.fields.sgn_imd = spu->psr.sgn_imd; spsr.u.fields.c = spu->psr.c; spsr.u.fields.se = spu->psr.se; spsr.u.fields.le = spu->psr.le; spsr.u.fields.supr = spu->psr.supr; spsr.u.word = swap32(spsr.u.word); memcpy(&(spu->psr), &spsr, sizeof(struct swap_m88110_psr)); sfps.u.fields.efinx = spu->fp_trap_status.efinx; sfps.u.fields.efovf = spu->fp_trap_status.efovf; sfps.u.fields.efunf = spu->fp_trap_status.efunf; sfps.u.fields.efdvz = spu->fp_trap_status.efdvz; sfps.u.fields.efinv = spu->fp_trap_status.efinv; sfps.u.fields.priv = spu->fp_trap_status.priv; sfps.u.fields.unimp = spu->fp_trap_status.unimp; sfps.u.fields.sfu1_disabled = spu->fp_trap_status.sfu1_disabled; sfps.u.fields.iresult_size = spu->fp_trap_status.iresult_size; sfps.u.word = swap32(sfps.u.word); memcpy(&(spu->fp_trap_status), &sfps, sizeof(struct swapped_m88110_fp_trap_status)); } spu->intermediate_result.x[0] = swap32(spu->intermediate_result.x[0]); spu->intermediate_result.x[1] = swap32(spu->intermediate_result.x[1]); spu->intermediate_result.x[2] = swap32(spu->intermediate_result.x[2]); spu->intermediate_result.x[3] = swap32(spu->intermediate_result.x[3]); } //-------------------------------------------------------------------------- static void swap_i860_thread_state_regs(struct i860_thread_state_regs *cpu) { uint32 i; for ( i = 0; i < 31; i++ ) cpu->ireg[i] = swap32(cpu->ireg[i]); for ( i = 0; i < 30; i++ ) cpu->freg[i] = swap32(cpu->freg[i]); cpu->psr = swap32(cpu->psr); cpu->epsr = swap32(cpu->epsr); cpu->db = swap32(cpu->db); cpu->pc = swap32(cpu->pc); cpu->_padding_ = swap32(cpu->_padding_); cpu->Mres3 = SWAP_DOUBLE(cpu->Mres3); cpu->Ares3 = SWAP_DOUBLE(cpu->Ares3); cpu->Mres2 = SWAP_DOUBLE(cpu->Mres2); cpu->Ares2 = SWAP_DOUBLE(cpu->Ares2); cpu->Mres1 = SWAP_DOUBLE(cpu->Mres1); cpu->Ares1 = SWAP_DOUBLE(cpu->Ares1); cpu->Ires1 = SWAP_DOUBLE(cpu->Ires1); cpu->Lres3m = SWAP_DOUBLE(cpu->Lres3m); cpu->Lres2m = SWAP_DOUBLE(cpu->Lres2m); cpu->Lres1m = SWAP_DOUBLE(cpu->Lres1m); cpu->KR = SWAP_DOUBLE(cpu->KR); cpu->KI = SWAP_DOUBLE(cpu->KI); cpu->T = SWAP_DOUBLE(cpu->T); cpu->Fsr3 = swap32(cpu->Fsr3); cpu->Fsr2 = swap32(cpu->Fsr2); cpu->Fsr1 = swap32(cpu->Fsr1); cpu->Mergelo32 = swap32(cpu->Mergelo32); cpu->Mergehi32 = swap32(cpu->Mergehi32); } */ #if defined(EFD_COMPILE) //-------------------------------------------------------------------------- static void swap_arm_thread_state(arm_thread_state_t *cpu) { for ( int i = 0; i < 13; i++ ) cpu->__r[i] = swap32(cpu->__r[i]); cpu->__sp = swap32(cpu->__sp); cpu->__lr = swap32(cpu->__lr); cpu->__pc = swap32(cpu->__pc); cpu->__cpsr = swap32(cpu->__cpsr); } //-------------------------------------------------------------------------- static void swap_arm_thread_state64(arm_thread_state64_t *cpu) { for ( int i = 0; i < 29; i++ ) cpu->__x[i] = swap64(cpu->__x[i]); cpu->__fp = swap64(cpu->__fp); cpu->__lr = swap64(cpu->__lr); cpu->__sp = swap64(cpu->__sp); cpu->__pc = swap64(cpu->__pc); cpu->__cpsr = swap32(cpu->__cpsr); } //-------------------------------------------------------------------------- static void swap_i386_thread_state(i386_thread_state_t *cpu) { cpu->__eax = swap32(cpu->__eax); cpu->__ebx = swap32(cpu->__ebx); cpu->__ecx = swap32(cpu->__ecx); cpu->__edx = swap32(cpu->__edx); cpu->__edi = swap32(cpu->__edi); cpu->__esi = swap32(cpu->__esi); cpu->__ebp = swap32(cpu->__ebp); cpu->__esp = swap32(cpu->__esp); cpu->__ss = swap32(cpu->__ss); cpu->__eflags = swap32(cpu->__eflags); cpu->__eip = swap32(cpu->__eip); cpu->__cs = swap32(cpu->__cs); cpu->__ds = swap32(cpu->__ds); cpu->__es = swap32(cpu->__es); cpu->__fs = swap32(cpu->__fs); cpu->__gs = swap32(cpu->__gs); } //-------------------------------------------------------------------------- static void swap_x86_thread_state64(x86_thread_state64_t *cpu) { cpu->__rax = swap64(cpu->__rax); cpu->__rbx = swap64(cpu->__rbx); cpu->__rcx = swap64(cpu->__rcx); cpu->__rdx = swap64(cpu->__rdx); cpu->__rdi = swap64(cpu->__rdi); cpu->__rsi = swap64(cpu->__rsi); cpu->__rbp = swap64(cpu->__rbp); cpu->__rsp = swap64(cpu->__rsp); cpu->__rflags = swap64(cpu->__rflags); cpu->__rip = swap64(cpu->__rip); cpu->__r8 = swap64(cpu->__r8); cpu->__r9 = swap64(cpu->__r9); cpu->__r10 = swap64(cpu->__r10); cpu->__r11 = swap64(cpu->__r11); cpu->__r12 = swap64(cpu->__r12); cpu->__r13 = swap64(cpu->__r13); cpu->__r14 = swap64(cpu->__r14); cpu->__r15 = swap64(cpu->__r15); cpu->__cs = swap64(cpu->__cs); cpu->__fs = swap64(cpu->__fs); cpu->__gs = swap64(cpu->__gs); } #endif //-------------------------------------------------------------------------- #if 0 // !defined(EFD_COMPILE) && !defined(LOADER_COMPILE) static void swap_x86_state_hdr(x86_state_hdr_t *hdr) { hdr->flavor = swap32(hdr->flavor); hdr->count = swap32(hdr->count); } //-------------------------------------------------------------------------- static void swap_x86_float_state64(x86_float_state64_t *fpu) { struct swapped_fp_control { union { struct { unsigned short :3, /*inf*/ :1, rc :2, pc :2, :2, precis :1, undfl :1, ovrfl :1, zdiv :1, denorm :1, invalid :1; } fields; unsigned short half; } u; } sfpc; struct swapped_fp_status { union { struct { unsigned short busy :1, c3 :1, tos :3, c2 :1, c1 :1, c0 :1, errsumm :1, stkflt :1, precis :1, undfl :1, ovrfl :1, zdiv :1, denorm :1, invalid :1; } fields; unsigned short half; } u; } sfps; fpu->__fpu_reserved[0] = swap32(fpu->__fpu_reserved[0]); fpu->__fpu_reserved[1] = swap32(fpu->__fpu_reserved[1]); if ( !mf ) { memcpy(&sfpc, &(fpu->__fpu_fcw), sizeof(struct swapped_fp_control)); sfpc.u.half = swap16(sfpc.u.half); fpu->__fpu_fcw.__rc = sfpc.u.fields.rc; fpu->__fpu_fcw.__pc = sfpc.u.fields.pc; fpu->__fpu_fcw.__precis = sfpc.u.fields.precis; fpu->__fpu_fcw.__undfl = sfpc.u.fields.undfl; fpu->__fpu_fcw.__ovrfl = sfpc.u.fields.ovrfl; fpu->__fpu_fcw.__zdiv = sfpc.u.fields.zdiv; fpu->__fpu_fcw.__denorm = sfpc.u.fields.denorm; fpu->__fpu_fcw.__invalid = sfpc.u.fields.invalid; memcpy(&sfps, &(fpu->__fpu_fsw), sizeof(struct swapped_fp_status)); sfps.u.half = swap16(sfps.u.half); fpu->__fpu_fsw.__busy = sfps.u.fields.busy; fpu->__fpu_fsw.__c3 = sfps.u.fields.c3; fpu->__fpu_fsw.__tos = sfps.u.fields.tos; fpu->__fpu_fsw.__c2 = sfps.u.fields.c2; fpu->__fpu_fsw.__c1 = sfps.u.fields.c1; fpu->__fpu_fsw.__c0 = sfps.u.fields.c0; fpu->__fpu_fsw.__errsumm = sfps.u.fields.errsumm; fpu->__fpu_fsw.__stkflt = sfps.u.fields.stkflt; fpu->__fpu_fsw.__precis = sfps.u.fields.precis; fpu->__fpu_fsw.__undfl = sfps.u.fields.undfl; fpu->__fpu_fsw.__ovrfl = sfps.u.fields.ovrfl; fpu->__fpu_fsw.__zdiv = sfps.u.fields.zdiv; fpu->__fpu_fsw.__denorm = sfps.u.fields.denorm; fpu->__fpu_fsw.__invalid = sfps.u.fields.invalid; } else { sfpc.u.fields.rc = fpu->__fpu_fcw.__rc; sfpc.u.fields.pc = fpu->__fpu_fcw.__pc; sfpc.u.fields.precis = fpu->__fpu_fcw.__precis; sfpc.u.fields.undfl = fpu->__fpu_fcw.__undfl; sfpc.u.fields.ovrfl = fpu->__fpu_fcw.__ovrfl; sfpc.u.fields.zdiv = fpu->__fpu_fcw.__zdiv; sfpc.u.fields.denorm = fpu->__fpu_fcw.__denorm; sfpc.u.fields.invalid = fpu->__fpu_fcw.__invalid; sfpc.u.half = swap16(sfpc.u.half); memcpy(&(fpu->__fpu_fcw), &sfpc, sizeof(struct swapped_fp_control)); sfps.u.fields.busy = fpu->__fpu_fsw.__busy; sfps.u.fields.c3 = fpu->__fpu_fsw.__c3; sfps.u.fields.tos = fpu->__fpu_fsw.__tos; sfps.u.fields.c2 = fpu->__fpu_fsw.__c2; sfps.u.fields.c1 = fpu->__fpu_fsw.__c1; sfps.u.fields.c0 = fpu->__fpu_fsw.__c0; sfps.u.fields.errsumm = fpu->__fpu_fsw.__errsumm; sfps.u.fields.stkflt = fpu->__fpu_fsw.__stkflt; sfps.u.fields.precis = fpu->__fpu_fsw.__precis; sfps.u.fields.undfl = fpu->__fpu_fsw.__undfl; sfps.u.fields.ovrfl = fpu->__fpu_fsw.__ovrfl; sfps.u.fields.zdiv = fpu->__fpu_fsw.__zdiv; sfps.u.fields.denorm = fpu->__fpu_fsw.__denorm; sfps.u.fields.invalid = fpu->__fpu_fsw.__invalid; sfps.u.half = swap16(sfps.u.half); memcpy(&(fpu->__fpu_fsw), &sfps, sizeof(struct swapped_fp_status)); } fpu->__fpu_fop = swap16(fpu->__fpu_fop); fpu->__fpu_ip = swap32(fpu->__fpu_ip); fpu->__fpu_cs = swap16(fpu->__fpu_cs); fpu->__fpu_rsrv2 = swap16(fpu->__fpu_rsrv2); fpu->__fpu_dp = swap32(fpu->__fpu_dp); fpu->__fpu_ds = swap16(fpu->__fpu_ds); fpu->__fpu_rsrv3 = swap16(fpu->__fpu_rsrv3); fpu->__fpu_mxcsr = swap32(fpu->__fpu_mxcsr); fpu->__fpu_mxcsrmask = swap32(fpu->__fpu_mxcsrmask); fpu->__fpu_reserved1 = swap32(fpu->__fpu_reserved1); } //-------------------------------------------------------------------------- static void swap_x86_exception_state64(x86_exception_state64_t *exc) { exc->__trapno = swap32(exc->__trapno); exc->__err = swap32(exc->__err); exc->__faultvaddr = swap64(exc->__faultvaddr); } //-------------------------------------------------------------------------- static void swap_x86_debug_state32(x86_debug_state32_t *debug) { debug->__dr0 = swap32(debug->__dr0); debug->__dr1 = swap32(debug->__dr1); debug->__dr2 = swap32(debug->__dr2); debug->__dr3 = swap32(debug->__dr3); debug->__dr4 = swap32(debug->__dr4); debug->__dr5 = swap32(debug->__dr5); debug->__dr6 = swap32(debug->__dr6); debug->__dr7 = swap32(debug->__dr7); } //-------------------------------------------------------------------------- static void swap_x86_debug_state64(x86_debug_state64_t *debug) { debug->__dr0 = swap64(debug->__dr0); debug->__dr1 = swap64(debug->__dr1); debug->__dr2 = swap64(debug->__dr2); debug->__dr3 = swap64(debug->__dr3); debug->__dr4 = swap64(debug->__dr4); debug->__dr5 = swap64(debug->__dr5); debug->__dr6 = swap64(debug->__dr6); debug->__dr7 = swap64(debug->__dr7); } #endif // !EFD_COMPILE && !LOADER_COMPILE /* current i386 thread states */ #if i386_THREAD_STATE == 1 void swap_i386_float_state(i386_float_state_t *fpu) { #ifndef i386_EXCEPTION_STATE_COUNT /* this routine does nothing as their are currently no non-byte fields */ #else /* !defined(i386_EXCEPTION_STATE_COUNT) */ struct swapped_fp_control { union { struct { unsigned short :3, /*inf*/ :1, rc :2, pc :2, :2, precis :1, undfl :1, ovrfl :1, zdiv :1, denorm :1, invalid :1; } fields; unsigned short half; } u; } sfpc; struct swapped_fp_status { union { struct { unsigned short busy :1, c3 :1, tos :3, c2 :1, c1 :1, c0 :1, errsumm :1, stkflt :1, precis :1, undfl :1, ovrfl :1, zdiv :1, denorm :1, invalid :1; } fields; unsigned short half; } u; } sfps; // enum NXByteOrder host_byte_sex; fpu->__fpu_reserved[0] = swap32(fpu->__fpu_reserved[0]); fpu->__fpu_reserved[1] = swap32(fpu->__fpu_reserved[1]); sfpc.u.fields.rc = fpu->__fpu_fcw.__rc; sfpc.u.fields.pc = fpu->__fpu_fcw.__pc; sfpc.u.fields.precis = fpu->__fpu_fcw.__precis; sfpc.u.fields.undfl = fpu->__fpu_fcw.__undfl; sfpc.u.fields.ovrfl = fpu->__fpu_fcw.__ovrfl; sfpc.u.fields.zdiv = fpu->__fpu_fcw.__zdiv; sfpc.u.fields.denorm = fpu->__fpu_fcw.__denorm; sfpc.u.fields.invalid = fpu->__fpu_fcw.__invalid; sfpc.u.half = swap16(sfpc.u.half); memcpy(&(fpu->__fpu_fcw), &sfpc, sizeof(struct swapped_fp_control)); //-V512 A call of the 'memcpy' function will lead to underflow of the buffer sfps.u.fields.busy = fpu->__fpu_fsw.__busy; sfps.u.fields.c3 = fpu->__fpu_fsw.__c3; sfps.u.fields.tos = fpu->__fpu_fsw.__tos; sfps.u.fields.c2 = fpu->__fpu_fsw.__c2; sfps.u.fields.c1 = fpu->__fpu_fsw.__c1; sfps.u.fields.c0 = fpu->__fpu_fsw.__c0; sfps.u.fields.errsumm = fpu->__fpu_fsw.__errsumm; sfps.u.fields.stkflt = fpu->__fpu_fsw.__stkflt; sfps.u.fields.precis = fpu->__fpu_fsw.__precis; sfps.u.fields.undfl = fpu->__fpu_fsw.__undfl; sfps.u.fields.ovrfl = fpu->__fpu_fsw.__ovrfl; sfps.u.fields.zdiv = fpu->__fpu_fsw.__zdiv; sfps.u.fields.denorm = fpu->__fpu_fsw.__denorm; sfps.u.fields.invalid = fpu->__fpu_fsw.__invalid; sfps.u.half = swap16(sfps.u.half); memcpy(&(fpu->__fpu_fsw), &sfps, sizeof(struct swapped_fp_status)); fpu->__fpu_fop = swap16(fpu->__fpu_fop); fpu->__fpu_ip = swap32(fpu->__fpu_ip); fpu->__fpu_cs = swap16(fpu->__fpu_cs); fpu->__fpu_rsrv2 = swap16(fpu->__fpu_rsrv2); fpu->__fpu_dp = swap32(fpu->__fpu_dp); fpu->__fpu_ds = swap16(fpu->__fpu_ds); fpu->__fpu_rsrv3 = swap16(fpu->__fpu_rsrv3); fpu->__fpu_mxcsr = swap32(fpu->__fpu_mxcsr); fpu->__fpu_mxcsrmask = swap32(fpu->__fpu_mxcsrmask); fpu->__fpu_reserved1 = swap32(fpu->__fpu_reserved1); #endif /* !defined(i386_EXCEPTION_STATE_COUNT) */ } void swap_i386_exception_state(i386_exception_state_t *exc) { exc->__trapno = swap32(exc->__trapno); exc->__err = swap32(exc->__err); exc->__faultvaddr = swap32(exc->__faultvaddr); } #endif /* i386_THREAD_STATE == 1 */ //-------------------------------------------------------------------------- /* static void swap_hppa_integer_thread_state(struct hp_pa_integer_thread_state *regs) { regs->ts_gr1 = swap32(regs->ts_gr1); regs->ts_gr2 = swap32(regs->ts_gr2); regs->ts_gr3 = swap32(regs->ts_gr3); regs->ts_gr4 = swap32(regs->ts_gr4); regs->ts_gr5 = swap32(regs->ts_gr5); regs->ts_gr6 = swap32(regs->ts_gr6); regs->ts_gr7 = swap32(regs->ts_gr7); regs->ts_gr8 = swap32(regs->ts_gr8); regs->ts_gr9 = swap32(regs->ts_gr9); regs->ts_gr10 = swap32(regs->ts_gr10); regs->ts_gr11 = swap32(regs->ts_gr11); regs->ts_gr12 = swap32(regs->ts_gr12); regs->ts_gr13 = swap32(regs->ts_gr13); regs->ts_gr14 = swap32(regs->ts_gr14); regs->ts_gr15 = swap32(regs->ts_gr15); regs->ts_gr16 = swap32(regs->ts_gr16); regs->ts_gr17 = swap32(regs->ts_gr17); regs->ts_gr18 = swap32(regs->ts_gr18); regs->ts_gr19 = swap32(regs->ts_gr19); regs->ts_gr20 = swap32(regs->ts_gr20); regs->ts_gr21 = swap32(regs->ts_gr21); regs->ts_gr22 = swap32(regs->ts_gr22); regs->ts_gr23 = swap32(regs->ts_gr23); regs->ts_gr24 = swap32(regs->ts_gr24); regs->ts_gr25 = swap32(regs->ts_gr25); regs->ts_gr26 = swap32(regs->ts_gr26); regs->ts_gr27 = swap32(regs->ts_gr27); regs->ts_gr28 = swap32(regs->ts_gr28); regs->ts_gr29 = swap32(regs->ts_gr29); regs->ts_gr30 = swap32(regs->ts_gr30); regs->ts_gr31 = swap32(regs->ts_gr31); regs->ts_sr0 = swap32(regs->ts_sr0); regs->ts_sr1 = swap32(regs->ts_sr1); regs->ts_sr2 = swap32(regs->ts_sr2); regs->ts_sr3 = swap32(regs->ts_sr3); regs->ts_sar = swap32(regs->ts_sar); } //-------------------------------------------------------------------------- static void swap_hppa_frame_thread_state( struct hp_pa_frame_thread_state *frame) { frame->ts_pcsq_front = swap32(frame->ts_pcsq_front); frame->ts_pcsq_back = swap32(frame->ts_pcsq_back); frame->ts_pcoq_front = swap32(frame->ts_pcoq_front); frame->ts_pcoq_back = swap32(frame->ts_pcoq_back); frame->ts_psw = swap32(frame->ts_psw); frame->ts_unaligned_faults = swap32(frame->ts_unaligned_faults); frame->ts_fault_address = swap32(frame->ts_fault_address); frame->ts_step_range_start = swap32(frame->ts_step_range_start); frame->ts_step_range_stop = swap32(frame->ts_step_range_stop); } //-------------------------------------------------------------------------- static void swap_hppa_fp_thread_state( struct hp_pa_fp_thread_state *fp) { fp->ts_fp0 = SWAP_DOUBLE(fp->ts_fp0); fp->ts_fp1 = SWAP_DOUBLE(fp->ts_fp1); fp->ts_fp2 = SWAP_DOUBLE(fp->ts_fp2); fp->ts_fp3 = SWAP_DOUBLE(fp->ts_fp3); fp->ts_fp4 = SWAP_DOUBLE(fp->ts_fp4); fp->ts_fp5 = SWAP_DOUBLE(fp->ts_fp5); fp->ts_fp6 = SWAP_DOUBLE(fp->ts_fp6); fp->ts_fp7 = SWAP_DOUBLE(fp->ts_fp7); fp->ts_fp8 = SWAP_DOUBLE(fp->ts_fp8); fp->ts_fp9 = SWAP_DOUBLE(fp->ts_fp9); fp->ts_fp10 = SWAP_DOUBLE(fp->ts_fp10); fp->ts_fp11 = SWAP_DOUBLE(fp->ts_fp11); fp->ts_fp12 = SWAP_DOUBLE(fp->ts_fp12); fp->ts_fp13 = SWAP_DOUBLE(fp->ts_fp13); fp->ts_fp14 = SWAP_DOUBLE(fp->ts_fp14); fp->ts_fp15 = SWAP_DOUBLE(fp->ts_fp15); fp->ts_fp16 = SWAP_DOUBLE(fp->ts_fp16); fp->ts_fp17 = SWAP_DOUBLE(fp->ts_fp17); fp->ts_fp18 = SWAP_DOUBLE(fp->ts_fp18); fp->ts_fp19 = SWAP_DOUBLE(fp->ts_fp19); fp->ts_fp20 = SWAP_DOUBLE(fp->ts_fp20); fp->ts_fp21 = SWAP_DOUBLE(fp->ts_fp21); fp->ts_fp22 = SWAP_DOUBLE(fp->ts_fp22); fp->ts_fp23 = SWAP_DOUBLE(fp->ts_fp23); fp->ts_fp24 = SWAP_DOUBLE(fp->ts_fp24); fp->ts_fp25 = SWAP_DOUBLE(fp->ts_fp25); fp->ts_fp26 = SWAP_DOUBLE(fp->ts_fp26); fp->ts_fp27 = SWAP_DOUBLE(fp->ts_fp27); fp->ts_fp28 = SWAP_DOUBLE(fp->ts_fp28); fp->ts_fp29 = SWAP_DOUBLE(fp->ts_fp29); fp->ts_fp30 = SWAP_DOUBLE(fp->ts_fp30); fp->ts_fp31 = SWAP_DOUBLE(fp->ts_fp31); } //-------------------------------------------------------------------------- static void swap_sparc_thread_state_regs(struct sparc_thread_state_regs *cpu) { struct swapped_psr { union { struct { unsigned int cwp:BITS_WIDTH(4,0), et:BIT_WIDTH(5), ps:BIT_WIDTH(6), s:BIT_WIDTH(7), pil:BITS_WIDTH(11,8), ef:BIT_WIDTH(12), ec:BIT_WIDTH(13), reserved:BITS_WIDTH(19,14), icc:BITS_WIDTH(23,20), ver:BITS_WIDTH(27,24), impl:BITS_WIDTH(31,28); } fields; unsigned int word; } u; } spsr; struct p_status *pr_status; cpu->regs.r_pc = swap32(cpu->regs.r_pc); cpu->regs.r_npc = swap32(cpu->regs.r_npc); cpu->regs.r_y = swap32(cpu->regs.r_y); cpu->regs.r_g1 = swap32(cpu->regs.r_g1); cpu->regs.r_g2 = swap32(cpu->regs.r_g2); cpu->regs.r_g3 = swap32(cpu->regs.r_g3); cpu->regs.r_g4 = swap32(cpu->regs.r_g4); cpu->regs.r_g5 = swap32(cpu->regs.r_g5); cpu->regs.r_g6 = swap32(cpu->regs.r_g6); cpu->regs.r_g7 = swap32(cpu->regs.r_g7); cpu->regs.r_o0 = swap32(cpu->regs.r_o0); cpu->regs.r_o1 = swap32(cpu->regs.r_o1); cpu->regs.r_o2 = swap32(cpu->regs.r_o2); cpu->regs.r_o3 = swap32(cpu->regs.r_o3); cpu->regs.r_o4 = swap32(cpu->regs.r_o4); cpu->regs.r_o5 = swap32(cpu->regs.r_o5); cpu->regs.r_o6 = swap32(cpu->regs.r_o6); cpu->regs.r_o7 = swap32(cpu->regs.r_o7); pr_status = (struct p_status *) &(cpu->regs.r_psr); if ( !mf ) { memcpy(&spsr, &(cpu->regs.r_psr), sizeof(struct swapped_psr)); spsr.u.word = swap32(spsr.u.word); pr_status->PSRREG.psr_bits.cwp = spsr.u.fields.cwp; pr_status->PSRREG.psr_bits.ps = spsr.u.fields.ps; pr_status->PSRREG.psr_bits.s = spsr.u.fields.s; pr_status->PSRREG.psr_bits.pil = spsr.u.fields.pil; pr_status->PSRREG.psr_bits.ef = spsr.u.fields.ef; pr_status->PSRREG.psr_bits.ec = spsr.u.fields.ec; pr_status->PSRREG.psr_bits.reserved = spsr.u.fields.reserved; pr_status->PSRREG.psr_bits.icc = spsr.u.fields.icc; pr_status->PSRREG.psr_bits.et = spsr.u.fields.ver; pr_status->PSRREG.psr_bits.impl = spsr.u.fields.impl; } else { spsr.u.fields.cwp = pr_status->PSRREG.psr_bits.cwp; spsr.u.fields.ps = pr_status->PSRREG.psr_bits.ps; spsr.u.fields.s = pr_status->PSRREG.psr_bits.s; spsr.u.fields.pil = pr_status->PSRREG.psr_bits.pil; spsr.u.fields.ef = pr_status->PSRREG.psr_bits.ef; spsr.u.fields.ec = pr_status->PSRREG.psr_bits.ec; spsr.u.fields.reserved = pr_status->PSRREG.psr_bits.reserved; spsr.u.fields.icc = pr_status->PSRREG.psr_bits.icc; spsr.u.fields.ver = pr_status->PSRREG.psr_bits.et; spsr.u.fields.impl = pr_status->PSRREG.psr_bits.impl; spsr.u.word = swap32(spsr.u.word); memcpy(&(cpu->regs.r_psr), &spsr, sizeof(struct swapped_psr)); } } //-------------------------------------------------------------------------- static void swap_sparc_thread_state_fpu(struct sparc_thread_state_fpu *fpu) { struct swapped_fsr { union { struct { unsigned int cexc:BITS_WIDTH(4,0), aexc:BITS_WIDTH(9,5), fcc:BITS_WIDTH(11,10), pr:BIT_WIDTH(12), qne:BIT_WIDTH(13), ftt:BITS_WIDTH(16,14), res:BITS_WIDTH(22,17), tem:BITS_WIDTH(27,23), rp:BITS_WIDTH(29,28), rd:BITS_WIDTH(31,30); } fields; unsigned int word; } u; } sfsr; uint32 i; struct f_status *fpu_status; // floating point registers for ( i = 0; i < 16; i++ ) // 16 doubles fpu->fpu.fpu_fr.Fpu_dregs[i] = SWAP_DOUBLE(fpu->fpu.fpu_fr.Fpu_dregs[i]); fpu->fpu.Fpu_q[0].FQu.whole = SWAP_DOUBLE(fpu->fpu.Fpu_q[0].FQu.whole); fpu->fpu.Fpu_q[1].FQu.whole = SWAP_DOUBLE(fpu->fpu.Fpu_q[1].FQu.whole); fpu->fpu.Fpu_flags = swap32(fpu->fpu.Fpu_flags); fpu->fpu.Fpu_extra = swap32(fpu->fpu.Fpu_extra); fpu->fpu.Fpu_qcnt = swap32(fpu->fpu.Fpu_qcnt); fpu_status = (struct f_status *) &(fpu->fpu.Fpu_fsr); if ( !mf ) { memcpy(&sfsr, &(fpu->fpu.Fpu_fsr), sizeof(unsigned int)); sfsr.u.word = swap32(sfsr.u.word); fpu_status->FPUREG.Fpu_fsr_bits.rd = sfsr.u.fields.rd; fpu_status->FPUREG.Fpu_fsr_bits.rp = sfsr.u.fields.rp; fpu_status->FPUREG.Fpu_fsr_bits.tem = sfsr.u.fields.tem; fpu_status->FPUREG.Fpu_fsr_bits.res = sfsr.u.fields.res; fpu_status->FPUREG.Fpu_fsr_bits.ftt = sfsr.u.fields.ftt; fpu_status->FPUREG.Fpu_fsr_bits.qne = sfsr.u.fields.qne; fpu_status->FPUREG.Fpu_fsr_bits.pr = sfsr.u.fields.pr; fpu_status->FPUREG.Fpu_fsr_bits.fcc = sfsr.u.fields.fcc; fpu_status->FPUREG.Fpu_fsr_bits.aexc = sfsr.u.fields.aexc; fpu_status->FPUREG.Fpu_fsr_bits.cexc = sfsr.u.fields.cexc; } else { sfsr.u.fields.rd = fpu_status->FPUREG.Fpu_fsr_bits.rd; sfsr.u.fields.rp = fpu_status->FPUREG.Fpu_fsr_bits.rp; sfsr.u.fields.tem = fpu_status->FPUREG.Fpu_fsr_bits.tem; sfsr.u.fields.res = fpu_status->FPUREG.Fpu_fsr_bits.res; sfsr.u.fields.ftt = fpu_status->FPUREG.Fpu_fsr_bits.ftt; sfsr.u.fields.qne = fpu_status->FPUREG.Fpu_fsr_bits.qne; sfsr.u.fields.pr = fpu_status->FPUREG.Fpu_fsr_bits.pr; sfsr.u.fields.fcc = fpu_status->FPUREG.Fpu_fsr_bits.fcc; sfsr.u.fields.aexc = fpu_status->FPUREG.Fpu_fsr_bits.aexc; sfsr.u.fields.cexc = fpu_status->FPUREG.Fpu_fsr_bits.cexc; sfsr.u.word = swap32(sfsr.u.word); memcpy(&(fpu->fpu.Fpu_fsr), &sfsr, sizeof(struct swapped_fsr)); } } */ //-------------------------------------------------------------------------- static void swap_ident_command(struct ident_command *id_cmd) { id_cmd->cmd = swap32(id_cmd->cmd); id_cmd->cmdsize = swap32(id_cmd->cmdsize); } //-------------------------------------------------------------------------- static void swap_routines_command(struct routines_command *r_cmd) { r_cmd->cmd = swap32(r_cmd->cmd); r_cmd->cmdsize = swap32(r_cmd->cmdsize); r_cmd->init_address = swap32(r_cmd->init_address); r_cmd->init_module = swap32(r_cmd->init_module); r_cmd->reserved1 = swap32(r_cmd->reserved1); r_cmd->reserved2 = swap32(r_cmd->reserved2); r_cmd->reserved3 = swap32(r_cmd->reserved3); r_cmd->reserved4 = swap32(r_cmd->reserved4); r_cmd->reserved5 = swap32(r_cmd->reserved5); r_cmd->reserved6 = swap32(r_cmd->reserved6); } //-------------------------------------------------------------------------- static void swap_routines_command_64(struct routines_command_64 *r_cmd) { r_cmd->cmd = swap32(r_cmd->cmd); r_cmd->cmdsize = swap32(r_cmd->cmdsize); r_cmd->init_address = swap64(r_cmd->init_address); r_cmd->init_module = swap64(r_cmd->init_module); r_cmd->reserved1 = swap64(r_cmd->reserved1); r_cmd->reserved2 = swap64(r_cmd->reserved2); r_cmd->reserved3 = swap64(r_cmd->reserved3); r_cmd->reserved4 = swap64(r_cmd->reserved4); r_cmd->reserved5 = swap64(r_cmd->reserved5); r_cmd->reserved6 = swap64(r_cmd->reserved6); } //-------------------------------------------------------------------------- static void swap_twolevel_hints_command(twolevel_hints_command *hints_cmd) { hints_cmd->cmd = swap32(hints_cmd->cmd); hints_cmd->cmdsize = swap32(hints_cmd->cmdsize); hints_cmd->offset = swap32(hints_cmd->offset); hints_cmd->nhints = swap32(hints_cmd->nhints); } //-------------------------------------------------------------------------- static void swap_prebind_cksum_command(prebind_cksum_command *cksum_cmd) { cksum_cmd->cmd = swap32(cksum_cmd->cmd); cksum_cmd->cmdsize = swap32(cksum_cmd->cmdsize); cksum_cmd->cksum = swap32(cksum_cmd->cksum); } //---------------------------------------------------------------------- static void swap_uuid_command(struct uuid_command *uuid_cmd) { uuid_cmd->cmd = swap32(uuid_cmd->cmd); uuid_cmd->cmdsize = swap32(uuid_cmd->cmdsize); } //-------------------------------------------------------------------------- static void swap_linkedit_data_command(struct linkedit_data_command *ld) { ld->cmd = swap32(ld->cmd); ld->cmdsize = swap32(ld->cmdsize); ld->dataoff = swap32(ld->dataoff); ld->datasize = swap32(ld->datasize); } //-------------------------------------------------------------------------- static void swap_entry_point_command(struct entry_point_command *ep) { ep->cmd = swap32(ep->cmd); ep->cmdsize = swap32(ep->cmdsize); ep->entryoff = swap64(ep->entryoff); ep->stacksize = swap64(ep->stacksize); } //-------------------------------------------------------------------------- static void swap_source_version_command(struct source_version_command *sv) { sv->cmd = swap32(sv->cmd); sv->cmdsize = swap32(sv->cmdsize); sv->version = swap64(sv->version); } //-------------------------------------------------------------------------- static void swap_rpath_command(struct rpath_command *rpath_cmd) { rpath_cmd->cmd = swap32(rpath_cmd->cmd); rpath_cmd->cmdsize = swap32(rpath_cmd->cmdsize); rpath_cmd->path.offset = swap32(rpath_cmd->path.offset); } //-------------------------------------------------------------------------- static void swap_encryption_info_command(struct encryption_info_command *ec) { ec->cmd = swap32(ec->cmd); ec->cmdsize = swap32(ec->cmdsize); ec->cryptoff = swap32(ec->cryptoff); ec->cryptsize = swap32(ec->cryptsize); ec->cryptid = swap32(ec->cryptid); } //-------------------------------------------------------------------------- static void swap_encryption_info_command_64(struct encryption_info_command_64 *ec) { ec->cmd = swap32(ec->cmd); ec->cmdsize = swap32(ec->cmdsize); ec->cryptoff = swap32(ec->cryptoff); ec->cryptsize = swap32(ec->cryptsize); ec->cryptid = swap32(ec->cryptid); ec->pad = swap32(ec->pad); } //-------------------------------------------------------------------------- static void swap_dyld_info_command(struct dyld_info_command *ed) { ed->cmd = swap32(ed->cmd); ed->cmdsize = swap32(ed->cmdsize); ed->rebase_off = swap32(ed->rebase_off); ed->rebase_size = swap32(ed->rebase_size); ed->bind_off = swap32(ed->bind_off); ed->bind_size = swap32(ed->bind_size); ed->weak_bind_off = swap32(ed->weak_bind_off); ed->weak_bind_size = swap32(ed->weak_bind_size); ed->lazy_bind_off = swap32(ed->lazy_bind_off); ed->lazy_bind_size = swap32(ed->lazy_bind_size); ed->export_off = swap32(ed->export_off); ed->export_size = swap32(ed->export_size); } //-------------------------------------------------------------------------- static void swap_version_min_command(struct version_min_command *ec) { ec->cmd = swap32(ec->cmd); ec->cmdsize = swap32(ec->cmdsize); ec->version = swap32(ec->version); ec->sdk = swap32(ec->sdk); } //-------------------------------------------------------------------------- static void swap_build_version_command(struct build_version_command *bv) { bv->cmd = swap32(bv->cmd); bv->platform = swap32(bv->platform); bv->minos = swap32(bv->minos); bv->sdk = swap32(bv->sdk); bv->ntools = swap32(bv->ntools); } //-------------------------------------------------------------------------- static void swap_build_tool_version(struct build_tool_version *bt) { bt->tool = swap32(bt->tool); bt->version = swap32(bt->version); } //-------------------------------------------------------------------------- static void swap_nlist_64(struct nlist_64 *symbols_from, struct nlist_64 *symbols_to, uint32 nsymbols) { uint32 i; for ( i = 0; i < nsymbols; i++ ) { symbols_to[i].n_un.n_strx = swap32(symbols_from[i].n_un.n_strx); if ( symbols_to != symbols_from ) { symbols_to[i].n_type = symbols_from[i].n_type; symbols_to[i].n_sect = symbols_from[i].n_sect; } symbols_to[i].n_desc = swap16(symbols_from[i].n_desc); symbols_to[i].n_value = swap64(symbols_from[i].n_value); } } //-------------------------------------------------------------------------- static void nlist_to64( const struct nlist *symbols_from, struct nlist_64 *symbols_to, size_t nsymbols, bool swap) { if ( swap ) { for ( size_t i = 0; i < nsymbols; i++ ) { symbols_to[i].n_un.n_strx = swap32(symbols_from[i].n_un.n_strx); symbols_to[i].n_type = symbols_from[i].n_type; symbols_to[i].n_sect = symbols_from[i].n_sect; symbols_to[i].n_desc = swap16(symbols_from[i].n_desc); symbols_to[i].n_value = swap32(symbols_from[i].n_value); } } else { for ( size_t i = 0; i < nsymbols; i++ ) { symbols_to[i].n_un.n_strx = symbols_from[i].n_un.n_strx; symbols_to[i].n_type = symbols_from[i].n_type; symbols_to[i].n_sect = symbols_from[i].n_sect; symbols_to[i].n_desc = symbols_from[i].n_desc; symbols_to[i].n_value = symbols_from[i].n_value; } } } //-------------------------------------------------------------------------- static void swap_dylib_module_64(struct dylib_module_64 *mods, uint32 nmods) { uint32 i; for ( i = 0; i < nmods; i++ ) { mods[i].module_name = swap32(mods[i].module_name); mods[i].iextdefsym = swap32(mods[i].iextdefsym); mods[i].nextdefsym = swap32(mods[i].nextdefsym); mods[i].irefsym = swap32(mods[i].irefsym); mods[i].nrefsym = swap32(mods[i].nrefsym); mods[i].ilocalsym = swap32(mods[i].ilocalsym); mods[i].nlocalsym = swap32(mods[i].nlocalsym); mods[i].iextrel = swap32(mods[i].iextrel); mods[i].nextrel = swap32(mods[i].nextrel); mods[i].iinit_iterm = swap32(mods[i].iinit_iterm); mods[i].ninit_nterm = swap32(mods[i].ninit_nterm); mods[i].objc_module_info_size = swap32(mods[i].objc_module_info_size); mods[i].objc_module_info_addr = swap64(mods[i].objc_module_info_addr); } } //-------------------------------------------------------------------------- static void dylib_module_to64( const struct dylib_module *mods_from, struct dylib_module_64 *mods_to, size_t nmods, bool swap) { if ( swap ) { for ( size_t i = 0; i < nmods; i++ ) { mods_to[i].module_name = swap32(mods_from[i].module_name); mods_to[i].iextdefsym = swap32(mods_from[i].iextdefsym); mods_to[i].nextdefsym = swap32(mods_from[i].nextdefsym); mods_to[i].irefsym = swap32(mods_from[i].irefsym); mods_to[i].nrefsym = swap32(mods_from[i].nrefsym); mods_to[i].ilocalsym = swap32(mods_from[i].ilocalsym); mods_to[i].nlocalsym = swap32(mods_from[i].nlocalsym); mods_to[i].iextrel = swap32(mods_from[i].iextrel); mods_to[i].nextrel = swap32(mods_from[i].nextrel); mods_to[i].iinit_iterm = swap32(mods_from[i].iinit_iterm); mods_to[i].ninit_nterm = swap32(mods_from[i].ninit_nterm); mods_to[i].objc_module_info_size = swap32(mods_from[i].objc_module_info_size); mods_to[i].objc_module_info_addr = swap32(mods_from[i].objc_module_info_addr); } } else { for ( size_t i = 0; i < nmods; i++ ) { mods_to[i].module_name = mods_from[i].module_name; mods_to[i].iextdefsym = mods_from[i].iextdefsym; mods_to[i].nextdefsym = mods_from[i].nextdefsym; mods_to[i].irefsym = mods_from[i].irefsym; mods_to[i].nrefsym = mods_from[i].nrefsym; mods_to[i].ilocalsym = mods_from[i].ilocalsym; mods_to[i].nlocalsym = mods_from[i].nlocalsym; mods_to[i].iextrel = mods_from[i].iextrel; mods_to[i].nextrel = mods_from[i].nextrel; mods_to[i].iinit_iterm = mods_from[i].iinit_iterm; mods_to[i].ninit_nterm = mods_from[i].ninit_nterm; mods_to[i].objc_module_info_size = mods_from[i].objc_module_info_size; mods_to[i].objc_module_info_addr = mods_from[i].objc_module_info_addr; } } } //-------------------------------------------------------------------------- static void swap_dylib_table_of_contents(struct dylib_table_of_contents *tocs, uint32 ntocs) { for ( uint32 i = 0; i < ntocs; i++ ) { tocs[i].symbol_index = swap32(tocs[i].symbol_index); tocs[i].module_index = swap32(tocs[i].module_index); } } //-------------------------------------------------------------------------- static void swap_dylib_reference(struct dylib_reference *refs, uint32 nrefs) { struct swapped_dylib_reference { union { struct { uint32 flags:8, isym:24; } fields; uint32 word; } u; } sref; for ( uint32 i = 0; i < nrefs; i++ ) { sref.u.fields.isym = refs[i].isym; sref.u.fields.flags = refs[i].flags; sref.u.word = swap32(sref.u.word); memcpy(refs + i, &sref, sizeof(struct swapped_dylib_reference)); } } //-------------------------------------------------------------------------- static void swap_indirect_symbols(uint32 *indirect_symbols, uint32 nindirect_symbols) { for ( uint32 i = 0; i < nindirect_symbols; i++ ) indirect_symbols[i] = swap32(indirect_symbols[i]); } //-------------------------------------------------------------------------- static void swap_relocation_info(struct relocation_info *relocs, uint32 nrelocs) { uint32 i; bool scattered; struct swapped_relocation_info { int32 r_address; union { struct { unsigned int r_type:4, r_extern:1, r_length:2, r_pcrel:1, r_symbolnum:24; } fields; uint32 word; } u; } sr; CASSERT(sizeof(swapped_relocation_info) == sizeof(relocation_info)); struct swapped_scattered_relocation_info { uint32 word; int32 r_value; } *ssr; for ( i = 0; i < nrelocs; i++ ) { scattered = (bool)((swap32(relocs[i].r_address) & R_SCATTERED) != 0); if ( scattered == FALSE ) { memcpy(&sr, relocs + i, sizeof(struct swapped_relocation_info)); sr.r_address = swap32(sr.r_address); sr.u.word = swap32(sr.u.word); relocs[i].r_address = sr.r_address; relocs[i].r_symbolnum = sr.u.fields.r_symbolnum; relocs[i].r_pcrel = sr.u.fields.r_pcrel; relocs[i].r_length = sr.u.fields.r_length; relocs[i].r_extern = sr.u.fields.r_extern; relocs[i].r_type = sr.u.fields.r_type; } else { ssr = (struct swapped_scattered_relocation_info *)(relocs + i); ssr->word = swap32(ssr->word); ssr->r_value = swap32(ssr->r_value); } } } struct segment_command_64 segment_to64(const struct segment_command &sg) { struct segment_command_64 res; res.cmd = sg.cmd; res.cmdsize = sg.cmdsize; memcpy(res.segname, sg.segname, 16); res.vmaddr = sg.vmaddr; res.vmsize = sg.vmsize; res.fileoff = sg.fileoff; res.filesize = sg.filesize; res.maxprot = sg.maxprot; res.initprot = sg.initprot; res.nsects = sg.nsects; res.flags = sg.flags; return res; } struct section_64 section_to64(const struct section &sec) { struct section_64 res; memcpy(res.sectname, sec.sectname, 16); memcpy(res.segname, sec.segname, 16); res.addr = sec.addr; res.size = sec.size; res.offset = sec.offset; res.align = sec.align; res.reloff = sec.reloff; res.nreloc = sec.nreloc; res.flags = sec.flags; res.reserved1 = sec.reserved1; res.reserved2 = sec.reserved2; res.reserved3 = 0; return res; } // load commands for prelinked kexts might contain tagged addresses static void untag_lc_addr(uint64_t *untagged, uint64_t addr) { if ( (addr & 0xFFFF000000000000) != 0 ) { // looks like a kernel address, check if it's tagged if ( (addr & (1ull << 62)) == 0 ) { // assume non-PAC, 51-bit sign extend *untagged = UNTAG_51BIT_SE(addr); } } } void untag_segment_command(struct segment_command *) {} void untag_segment_command(struct segment_command_64 *sg) { untag_lc_addr(&sg->vmaddr, sg->vmaddr); } void untag_section(struct section *) {} void untag_section(struct section_64 *s) { untag_lc_addr(&s->addr, s->addr); } #include "base.cpp" #ifdef LOADER_COMPILE //-------------------------------------------------------------------------- static const char *convert_cpu(cpu_type_t cputype, cpu_subtype_t cpusubtype, int *p_target, bool mf) { const char *name = NULL; switch ( cputype ) { default: case CPU_TYPE_VAX: case CPU_TYPE_ROMP: case CPU_TYPE_NS32032: case CPU_TYPE_NS32332: case CPU_TYPE_MC88000: break; case CPU_TYPE_MC680x0: name = "68K"; switch ( cpusubtype ) { case CPU_SUBTYPE_MC680x0_ALL: break; case CPU_SUBTYPE_MC68030_ONLY: name = "68030"; break; case CPU_SUBTYPE_MC68040: name = "68040"; break; } break; case CPU_TYPE_I860: name = "860xp"; switch ( cpusubtype ) { case CPU_SUBTYPE_I860_ALL: break; case CPU_SUBTYPE_I860_860: name = "860xr"; break; } break; case CPU_TYPE_I386: name = "metapc"; switch ( cpusubtype ) { case CPU_SUBTYPE_I386_ALL: break; case CPU_SUBTYPE_486: case CPU_SUBTYPE_486SX: name = "80486p"; break; case CPU_SUBTYPE_PENT: /* same as 586 */ case CPU_SUBTYPE_PENTPRO: case CPU_SUBTYPE_PENTII_M3: case CPU_SUBTYPE_PENTII_M5: break; } break; case CPU_TYPE_POWERPC: name = "ppc"; break; case CPU_TYPE_HPPA: name = "hppa"; break; case CPU_TYPE_SPARC: name = mf ? "sparcb" : "sparcl"; break; case CPU_TYPE_MIPS: name = mf ? "mipsb" : "mipsl"; break; case CPU_TYPE_ARM: name = mf ? "armb" : "arm"; break; #ifdef __EA64__ // see also below, the error message for it case CPU_TYPE_X86_64: name = "metapc"; break; case CPU_TYPE_ARM64: case CPU_TYPE_ARM64_32: // ARM64_32 can only be disassembled with ida64 name = "arm"; break; #endif case CPU_TYPE_POWERPC64: name = "ppc"; break; } if ( p_target != NULL ) *p_target = macho_arch_to_ida_arch(cputype, cpusubtype, NULL); return name; } //-------------------------------------------------------------------------- static size_t get_cpu_name(cpu_type_t cputype, cpu_subtype_t subtype, char *buf, size_t bufsize) { const char *name; const char *subname = ""; char subbuf[32]; if ( subtype != 0 ) { qsnprintf(subbuf, sizeof(subbuf), " (subtype 0x%02X)", subtype); subname = subbuf; } switch ( cputype & ~CPU_ARCH_ABI64_MASK ) { case CPU_TYPE_VAX: name = "VAX"; break; case CPU_TYPE_ROMP: name = "ROMP"; break; case CPU_TYPE_NS32032: name = "NS32032"; break; case CPU_TYPE_NS32332: name = "NS32332"; break; case CPU_TYPE_VEO: name = "VEO"; break; case CPU_TYPE_MC680x0: name = "MC680x0"; break; case CPU_TYPE_MC88000: name = "MC88000"; break; case CPU_TYPE_I860: name = "I860"; break; case CPU_TYPE_POWERPC: name = "POWERPC"; break; case CPU_TYPE_HPPA: name = "HPPA"; break; case CPU_TYPE_SPARC: name = "SPARC"; break; case CPU_TYPE_I386: name = "I386"; if ( subtype == CPU_SUBTYPE_I386_ALL ) // same as CPU_SUBTYPE_386 subname = ""; break; case CPU_TYPE_ARM: name = "ARM"; switch ( subtype ) { case CPU_SUBTYPE_ARM_A500_ARCH: case CPU_SUBTYPE_ARM_A500: subname = "500"; break; case CPU_SUBTYPE_ARM_A440: subname = "440"; break; case CPU_SUBTYPE_ARM_M4: subname = " M4"; break; case CPU_SUBTYPE_ARM_V4T: subname = "v4T"; break; case CPU_SUBTYPE_ARM_V6: subname = "v6"; break; case CPU_SUBTYPE_ARM_V5TEJ: subname = "v5TEJ"; break; case CPU_SUBTYPE_ARM_XSCALE: subname = " XScale"; break; case CPU_SUBTYPE_ARM_V7: subname = "v7"; break; case CPU_SUBTYPE_ARM_V7F: subname = "v7F"; break; case CPU_SUBTYPE_ARM_V7K: subname = "v7K"; break; case CPU_SUBTYPE_ARM_V7S: subname = "v7S"; break; case CPU_SUBTYPE_ARM_V8: subname = "v8"; break; } break; default: return qsnprintf(buf, bufsize, "0x%02X%s", cputype, subname); } if ( cputype & CPU_ARCH_ABI64 ) { switch ( cputype ) { case CPU_TYPE_X86_64: name = "X86_64"; switch ( subtype & ~CPU_SUBTYPE_MASK ) { case CPU_SUBTYPE_X86_ALL: subname = ""; break; case CPU_SUBTYPE_X86_ARCH1: subname = " (arch1)"; break; case CPU_SUBTYPE_X86_64_H: subname = " (x86_64h)"; break; } break; case CPU_TYPE_POWERPC64: name = "POWERPC64"; break; case CPU_TYPE_ARM64: name = "ARM64"; switch ( subtype & ~CPU_SUBTYPE_MASK ) { case CPU_SUBTYPE_ARM64_ALL: subname = ""; break; case CPU_SUBTYPE_ARM64_V8: subname = "v8"; break; case CPU_SUBTYPE_ARM64E: subname = "e"; break; default: break; } break; default: return qsnprintf(buf, bufsize, "%s64%s", name, subname); } } if ( cputype & CPU_ARCH_ABI64_32 ) { switch ( cputype ) { case CPU_TYPE_ARM64_32: name = "ARM64_32"; switch ( subtype & ~CPU_SUBTYPE_MASK ) { case CPU_SUBTYPE_ARM64_32_ALL: subname = ""; break; case CPU_SUBTYPE_ARM64_32_V8: subname = "_V8"; break; default: break; } break; default: return qsnprintf(buf, bufsize, "%s64%s", name, subname); } } return qsnprintf(buf, bufsize, "%s%s", name, subname); } #endif // --------------------------------------------------------------------------- bool macho_file_t::seek_to_subfile(uint n, size_t filesize) { int64 fsize = filesize == 0 ? qlsize(li) : filesize; if ( fsize <= 0 ) return false; if ( n == 0 && fat_archs.size() == 0 ) { mach_offset = 0; mach_size = size_t(fsize); } else if ( n < fat_archs.size() ) { mach_offset = fat_archs[n].offset; mach_size = fat_archs[n].size; if ( mach_offset >= size_t(fsize) ) { msg("Fat subfile %i is outside the file\n", n); return false; } if ( mach_offset + mach_size > size_t(fsize) ) { msg("Fat subfile %i is truncated\n", n); mach_size = qlsize(li) - mach_offset; } } else { return false; } qoff64_t pos = mach_offset + start_offset; return qlseek(li, pos) == pos; } //-------------------------------------------------------------------------- bool macho_file_t::is_loaded_addr(uint64 addr) { const segcmdvec_t &cmds = get_segcmds(); for ( const segment_command_64 &sg : cmds ) { if ( addr >= sg.vmaddr && addr < sg.vmaddr + sg.vmsize ) return true; } return false; } //-------------------------------------------------------------------------- // select a module from the current FAT submodule (the current submodule is an archive) // 1. create a temporary file from the FAT subfile // 2. let the user select a module from it // 3. load it bool macho_file_t::select_ar_module(size_t offset, size_t size) { if ( extractor == NULL ) return false; // copy the submodule into a separate file char *tmp_fname = qtmpnam(NULL, 0); FILE *out = fopenWB(tmp_fname); if ( out == NULL ) { warning("%s", get_errdesc(tmp_fname)); return false; } qlseek(li, offset, SEEK_SET); char buf[4096]; int rest = size; while ( rest > 0 ) { int chunk = rest > sizeof(buf) ? sizeof(buf) : rest; if ( qlread(li, buf, chunk) != chunk ) break; if ( qfwrite(out, buf, chunk) != chunk ) break; rest -= chunk; } qfclose(out); bool ok = false; if ( rest == 0 ) // read the whole file, good { // let the user select char *tmpfname = NULL; qstrncpy(buf, tmp_fname, sizeof(buf)); if ( extractor->extract(buf, sizeof(buf), &tmpfname, false) ) { // the selected file is in tmpfile, switch to it and try to load it if ( should_close_linput ) close_linput(li); li = open_linput(tmpfname, false); QASSERT(20028, li != NULL); should_close_linput = true; start_offset = 0; fat_archs.clear(); if ( parse_header() && set_subfile(0) ) ok = true; } } qunlink(tmp_fname); return ok; } //-------------------------------------------------------------------------- bool macho_file_t::set_subfile(uint n, size_t filesize, bool silent) { // clear various cached tables mach_header_data.clear(); load_commands.clear(); mach_segcmds.clear(); mach_sections.clear(); mach_dylibs.clear(); mach_modtable.clear(); mach_toc.clear(); mach_reftable.clear(); parsed_section_info = false; base_addr = BADADDR64; size_t mh_len; subfile_type_t type = get_subfile_type(n, filesize); switch ( type ) { default: INTERR(20026); case SUBFILE_UNKNOWN: return false; case SUBFILE_AR: return select_ar_module(mach_offset, mach_size); case SUBFILE_MACH: // 32-bit file m64 = false; mh_len = sizeof(mach_header); break; case SUBFILE_MACH_64: // 64-bit file m64 = true; mh_len = sizeof(mach_header_64); break; } mach_header_data.resize(mh_len); if ( qlread(li, mach_header_data.begin(), mh_len) != mh_len ) return false; memcpy(&mh, mach_header_data.begin(), mh_len); if ( !m64 ) mh.reserved = 0; mf = is_cigam(mh.magic); if ( mf ) swap_mach_header_64(&mh); size_t size = mh_len + mh.sizeofcmds; if ( size > mach_size || size < mh_len ) // overflow? return false; mach_header_data.resize(size); if ( qlread(li, &mach_header_data[mh_len], mh.sizeofcmds) != mh.sizeofcmds ) { mach_header_data.clear(); return false; } return parse_load_commands(silent); } //-------------------------------------------------------------------------- bool macho_file_t::select_subfile(cpu_type_t cputype, cpu_subtype_t cpusubtype) { if ( fat_archs.empty() && set_subfile(0) ) { // single file; check if it matches what we need if ( mh.cputype == cputype && (mh.cpusubtype == cpusubtype || cpusubtype == 0) ) return true; } for ( size_t i = 0; i < fat_archs.size(); i++ ) { // fat file; enumerate architectures const fat_arch &fa = fat_archs[i]; if ( fa.cputype == cputype && (fa.cpusubtype == cpusubtype || cpusubtype == 0) && set_subfile(i) ) { if ( mh.cputype == cputype && (mh.cpusubtype == cpusubtype || cpusubtype == 0) ) return true; } } return false; } // --------------------------------------------------------------------------- // upon return: file position after at the beginning of the subfile macho_file_t::subfile_type_t macho_file_t::get_subfile_type(uint n, size_t filesize) { if ( !seek_to_subfile(n, filesize) ) return SUBFILE_UNKNOWN; uint32_t magic; if ( qlread(li, &magic, sizeof(magic)) != sizeof(magic) ) return SUBFILE_UNKNOWN; qlseek(li, -int(sizeof(magic)), SEEK_CUR); subfile_type_t type = SUBFILE_UNKNOWN; if ( magic == MH_CIGAM || magic == MH_MAGIC ) { type = SUBFILE_MACH; } else if ( magic == MH_CIGAM_64 || magic == MH_MAGIC_64 ) { type = SUBFILE_MACH_64; } else if ( strncmp(ARMAG, (const char *)&magic, sizeof(magic)) == 0 ) { qoff64_t fpos = qltell(li); if ( is_ar_file(li, fpos, false) ) type = SUBFILE_AR; qlseek(li, fpos, SEEK_SET); } return type; } //-------------------------------------------------------------------------- const mach_header_64 &macho_file_t::get_mach_header() { QASSERT(20005, mach_offset != -1); // macho_file_t::get_mach_header: set_subfile() must be called first return mh; } //-------------------------------------------------------------------------- bool macho_file_t::parse_load_commands(bool silent) { struct load_command l; const char *begin = (const char*)&mach_header_data[0]; size_t delta = m64 ? sizeof(mach_header_64) : sizeof(mach_header); const struct load_command *lc = (load_command *)(begin + delta); const char *commands_end = begin + mach_header_data.size(); begin = (const char*)lc; load_commands.clear(); if ( begin >= commands_end && mh.ncmds != 0 ) { if ( !silent ) warning("Inconsistent mh.ncmds %u", mh.ncmds); return false; } for ( uint32 i = 0; i < mh.ncmds && begin < commands_end; i++ ) { safecopy(begin, commands_end, &l); if ( mf ) swap_load_command(&l); if ( l.cmdsize % sizeof(int32) != 0 ) { if ( !silent ) warning("load command %u size not a multiple of 4\n", i); return false; } begin = (const char *)lc; const char *end = begin + l.cmdsize; if ( end > commands_end ) { if ( !silent ) warning("load command %u extends past end of load commands\n", i); return false; } if ( begin > end ) { if ( !silent ) warning("load command %u: cmdsize overflow", i); return false; } if ( l.cmdsize == 0 ) { if ( !silent ) warning("load command %u size zero (can't advance to next load commands)", i); return false; } load_commands.push_back(lc); begin = end; lc = (struct load_command *)begin; } if ( commands_end != begin ) { if ( !silent ) warning("Inconsistent mh.sizeofcmds"); } parsed_section_info = false; return !load_commands.empty(); } //-------------------------------------------------------------------------- #define HANDLE_SIMPLE_COMMAND(name) \ { \ name##_command cmd##name; \ safecopy(begin, end, &cmd##name); \ if ( mf ) \ swap_##name##_command(&cmd##name); \ result = v.visit_##name (&cmd##name, cmd_begin, end); \ } //-------------------------------------------------------------------------- template int handle_lc_segment(const char *begin, const char *end, macho_lc_visitor_t &v, bool _mf) { generic_segment_command sg; safecopy(begin, end, &sg); if ( _mf ) swap_segment_command(&sg); untag_segment_command(&sg); const char *cmd_begin = begin; int result = v.visit_segment(&sg, cmd_begin, end); if ( result == 0 && sg.nsects > 0 ) { generic_section s; for ( uint32_t j=0; j < sg.nsects; j++ ) { if ( begin >= end ) { static bool complained = false; if ( !complained ) warning("Inconsistent number of sections %u in a segment", sg.nsects); complained = true; break; } cmd_begin = begin; safecopy(begin, end, &s); if ( _mf ) swap_section(&s, 1); untag_section(&s); // ignore sections outside of the segment result = v.visit_section(&s, cmd_begin, end); if ( result != 0 ) break; } } return result; } //-------------------------------------------------------------------------- // build_version_command is variable-sized so we need to handle it separately int handle_build_version(const char *begin, const char *end, macho_lc_visitor_t &v, bool _mf) { build_version_command bv; safecopy(begin, end, &bv); if ( _mf ) swap_build_version_command(&bv); const char *cmd_begin = begin; int result = v.visit_build_version(&bv, cmd_begin, end); //followed by 'ntools'*build_tool_version structures if ( result == 0 && bv.ntools > 0 ) { build_tool_version bt; for ( uint32_t j = 0; j < bv.ntools; j++ ) { if ( begin >= end ) { static bool complained = false; if ( !complained ) warning("Inconsistent number of tools %u in LC_BUILD_VERSION command", bv.ntools); complained = true; break; } cmd_begin = begin; safecopy(begin, end, &bt); if ( _mf ) swap_build_tool_version(&bt); // ignore entries outside of the command result = v.visit_build_tool_version(&bt, cmd_begin, end); if ( result != 0 ) break; } } return result; } //-------------------------------------------------------------------------- bool macho_file_t::visit_load_commands(macho_lc_visitor_t &v) { struct load_command l; int result = 0; const char *begin = (const char*)&mach_header_data[0], *end; const char *commands_end = begin + mach_header_data.size(); for ( size_t i=0; result == 0 && i < load_commands.size(); i++ ) { const struct load_command *lc = load_commands[i]; l = *lc; if ( mf ) swap_load_command(&l); begin = (const char*)lc; const char *cmd_begin = begin; end = begin + l.cmdsize; if ( end > commands_end || begin >= end ) { msg("Inconsistency in load commands"); break; } result = v.visit_any_load_command(&l, cmd_begin, end); if ( result == 2 ) { // don't call specific callback and continue result = 0; continue; } else if ( result != 0 ) { // stop enumeration break; } switch ( l.cmd ) { case LC_SEGMENT: result = handle_lc_segment(begin, end, v, mf); break; case LC_SEGMENT_64: result = handle_lc_segment(begin, end, v, mf); break; case LC_SYMTAB: HANDLE_SIMPLE_COMMAND(symtab); break; case LC_SYMSEG: HANDLE_SIMPLE_COMMAND(symseg); break; case LC_THREAD: case LC_UNIXTHREAD: HANDLE_SIMPLE_COMMAND(thread); break; case LC_IDFVMLIB: case LC_LOADFVMLIB: HANDLE_SIMPLE_COMMAND(fvmlib); break; case LC_IDENT: HANDLE_SIMPLE_COMMAND(ident); break; case LC_FVMFILE: HANDLE_SIMPLE_COMMAND(fvmfile); break; case LC_DYSYMTAB: HANDLE_SIMPLE_COMMAND(dysymtab); break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_ID_DYLIB: case LC_REEXPORT_DYLIB: case LC_LAZY_LOAD_DYLIB: case LC_LOAD_UPWARD_DYLIB: HANDLE_SIMPLE_COMMAND(dylib); break; case LC_ID_DYLINKER: case LC_LOAD_DYLINKER: case LC_DYLD_ENVIRONMENT: HANDLE_SIMPLE_COMMAND(dylinker); break; case LC_PREBOUND_DYLIB: HANDLE_SIMPLE_COMMAND(prebound_dylib); break; case LC_ROUTINES: HANDLE_SIMPLE_COMMAND(routines); break; case LC_SUB_FRAMEWORK: HANDLE_SIMPLE_COMMAND(sub_framework); break; case LC_SUB_UMBRELLA: HANDLE_SIMPLE_COMMAND(sub_umbrella); break; case LC_SUB_CLIENT: HANDLE_SIMPLE_COMMAND(sub_client); break; case LC_SUB_LIBRARY: HANDLE_SIMPLE_COMMAND(sub_library); break; case LC_TWOLEVEL_HINTS: HANDLE_SIMPLE_COMMAND(twolevel_hints); break; case LC_PREBIND_CKSUM: HANDLE_SIMPLE_COMMAND(prebind_cksum); break; case LC_ROUTINES_64: { routines_command_64 rc; safecopy(begin, end, &rc); if ( mf ) swap_routines_command_64(&rc); result = v.visit_routines_64(&rc, cmd_begin, end); } break; case LC_UUID: HANDLE_SIMPLE_COMMAND(uuid); break; case LC_RPATH: HANDLE_SIMPLE_COMMAND(rpath); break; case LC_CODE_SIGNATURE: case LC_SEGMENT_SPLIT_INFO: case LC_FUNCTION_STARTS: case LC_DATA_IN_CODE: case LC_DYLIB_CODE_SIGN_DRS: HANDLE_SIMPLE_COMMAND(linkedit_data); break; case LC_ENCRYPTION_INFO: HANDLE_SIMPLE_COMMAND(encryption_info); break; case LC_ENCRYPTION_INFO_64: { encryption_info_command_64 ec; safecopy(begin, end, &ec); if ( mf ) swap_encryption_info_command_64(&ec); result = v.visit_encryption_info_64(&ec, cmd_begin, end); } break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: HANDLE_SIMPLE_COMMAND(dyld_info); break; case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: case LC_VERSION_MIN_WATCHOS: case LC_VERSION_MIN_TVOS: HANDLE_SIMPLE_COMMAND(version_min); break; case LC_BUILD_VERSION: result = handle_build_version(begin, end, v, mf); break; case LC_MAIN: HANDLE_SIMPLE_COMMAND(entry_point); break; case LC_SOURCE_VERSION: HANDLE_SIMPLE_COMMAND(source_version); break; default: result = v.visit_unknown_load_command(&l, cmd_begin, end); break; } } return result != 0; } //-------------------------------------------------------------------------- int macho_file_t::is_encrypted() { struct myvisitor: macho_lc_visitor_t { int is_encrypted; myvisitor(): is_encrypted(0) {} virtual int visit_segment( const struct segment_command *sg, const char *, const char *) override { if ( is_protected(*sg) ) { is_encrypted = 1; return 1; } return 0; } virtual int visit_segment( const struct segment_command_64 *sg, const char *, const char *) override { if ( is_protected(*sg) ) { is_encrypted = 1; return 1; } return 0; } virtual int visit_encryption_info( const struct encryption_info_command *ec, const char *, const char *) override { if ( ec->cryptsize != 0 && ec->cryptid != 0 ) { is_encrypted = 2; return 1; } return 0; } virtual int visit_encryption_info_64( const struct encryption_info_command_64 *ec, const char *, const char *) override { if ( ec->cryptsize != 0 && ec->cryptid != 0 ) { is_encrypted = 2; return 1; } return 0; } }; myvisitor v; visit_load_commands(v); return v.is_encrypted; } //-------------------------------------------------------------------------- bool macho_file_t::is_kernel() { if ( mh.filetype != MH_EXECUTE ) return false; // look for the __KLD segment. it contains the kernel bootstrap code. // if the macho file is executable and bootable, it is likely the mach kernel. struct myvisitor: macho_lc_visitor_t { bool is_kernel; myvisitor(): is_kernel(false) {} #define CHECK_KLD(sc) \ do \ { \ if ( streq(sc->segname, "__KLD") ) \ { \ is_kernel = true; \ return 1; \ } \ } \ while ( false ) virtual int visit_segment(const segment_command *sc, const char *, const char *) override { CHECK_KLD(sc); return 0; } virtual int visit_segment(const segment_command_64 *sc, const char *, const char *) override { CHECK_KLD(sc); return 0; } #undef CHECK_KLD }; myvisitor v; visit_load_commands(v); return v.is_kernel; } //-------------------------------------------------------------------------- bool macho_file_t::is_kcache() { if ( mh.filetype != MH_EXECUTE ) return false; // look for a nontrivial __PRELINK_INFO:__info section. // such a file will likely contain prelinked KEXTs. struct myvisitor: macho_lc_visitor_t { bool is_kcache; myvisitor(): is_kcache(false) {} #define CHECK_PRELINK_INFO(s) \ do \ { \ if ( is_prelink_info(*s) ) \ { \ is_kcache = true; \ return 1; \ } \ } \ while ( false ) virtual int visit_section(const section *s, const char *, const char *) override { CHECK_PRELINK_INFO(s); return 0; } virtual int visit_section(const section_64 *s, const char *, const char *) override { CHECK_PRELINK_INFO(s); return 0; } #undef CHECK_PRELINK_INFO }; myvisitor v; visit_load_commands(v); return v.is_kcache; } //-------------------------------------------------------------------------- bool macho_file_t::is_kext() const { return mh.filetype == MH_KEXT_BUNDLE; } //-------------------------------------------------------------------------- // check for LC_MAIN and/or LC_THREAD/LC_UNIXTHREAD commands static void _get_thread_info(macho_file_t *mfile, const char **thr_begin, const char **thr_end, uint64_t *entryoff) { struct ida_local myvisitor: macho_lc_visitor_t { const char *begin, *end; uint64_t entryoff; myvisitor(): begin(NULL), end(NULL), entryoff(0) {} virtual int visit_thread( const struct thread_command *, const char *_begin, const char *_end) override { safeskip(_begin, _end, sizeof(thread_command)); if ( _begin >= _end ) { // bad command, try to continue return 0; } begin = _begin; end = _end; return 0; } //-------------------------------------------------------------------------- virtual int visit_entry_point( const struct entry_point_command *ei, const char *_begin, const char *_end) override { if ( !safeskip(_begin, _end, sizeof(entry_point_command)) ) { // bad command return 0; } entryoff = ei->entryoff; return 0; } }; myvisitor v; mfile->visit_load_commands(v); if ( thr_begin != NULL ) *thr_begin = v.begin; if ( thr_end != NULL ) *thr_end = v.end; if ( entryoff != NULL ) *entryoff = v.entryoff; } //-------------------------------------------------------------------------- void macho_file_t::get_thread_state(const char *&begin, const char *&end) { _get_thread_info(this, &begin, &end, NULL); } //-------------------------------------------------------------------------- uint64 macho_file_t::get_entry_address() { const char *begin, *end; uint64_t entryoff; _get_thread_info(this, &begin, &end, &entryoff); if ( entryoff != 0 ) { if ( base_addr == BADADDR64 ) parse_section_info(); return base_addr == BADADDR64 ? entryoff : base_addr + entryoff; } // no LC_MAIN, go the long way // fun fact: dyld does not check if the thread context actually has the correct flavor or even size // it just does stuff like: // const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); // void* entry = (void*)(registers->eip + fSlide); uint32 offset; switch ( mh.cputype ) { case CPU_TYPE_POWERPC: case CPU_TYPE_POWERPC64: case CPU_TYPE_VEO: // __srr0 is at the start of the context offset = 0; break; case CPU_TYPE_ARM: offset = qoffsetof(arm_thread_state32_t, __pc); break; case CPU_TYPE_ARM64: case CPU_TYPE_ARM64_32: offset = qoffsetof(arm_thread_state64_t, __pc); break; case CPU_TYPE_I386: offset = qoffsetof(i386_thread_state_t, __eip); break; case CPU_TYPE_X86_64: offset = qoffsetof(x86_thread_state64_t, __rip); break; default: // unhandled return BADADDR64; } // 8: skip flavor and count begin += 8 + offset; if ( m64 ) { uint64 val; if ( !safecopy(begin, end, &val) ) return BADADDR64; if ( mf ) val = swap64(val); return val; } else { uint32 val; if ( !safecopy(begin, end, &val) ) return BADADDR64; if ( mf ) val = swap32(val); return val; } } //-------------------------------------------------------------------------- bool has_contiguous_segments(const segcmdvec_t &segcmds) { size_t ncmds = segcmds.size(); if ( ncmds <= 1 ) return true; for ( size_t i = 0; i < ncmds - 1; i++ ) { const segment_command_64 &s1 = segcmds[i]; const segment_command_64 &s2 = segcmds[i+1]; if ( (s1.vmaddr + s1.vmsize) != s2.vmaddr ) return false; } return true; } //-------------------------------------------------------------------------- void macho_file_t::parse_section_info() { /* * Create an array of section structures in the host byte sex so it * can be processed and indexed into directly. */ struct myvisitor: macho_lc_visitor_t { secvec_t §ions; segcmdvec_t &segcmds; intvec_t &seg2section; bool m64; int32 nsecs_to_check; myvisitor(secvec_t &_sections, segcmdvec_t &_segcmds, intvec_t &seg2section_, bool _m64) : sections(_sections), segcmds(_segcmds), seg2section(seg2section_), m64(_m64), nsecs_to_check(0) {} virtual int visit_segment( const struct segment_command *sg, const char *, const char *) override { if ( m64 ) { warning("Found a 32-bit segment in 64-bit program, ignoring it"); return 0; } else { segcmds.push_back(segment_to64(*sg)); seg2section.push_back(sections.size()); nsecs_to_check = sg->nsects; } return 0; } virtual int visit_segment( const struct segment_command_64 *sg, const char *, const char *) override { if ( !m64 ) { warning("Found a 64-bit segment in 32-bit program, ignoring it"); return 0; } else { segcmds.push_back(*sg); seg2section.push_back(sections.size()); nsecs_to_check = sg->nsects; } return 0; } bool check_section(const struct section_64 *s) { if ( --nsecs_to_check <= 0 ) // We have already visited all the sections of last good segment return true; const struct segment_command_64 &curseg = segcmds.back(); if ( curseg.vmsize == 0 ) return true; ea_t start = s->addr; ea_t end = s->addr+s->size; for ( int i = 0; i < sections.size(); i++ ) { ea_t si_start = sections[i].addr; ea_t si_end = sections[i].addr + sections[i].size; if ( start > si_start && start < si_end || end > si_start && end < si_end ) { warning("Section 0x%" FMT_64 "X of size %" FMT_64 "X intersects " "with existing section(0x%" FMT_64 "X, size %" FMT_64 "X). Skipped", s->addr, s->size, sections[i].addr, sections[i].size); return false; } } return true; } virtual int visit_section( const struct section *s, const char *, const char *) override { section_64 s64 = section_to64(*s); if ( check_section(&s64) ) sections.push_back(s64); return 0; } virtual int visit_section( const struct section_64 *s, const char *, const char *) override { if ( check_section(s) ) sections.push_back(*s); return 0; } }; if ( !parsed_section_info ) { mach_sections.clear(); mach_segcmds.clear(); seg2section.clear(); myvisitor v(mach_sections, mach_segcmds, seg2section, m64); visit_load_commands(v); parsed_section_info = true; if ( is_shared_cache_lib() && !mach_segcmds.empty() ) { segment_command_64 &sg = mach_segcmds[0]; // kludge: for most dyldcache subfiles, the __TEXT segment will have fileoff=0. // thus, file reads will be realitve to the start offset of the subfile within the cache // (see dyld_single_macho_linput_t::read()). however, some dyldcaches use absolute offsets // in the __TEXT segments. to avoid guessing whether or not a given text offset is relative, // we enforce the use of relative offsets. if ( streq(sg.segname, SEG_TEXT) && sg.fileoff != 0 ) { for ( size_t i = 0; i < mach_sections.size(); i++ ) { // normalize the section offsets so they are 0-based section_64 § = mach_sections[i]; if ( streq(sect.segname, SEG_TEXT) ) sect.offset -= sg.fileoff; } sg.fileoff = 0; } } for ( size_t i = 0; i < mach_segcmds.size(); i++ ) { const segment_command_64 &sg64 = mach_segcmds[i]; if ( base_addr == BADADDR64 && sg64.fileoff == 0 && sg64.filesize != 0 ) { base_addr = sg64.vmaddr; break; } } } } //-------------------------------------------------------------------------- const segcmdvec_t &macho_file_t::get_segcmds() { parse_section_info(); return mach_segcmds; } //-------------------------------------------------------------------------- const secvec_t &macho_file_t::get_sections() { parse_section_info(); return mach_sections; } //-------------------------------------------------------------------------- // get section by 1-based index (0 for header pseudo-section) bool macho_file_t::get_section_or_hdr(section_64 *psect, size_t sectIndex) { memset(psect, 0, sizeof(*psect)); if ( sectIndex == 0 ) { // header psect->addr = 0; qstrncpy(psect->segname, SEG_TEXT, sizeof(psect->segname)); return true; } sectIndex--; if ( sectIndex >= mach_sections.size() ) return false; *psect = mach_sections[sectIndex]; return true; } //-------------------------------------------------------------------------- // get segment by index bool macho_file_t::get_segment(size_t segIndex, segment_command_64 *pseg) { parse_section_info(); if ( segIndex < mach_segcmds.size() ) { if ( pseg != NULL ) *pseg = mach_segcmds[segIndex]; return true; } return false; } //-------------------------------------------------------------------------- // get section by segment index and virtual address inside section bool macho_file_t::get_section(size_t segIndex, uint64_t vaddr, section_64 *psect) { if ( segIndex < seg2section.size() ) { const segment_command_64 &seg = mach_segcmds[segIndex]; for ( size_t i = seg2section[segIndex]; i < seg2section[segIndex] + seg.nsects; i++ ) { const section_64 § = mach_sections[i]; if ( sect.addr <= vaddr && vaddr < sect.addr + sect.size ) { if ( psect != NULL ) *psect = sect; return true; } } } return false; } //-------------------------------------------------------------------------- // find segment by name bool macho_file_t::get_segment(const char *segname, segment_command_64 *pseg) { parse_section_info(); for ( size_t idx = 0; idx < mach_segcmds.size(); ++idx ) { const segment_command_64 &seg = mach_segcmds[idx]; if ( strncmp(seg.segname, segname, sizeof(seg.segname)) == 0 ) { if ( pseg != NULL ) *pseg = seg; return true; } } return false; } //-------------------------------------------------------------------------- // get section by segment name and section name bool macho_file_t::get_section(const char *segname, const char *sectname, section_64 *psect) { parse_section_info(); for ( size_t idx = 0; idx < mach_segcmds.size(); ++idx ) { const segment_command_64 &seg = mach_segcmds[idx]; if ( strncmp(seg.segname, segname, sizeof(seg.segname)) == 0 ) { for ( size_t i = seg2section[idx]; i < seg2section[idx] + seg.nsects; i++ ) { QASSERT(20027, i < mach_sections.size()); const section_64 § = mach_sections[i]; if ( strncmp(sect.sectname, sectname, sizeof(sect.sectname)) == 0 ) { if ( psect != NULL ) *psect = sect; return true; } } } } return false; } //-------------------------------------------------------------------------- // get section's data by segment name and section name bool macho_file_t::get_section(const char *segname, const char *sectname, bytevec_t *data) { section_64 secthdr; if ( !get_section(segname, sectname, §hdr) ) return false; uint64 offset; if ( !is_mem_image() && mh.filetype == MH_OBJECT ) { // we should use section's file offsets offset = secthdr.offset; } else { // we should use section's vaddr and use segment to find the corresponding file offset segment_command_64 seghdr; if ( !get_segment(segname, &seghdr) ) return false; if ( is_mem_image() ) offset = secthdr.addr - base_addr; else offset = seghdr.fileoff + (secthdr.addr - seghdr.vmaddr); } qlseek(li, start_offset + mach_offset + offset); data->resize(secthdr.size); qlread(li, data->begin(), secthdr.size); return true; } //-------------------------------------------------------------------------- const dyliblist_t macho_file_t::get_dylib_list(int kind) { struct myvisitor: macho_lc_visitor_t { dyliblist_t &dylibs; int kind; myvisitor(dyliblist_t &_dylibs, int _kind): dylibs(_dylibs), kind(_kind) {} virtual int visit_dylib( const struct dylib_command *d, const char *begin, const char *end) override { switch ( d->cmd ) { case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: case LC_LAZY_LOAD_DYLIB: case LC_LOAD_UPWARD_DYLIB: { // check if it's the kind of dylib we're looking for if ( kind != 0 && kind != d->cmd ) return 0; // nope, continue // sanity check size_t off = d->dylib.name.offset; const char *namestart = begin + off; if ( off < sizeof(*d) || namestart < begin || namestart >= end ) { dylibs.push_back(""); break; } qstring dlname(namestart, end - namestart); dlname.rtrim('\0'); dylibs.push_back(dlname); } break; } // continue enumeration return 0; } }; if ( mach_dylibs.empty() || kind != 0 ) { myvisitor v(mach_dylibs, kind); visit_load_commands(v); if ( kind != 0 ) { dyliblist_t copy = mach_dylibs; mach_dylibs.clear(); return copy; } } return mach_dylibs; } //-------------------------------------------------------------------------- const mod_table_t &macho_file_t::get_module_table() { struct dysymtab_command dyst; if ( mach_modtable.empty() && get_dyst(&dyst) ) { if ( dyst.modtaboff >= mach_size ) { msg("module table offset is past end of file\n"); } else { size_t entrysize = (m64 ? sizeof(struct dylib_module_64) : sizeof(struct dylib_module)); size_t nmods = dyst.nmodtab; size_t size = nmods * entrysize; if ( dyst.modtaboff + size > mach_size ) { msg("module table extends past end of file\n"); size = mach_size - dyst.modtaboff; nmods = size / entrysize; size = nmods * entrysize; } qlseek(li, start_offset + mach_offset + dyst.modtaboff); mach_modtable.resize(nmods); if ( m64 ) { qlread(li, mach_modtable.begin(), size); if ( mf ) swap_dylib_module_64(mach_modtable.begin(), nmods); } else { qvector mods32; mods32.resize(nmods); qlread(li, mods32.begin(), size); dylib_module_to64(mods32.begin(), mach_modtable.begin(), nmods, mf); } } } return mach_modtable; } //-------------------------------------------------------------------------- const tocvec_t &macho_file_t::get_toc() { struct dysymtab_command dyst; if ( mach_toc.empty() && get_dyst(&dyst) ) { if ( dyst.tocoff >= mach_size ) { msg("table of contents offset is past end of file\n"); } else { size_t entrysize = sizeof(struct dylib_table_of_contents); size_t ntocs = dyst.ntoc; size_t size = ntocs * entrysize; if ( dyst.tocoff + size > mach_size ) { msg("table of contents table extends past end of file\n"); size = mach_size - dyst.tocoff; ntocs = size / entrysize; size = ntocs * entrysize; } qlseek(li, start_offset + mach_offset + dyst.tocoff); mach_toc.resize(ntocs); qlread(li, mach_toc.begin(), size); if ( mf ) swap_dylib_table_of_contents(mach_toc.begin(), ntocs); } } return mach_toc; } //-------------------------------------------------------------------------- const refvec_t &macho_file_t::get_ref_table() { struct dysymtab_command dyst; if ( mach_reftable.empty() && get_dyst(&dyst) ) { if ( dyst.extrefsymoff >= mach_size ) { msg("reference table offset is past end of file\n"); } else { size_t entrysize = sizeof(struct dylib_reference); size_t nrefs = dyst.nextrefsyms; size_t size = nrefs * entrysize; if ( dyst.extrefsymoff + size > mach_size ) { msg("table of contents table extends past end of file\n"); size = mach_size - dyst.extrefsymoff; nrefs = size / entrysize; size = nrefs * entrysize; } qlseek(li, start_offset + mach_offset + dyst.extrefsymoff); mach_reftable.resize(nrefs); qlread(li, mach_reftable.begin(), size); if ( mf ) swap_dylib_reference(&mach_reftable[0], nrefs); } } return mach_reftable; } //-------------------------------------------------------------------------- inline bool is_zeropage(const segment_command_64 &sg) { return sg.vmaddr == 0 && sg.fileoff == 0 && sg.initprot == 0; } //-------------------------------------------------------------------------- inline bool is_text_segment(const segment_command_64 &sg) { if ( is_zeropage(sg) ) return false; const char *name = sg.segname; for ( int i=0; i < sizeof(sg.segname); i++, name++ ) if ( *name != '_' ) break; return strnicmp(name, "TEXT", 4) == 0; } //-------------------------------------------------------------------------- inline bool is_linkedit_segment(const segment_command_64 &sg) { return strnicmp(sg.segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT)-1) == 0; } //-------------------------------------------------------------------------- // load chunk of data from the linkedit section bool macho_file_t::load_linkedit_data( uint32 offset, size_t *size, void *buffer) { if ( *size == 0 ) return true; sval_t linkedit_shift = 0; if ( is_mem_image() ) { // calculate shift between linkedit's segment file offset and memory address // so that we will seek to the correct address in memory for ( size_t i = 0; i < mach_segcmds.size(); i++ ) { const segment_command_64 &sg64 = mach_segcmds[i]; if ( base_addr == BADADDR64 && sg64.fileoff == 0 && sg64.filesize != 0 ) base_addr = sg64.vmaddr; else if ( is_linkedit_segment(sg64) && linkedit_shift == 0 ) linkedit_shift = sval_t(sg64.vmaddr - base_addr - sg64.fileoff); } } if ( offset >= mach_size ) return false; // outside file if ( offset + *size > mach_size ) *size = mach_size - offset; if ( *size == 0 ) return false; qlseek(li, start_offset + mach_offset + linkedit_shift + offset); *size = qlread(li, buffer, *size); return true; } //-------------------------------------------------------------------------- bool macho_file_t::get_uuid(uint8 uuid[16]) { struct ida_local uuid_getter_t : public macho_lc_visitor_t { uint8 *buf; bool retrieved; uuid_getter_t(uint8 *_buf) : buf(_buf), retrieved(false) {} virtual int visit_uuid( const struct uuid_command *cmnd, const char *, const char *) override { memmove(buf, cmnd->uuid, 16); retrieved = true; return 0; } }; uuid_getter_t uuid_getter(uuid); visit_load_commands(uuid_getter); return uuid_getter.retrieved; } //-------------------------------------------------------------------------- bool macho_file_t::match_uuid(const bytevec_t &bytes) { if ( bytes.size() != 16 ) return false; uint8 uuid[16]; return get_uuid(uuid) && memcmp(uuid, bytes.begin(), sizeof(uuid)) == 0; } //-------------------------------------------------------------------------- static void get_platform_version( macho_platform_version_t *mpv, uint32 version_mask, uint32 platform_id) { mpv->plfm = platform_id; mpv->major = uint16(version_mask >> 16); mpv->minor = uint8(version_mask >> 8); mpv->micro = uint8(version_mask); } //-------------------------------------------------------------------------- bool macho_file_t::get_platform_version_info(macho_platform_version_info_t *mpvi) { struct ida_local version_finder_t : public macho_lc_visitor_t { macho_platform_version_info_t *mpvi; bool has_version_info; version_finder_t(macho_platform_version_info_t *_mpvi) : mpvi(_mpvi), has_version_info(false) {} int visit_build_version( const struct build_version_command *bvc, const char *, const char *) override { get_platform_version(&mpvi->build_minos, bvc->minos, bvc->platform); get_platform_version(&mpvi->build_sdk, bvc->sdk, bvc->platform); has_version_info = true; return 0; } int visit_version_min( const struct version_min_command *vmc, const char *, const char *) override { uint32 plfm; switch ( vmc->cmd ) { case LC_VERSION_MIN_MACOSX: plfm = PLATFORM_MACOS; break; case LC_VERSION_MIN_IPHONEOS: plfm = PLATFORM_IOS; break; case LC_VERSION_MIN_WATCHOS: plfm = PLATFORM_WATCHOS; break; case LC_VERSION_MIN_TVOS: plfm = PLATFORM_TVOS; break; default: return 0; } get_platform_version(&mpvi->min_version, vmc->version, plfm); get_platform_version(&mpvi->min_sdk, vmc->sdk, plfm); has_version_info = true; return 0; } }; version_finder_t vf(mpvi); visit_load_commands(vf); return vf.has_version_info; } //-------------------------------------------------------------------------- bool macho_file_t::get_symtab_command(struct symtab_command *st) { struct myvisitor: macho_lc_visitor_t { struct symtab_command *st; myvisitor(struct symtab_command *st_): st(st_) {} virtual int visit_symtab( const struct symtab_command *s, const char *, const char *) override { *st = *s; return 1; } }; myvisitor v(st); return visit_load_commands(v); } //-------------------------------------------------------------------------- void macho_file_t::get_symbol_table( const struct symtab_command &st, nlistvec_t *symbols) { if ( st.symoff >= mach_size ) { // dyldcache branch islands tend to have bogus symbol info. it is not interesting. if ( !is_branch_island() ) msg("WARNING: symbol table offset is past end of file\n"); } else { size_t size = st.nsyms * (m64 ? sizeof(struct nlist_64) : sizeof(struct nlist)); size_t nsymbols; size_t stend = st.symoff + size; if ( stend < st.symoff || stend > mach_size ) { if ( !is_branch_island() ) msg("WARNING: symbol table extends past end of file\n"); size = mach_size - st.symoff; nsymbols = size / (m64 ? sizeof(struct nlist_64) : sizeof(struct nlist)); } else { nsymbols = st.nsyms; } if ( nsymbols != 0 ) { symbols->resize(nsymbols); struct nlist_64 *symbeg = symbols->begin(); if ( m64 ) { size = sizeof(struct nlist_64) * nsymbols; load_linkedit_data(st.symoff, &size, symbeg); if ( mf ) swap_nlist_64(symbeg, symbeg, nsymbols); } else { qvector syms32; syms32.resize(nsymbols); size = sizeof(struct nlist) * nsymbols; load_linkedit_data(st.symoff, &size, &syms32[0]); nlist_to64(&syms32[0], symbeg, nsymbols, mf); } } } } //-------------------------------------------------------------------------- void macho_file_t::get_string_table( const struct symtab_command &st, qstring *strings) { if ( st.stroff >= mach_size ) { // dyldcache branch islands tend to have bogus symbol info. it is not interesting. if ( !is_branch_island() ) msg("WARNING: string table offset is past end of file\n"); } else { size_t strings_size; size_t stend = st.stroff + st.strsize; if ( stend < st.stroff || stend > mach_size ) { if ( !is_branch_island() ) msg("WARNING: string table extends past end of file\n"); strings_size = mach_size - st.stroff; } else { strings_size = st.strsize; } string_table_waitbox_t wb(*this); for ( size_t off = 0; off < strings_size && !wb.cancelled(); ) { size_t chunksize = qmin(32*1024, strings_size - off); strings->resize(strings->size()+chunksize); size_t newsize = chunksize; load_linkedit_data(st.stroff+off, &newsize, &strings->at(off)); off += chunksize; } } } //-------------------------------------------------------------------------- void macho_file_t::get_symbol_table_info(nlistvec_t *symbols, qstring *strings) { symbols->clear(); strings->clear(); struct symtab_command st = { 0 }; if ( get_symtab_command(&st) ) { get_symbol_table(st, symbols); get_string_table(st, strings); } } //-------------------------------------------------------------------------- bool macho_file_t::get_dyst(struct dysymtab_command *dyst) { struct myvisitor: macho_lc_visitor_t { struct dysymtab_command *dyst; myvisitor(struct dysymtab_command *dyst_): dyst(dyst_) { dyst->cmd = 0; } virtual int visit_dysymtab( const struct dysymtab_command *s, const char *, const char *) override { *dyst = *s; return 1; } }; myvisitor v(dyst); return visit_load_commands(v); } //-------------------------------------------------------------------------- /* * get_indirect_symbol_table_info() returns indirect symbols. It handles the * problems related to the file being truncated and only returns valid info. * This routine may return misaligned pointers and it is up to * the caller to deal with alignment issues. */ void macho_file_t::get_indirect_symbol_table_info(qvector *indirect_symbols) { struct dysymtab_command dyst; indirect_symbols->clear(); if ( !get_dyst(&dyst) ) return; if ( dyst.indirectsymoff >= mach_size ) { // dyldcache branch islands tend to have bogus symbol info. it is not interesting. if ( !is_branch_island() ) msg("indirect symbol table offset is past end of file\n"); } else { size_t size = dyst.nindirectsyms * sizeof(uint32); size_t nindirect_symbols = dyst.nindirectsyms; if ( dyst.indirectsymoff + size > mach_size ) { if ( !is_branch_island() ) msg("indirect symbol table extends past end of file\n"); size = mach_size - dyst.indirectsymoff; nindirect_symbols = size / sizeof(uint32); size = nindirect_symbols * sizeof(uint32); } indirect_symbols->resize(nindirect_symbols); qlseek(li, start_offset + mach_offset + dyst.indirectsymoff); qlread(li, indirect_symbols->begin(), size); if ( mf ) swap_indirect_symbols(indirect_symbols->begin(), nindirect_symbols); } } //-------------------------------------------------------------------------- size_t macho_file_t::get_import_info( impvec_t *imports, dyliblist_t *dylibs, rangeset_t *ranges, bool verbose) { bool l64 = is64(); imports->qclear(); const secvec_t §ions = get_sections(); *dylibs = get_dylib_list(); // get the symbol table nlistvec_t symbols; qstring strings; get_symbol_table_info(&symbols, &strings); // get indirect symbol table qvector indirect_symbols; get_indirect_symbol_table_info(&indirect_symbols); uint32 ptrsize = l64 ? 8 : 4; uint32 stride; for ( size_t i = 0; i < sections.size(); i++ ) { const section_64 § = sections[i]; ranges->add(range_t(sect.addr, sect.addr+sect.size)); uint32 section_type = sections[i].flags & SECTION_TYPE; if ( section_type != S_LAZY_SYMBOL_POINTERS && section_type != S_NON_LAZY_SYMBOL_POINTERS ) { continue; } stride = ptrsize; size_t count = sect.size / stride; if ( verbose ) msg("\n\nChecking indirect symbols for (%.16s,%.16s): %" FMT_Z " entries", sect.segname, sect.sectname, count); uint32 n = sections[i].reserved1; if ( verbose ) msg("\naddress index name\n"); for ( size_t j = 0; j < count && n + j < indirect_symbols.size(); j++ ) { uint64_t addr = sect.addr + uint64_t(j) * stride; if ( verbose ) { if ( l64 ) msg("0x%016" FMT_64 "x ", addr); else msg("0x%08x ", (uint)addr); } uint32 symidx = indirect_symbols[j + n]; if ( ( symidx & ( INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS ) ) != 0 ) continue; if ( verbose ) msg("%5u ", symidx); if ( symidx >= symbols.size() ) continue; const struct nlist_64 &nl = symbols[symidx]; int type = nl.n_type & N_TYPE; if ( type == N_UNDF ) { uint32 libno = GET_LIBRARY_ORDINAL(nl.n_desc); if ( libno != SELF_LIBRARY_ORDINAL && libno != DYNAMIC_LOOKUP_ORDINAL && libno != EXECUTABLE_ORDINAL ) { if ( nl.n_un.n_strx >= strings.size() ) { if ( verbose ) msg("?\n"); } else { const char *symname = &strings[nl.n_un.n_strx]; if ( verbose ) msg("UNDF: %s from dylib %u\n", symname, libno); import_info_t &ii = imports->push_back(); ii.impea = addr; ii.dylib = libno; ii.name = symname; } } } } } return imports->size(); } //-------------------------------------------------------------------------- // load array of relocs from file with range checking and endianness swapping bool macho_file_t::load_relocs( uint32 reloff, uint32 nreloc, relocvec_t *relocs, const char *desc) { validate_array_count(NULL, &nreloc, sizeof(relocation_info), desc, reloff, mach_size); relocs->qclear(); relocs->resize(nreloc); size_t size = nreloc * sizeof(relocation_info); qlseek(li, start_offset + mach_offset + reloff); if ( qlread(li, relocs->begin(), size) != size ) { relocs->qclear(); return false; } if ( mf ) swap_relocation_info(relocs->begin(), nreloc); return true; } //-------------------------------------------------------------------------- void macho_file_t::visit_relocs(macho_reloc_visitor_t &v) { if ( qgetenv("IDA_NORELOC") ) return; struct dysymtab_command dyst; uint64 baseea = 0; /* (from Mach-O spec) r_address In images used by the dynamic linker, this is an offset from the virtual memory address of the data of the first segment_command (page 20) that appears in the file (not necessarily the one with the lowest address). For images with the MH_SPLIT_SEGS flag set, this is an offset from the virtual memory address of data of the first read/write segment_command (page 20). */ // we check for first writable segment if MH_SPLIT_SEGS is set // or on x64 (see ImageLoaderMachOClassic::getRelocBase() in dyld sources) // NB: in MH_KEXT_BUNDLE (processed by kernel kxld) r_address is still based on the first segment/0! bool need_writable = false; bool is_dyld_file = mh.filetype == MH_EXECUTE || mh.filetype == MH_DYLINKER || mh.filetype == MH_BUNDLE; if ( (mh.flags & MH_SPLIT_SEGS) != 0 || is_dyld_file && mh.cputype == CPU_TYPE_X86_64 ) { need_writable = true; } for ( size_t i=0; i < mach_segcmds.size(); i++ ) { if ( !need_writable || (mach_segcmds[i].initprot & (VM_PROT_WRITE|VM_PROT_READ)) == (VM_PROT_WRITE|VM_PROT_READ) ) { baseea = mach_segcmds[i].vmaddr; break; } } relocvec_t relocs; if ( get_dyst(&dyst) && dyst.cmd != 0 ) { // External relocation information uint32 nrelocs = dyst.nextrel; if ( nrelocs > 0 && load_relocs(dyst.extreloff, nrelocs, &relocs, "Number of dynamic external relocs") ) { v.visit_relocs(baseea, relocs, macho_reloc_visitor_t::mach_reloc_external); } // Local relocation information nrelocs = dyst.nlocrel; if ( nrelocs > 0 && load_relocs(dyst.locreloff, nrelocs, &relocs, "Number of dynamic local relocs") ) { v.visit_relocs(baseea, relocs, macho_reloc_visitor_t::mach_reloc_local); } } // Section relocation information for ( size_t i = 0; i < mach_sections.size(); i++ ) { if ( mach_sections[i].nreloc == 0 ) continue; char name[80]; qsnprintf(name, sizeof(name), "Number of relocs for section (%.16s,%.16s)", mach_sections[i].segname, mach_sections[i].sectname); uint32 nrelocs = mach_sections[i].nreloc; if ( nrelocs > 0 && load_relocs(mach_sections[i].reloff, nrelocs, &relocs, name) ) { v.visit_relocs(mach_sections[i].addr, relocs, i); } } } //-------------------------------------------------------------------------- bool macho_file_t::getSegInfo(uint64_t *segStartAddr, uint64_t *segSize, int segIndex) { segment_command_64 seg; if ( !get_segment(segIndex, &seg) ) return false; *segStartAddr = seg.vmaddr; *segSize = seg.vmsize; return true; } //-------------------------------------------------------------------------- static bool display_wrong_uleb(const uchar *p) { #ifdef EFD_COMPILE printf( "wrong uleb128/sleb128 encoding: %02X %02X %02X %02X %02X\n", p[0], p[1], p[2], p[3], p[4]); #else deb(IDA_DEBUG_LDR, "wrong uleb128/sleb128 encoding: %02X %02X %02X %02X %02X\n", p[0], p[1], p[2], p[3], p[4]); #endif return false; } //-------------------------------------------------------------------------- bool macho_file_t::visit_rebase_opcodes(const bytevec_t &data, dyld_info_visitor_t &v) { const uchar *begin = &data[0]; const uchar *end = begin + data.size(); const uchar *p = begin; bool done = false; uint64_t ulebv, ulebv2; const int ptrsize = is64() ? 8 : 4; int segIndex; uint64_t segOffset = 0; uchar type = REBASE_TYPE_POINTER; uint64_t segStartAddr = BADADDR64; uint64_t segSize = BADADDR64; while ( !done && p < end ) { uchar opcode = *p & REBASE_OPCODE_MASK; uchar imm = *p & REBASE_IMMEDIATE_MASK; p++; switch ( opcode ) { case REBASE_OPCODE_DONE: done = true; break; case REBASE_OPCODE_SET_TYPE_IMM: type = imm; break; case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: if ( !unpack_uleb128(&ulebv, &p, end) ) WRONG_ULEB: return display_wrong_uleb(p); segIndex = imm; segOffset = ulebv; segStartAddr = BADADDR64; segSize = BADADDR64; getSegInfo(&segStartAddr, &segSize, segIndex); break; case REBASE_OPCODE_ADD_ADDR_ULEB: { uint64 delta; if ( !unpack_uleb128(&delta, &p, end) ) goto WRONG_ULEB; segOffset += delta; } break; case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: segOffset += imm * ptrsize; break; case REBASE_OPCODE_DO_REBASE_IMM_TIMES: if ( imm > segSize ) { deb(IDA_DEBUG_LDR, "bad immediate value %02X in rebase info!\n", imm); return false; } if ( segStartAddr == BADADDR64 ) { WRONG_REBASE: msg("Wrong rebase info, file possibly corrupted!\n"); return false; } for ( int i = 0; i < imm; i++ ) { uint64 addr = segStartAddr + segOffset; if ( !is_loaded_addr(addr) ) goto WRONG_REBASE; v.visit_rebase(addr, type); segOffset += ptrsize; } break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: if ( !unpack_uleb128(&ulebv, &p, end) ) goto WRONG_ULEB; if ( ulebv > segSize ) goto WRONG_ULEB; if ( segStartAddr == BADADDR64 ) goto WRONG_REBASE; for ( size_t i = 0; i < ulebv; i++ ) { uint64 addr = segStartAddr + segOffset; if ( !is_loaded_addr(addr) ) goto WRONG_REBASE; v.visit_rebase(addr, type); segOffset += ptrsize; } break; case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: if ( !unpack_uleb128(&ulebv, &p, end) ) goto WRONG_ULEB; v.visit_rebase(uint64_t(segStartAddr + segOffset), type); segOffset += ulebv + ptrsize; break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: if ( !unpack_uleb128(&ulebv, &p, end) ) goto WRONG_ULEB; if ( !unpack_uleb128(&ulebv2, &p, end) ) goto WRONG_ULEB; if ( ulebv > segSize || ulebv2 > segSize ) goto WRONG_ULEB; if ( segStartAddr == BADADDR64 ) goto WRONG_REBASE; for ( size_t i = 0; i < ulebv; i++ ) { uint64 addr = segStartAddr + segOffset; if ( !is_loaded_addr(addr) ) goto WRONG_REBASE; v.visit_rebase(addr, type); segOffset += ptrsize + ulebv2; } break; default: deb(IDA_DEBUG_LDR, "bad opcode %02X in rebase info!\n", opcode); return false; } } return true; } struct ThreadedBindData { const char *symbolName; int64 addend; int64 libraryOrdinal; uchar type; uchar symboFlags; }; //-------------------------------------------------------------------------- bool macho_file_t::visit_bind_opcodes( dyld_info_visitor_t::bind_kind_t bind_kind, const bytevec_t &data, dyld_info_visitor_t &v) { const uchar *begin = &data[0]; const uchar *end = begin + data.size(); const uchar *p = begin; uint64 skip; uint64 count; const int ptrsize = is64() ? 8 : 4; int segIndex; char type = BIND_TYPE_POINTER; uchar flags = 0; uint64 libOrdinal = BIND_SPECIAL_DYLIB_SELF; int64_t addend = 0; const char *symbolName = NULL; uint64_t segStartAddr = BADADDR64; uint64_t segOffset = 0; uint64_t segSize = BADADDR64; bool done = false; bool threaded = false; qvector allsyms; while ( !done && p < end ) { uchar opcode = *p & BIND_OPCODE_MASK; uchar imm = *p & BIND_IMMEDIATE_MASK; p++; switch ( opcode ) { case BIND_OPCODE_DONE: if ( bind_kind != dyld_info_visitor_t::bind_kind_lazy ) done = true; break; case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: libOrdinal = imm; break; case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: if ( !unpack_uleb128(&libOrdinal, &p, end) ) WRONG_ULEB: return display_wrong_uleb(p); break; case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // the special ordinals are negative numbers if ( imm == 0 ) { libOrdinal = 0; } else { int8_t signExtended = BIND_OPCODE_MASK | imm; libOrdinal = signExtended; } break; case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: flags = imm; symbolName = (const char*)p; while ( *p != '\0' ) ++p; ++p; break; case BIND_OPCODE_SET_TYPE_IMM: type = imm; break; case BIND_OPCODE_SET_ADDEND_SLEB: if ( !unpack_sleb128(&addend, &p, end) ) goto WRONG_ULEB; break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segIndex = imm; if ( !unpack_uleb128(&segOffset, &p, end) ) goto WRONG_ULEB; segStartAddr = BADADDR64; segSize = BADADDR64; getSegInfo(&segStartAddr, &segSize, segIndex); break; case BIND_OPCODE_ADD_ADDR_ULEB: if ( !unpack_uleb128(&skip, &p, end) ) goto WRONG_ULEB; segOffset += skip; break; case BIND_OPCODE_DO_BIND: { uint64 addr = segStartAddr + segOffset; if ( threaded ) { ThreadedBindData &d = allsyms.push_back(); d.addend = addend; d.symbolName = symbolName; d.libraryOrdinal = libOrdinal; d.symboFlags = flags; d.type = type; } else { v.visit_bind(bind_kind, addr, type, flags, libOrdinal, addend, symbolName); } segOffset += ptrsize; } break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: { uint64 addr = segStartAddr + segOffset; if ( !unpack_uleb128(&skip, &p, end) ) goto WRONG_ULEB; v.visit_bind(bind_kind, addr, type, flags, libOrdinal, addend, symbolName); segOffset += skip + ptrsize; } break; case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: { uint64 addr = segStartAddr + segOffset; skip = imm*ptrsize + ptrsize; v.visit_bind(bind_kind, addr, type, flags, libOrdinal, addend, symbolName); segOffset += skip; } break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: if ( !unpack_uleb128(&count, &p, end) ) goto WRONG_ULEB; if ( !unpack_uleb128(&skip, &p, end) ) goto WRONG_ULEB; if ( count > segSize || skip > segSize ) goto WRONG_ULEB; { int i = 0; if ( symbolName != NULL && segStartAddr != BADADDR64 ) { for ( ; i < count; i++ ) { uint64 addr = segStartAddr + segOffset; if ( !is_loaded_addr(addr) ) { msg("Warning: reference to wrong address %" FMT_64 "x\n", addr); break; // exported function must have initialized bytes } v.visit_bind(bind_kind, addr, type, flags, libOrdinal, addend, symbolName); segOffset += skip + ptrsize; } } segOffset += (count - i) * (skip + ptrsize); } break; case BIND_OPCODE_THREADED: if ( imm == BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB ) { threaded = true; uint64 maxnum; if ( !unpack_uleb128(&maxnum, &p, end) ) count = -1; allsyms.clear(); } else if ( imm == BIND_SUBOPCODE_THREADED_APPLY ) { int delta = 0; do { uint64 addr = segStartAddr + segOffset; uint64 raw_value = read_addr_at_va(addr); if ( raw_value != BADADDR64 ) { tagged_pointer64 tp(raw_value); if ( tp.is_auth() ) { static const char *key_names[4] = { "IA", "IB", "DA", "DB" }; deb(IDA_DEBUG_LDR, "%a: JOP diversity 0x%04X, address %s, key %s", ea_t(addr), tp.diversity(), tp.is_addr() ? "true" : "false", key_names[tp.key_index()]); } if ( tp.is_bind() ) { uint16 symidx = uint16(raw_value); if ( symidx < allsyms.size() ) { const ThreadedBindData &d = allsyms[symidx]; type = tp.is_auth() ? BIND_TYPE_THREADED_POINTER_AUTH : BIND_TYPE_THREADED_POINTER; v.visit_bind(bind_kind, addr, type, d.symboFlags, d.libraryOrdinal, d.addend, d.symbolName); } } else { type = tp.is_auth() ? BIND_TYPE_THREADED_REBASE_AUTH : BIND_TYPE_THREADED_REBASE; v.visit_bind(bind_kind, addr, type, 0, -3, tp.untag(get_base()), (const char*)&tp); } delta = tp.skip_count(); segOffset += delta * 8; } } while ( delta != 0 ); } break; default: msg("Warning: bad rebase opcode found (0x%02X), file possibly corrupted!\n", opcode); return false; } } return true; } //-------------------------------------------------------------------------- bool macho_file_t::processExportNode( const uchar *start, const uchar *p, const uchar *end, char *symname, int symnameoff, size_t symnamelen, dyld_info_visitor_t &v, int level) { if ( symnameoff >= symnamelen || p >= end || p < start ) return false; if ( level >= MAX_DEPTH ) return false; const uchar terminalSize = unpack_db(&p, end); const uchar *children = p + terminalSize; if ( children >= end ) return false; if ( terminalSize != 0 ) { if ( symnameoff == 0 ) // no name?? return false; uint64_t flags; uint64_t address; if ( !unpack_uleb128(&flags, &p, end) || !unpack_uleb128(&address, &p, end) ) return display_wrong_uleb(p); if ( base_addr != BADADDR64 && (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 ) address += base_addr; if ( v.visit_export(address, uint32(flags), symname) != 0 ) return true; } const uchar childrenCount = unpack_db(&children, end); const uchar *s = children; for ( int i=0; i < childrenCount && s < end; ++i ) { int edgeStrLen = 0; int maxlen = symnamelen - symnameoff; for ( uchar c = unpack_db(&s, end); c != '\0' && edgeStrLen < maxlen; ++edgeStrLen, c = unpack_db(&s, end) ) { symname[symnameoff+edgeStrLen] = c; } if ( edgeStrLen >= maxlen ) return false; if ( symnameoff == 0 && edgeStrLen == 0 ) // empty symbol?? return false; symname[symnameoff+edgeStrLen] = '\0'; uint64 ulebv; if ( !unpack_uleb128(&ulebv, &s, end) ) return display_wrong_uleb(s); uint32_t childNodeOffset = (uint32_t)ulebv; if ( childNodeOffset == 0 || childNodeOffset != ulebv || !processExportNode(start, start+childNodeOffset, end, symname, symnameoff+edgeStrLen, symnamelen, v, level+1) ) { return false; } } return true; } //-------------------------------------------------------------------------- bool macho_file_t::visit_export_info(const bytevec_t &data, dyld_info_visitor_t &v) { char symname[MAXSTR*2]; const uchar *begin = &data[0]; const uchar *end = begin + data.size(); symname[0] = '\0'; if ( !processExportNode(begin, begin, end, symname, 0, sizeof(symname), v) ) { warning("Bad information in exports, it will be ignored."); return false; } return true; } //-------------------------------------------------------------------------- void macho_file_t::visit_dyld_info(dyld_info_visitor_t &v) { struct ida_local myvisitor: macho_lc_visitor_t { struct dyld_info_command *di; myvisitor(struct dyld_info_command *di_): di(di_) { di->cmd = 0; } virtual int visit_dyld_info( const struct dyld_info_command *lc, const char *, const char *) override { *di = *lc; return 1; } }; dyld_info_command di; myvisitor vdi(&di); if ( visit_load_commands(vdi) ) { bytevec_t data; if ( di.rebase_size != 0 ) { data.resize(di.rebase_size); size_t newsize = di.rebase_size; if ( load_linkedit_data(di.rebase_off, &newsize, &data[0]) && newsize != 0 ) visit_rebase_opcodes(data, v); else msg("Error loading dyld rebase info\n"); } if ( di.bind_size != 0 ) { data.resize(di.bind_size); size_t newsize = di.bind_size; if ( load_linkedit_data(di.bind_off, &newsize, &data[0]) && newsize != 0 ) visit_bind_opcodes(dyld_info_visitor_t::bind_kind_normal, data, v); else msg("Error loading dyld bind info\n"); } if ( di.weak_bind_size != 0 ) { data.resize(di.weak_bind_size); size_t newsize = di.weak_bind_size; if ( load_linkedit_data(di.weak_bind_off, &newsize, &data[0]) && newsize != 0 ) visit_bind_opcodes(dyld_info_visitor_t::bind_kind_weak, data, v); else msg("Error loading dyld weak bind info\n"); } if ( di.lazy_bind_size != 0 ) { data.resize(di.lazy_bind_size); size_t newsize = di.lazy_bind_size; if ( load_linkedit_data(di.lazy_bind_off, &newsize, &data[0]) && newsize != 0 ) visit_bind_opcodes(dyld_info_visitor_t::bind_kind_lazy, data, v); else msg("Error loading dyld lazy bind info\n"); } if ( di.export_size != 0 ) { data.resize(di.export_size); size_t newsize = di.export_size; if ( load_linkedit_data(di.export_off, &newsize, &data[0]) && newsize != 0 ) visit_export_info(data, v); else msg("Error loading dyld export info\n"); } } } //-------------------------------------------------------------------------- void function_starts_visitor_t::handle_error() { msg("Error loading function starts info\n"); } //-------------------------------------------------------------------------- void macho_file_t::visit_function_starts(function_starts_visitor_t &v) { struct myvisitor: macho_lc_visitor_t { struct linkedit_data_command *fs; myvisitor(struct linkedit_data_command *fs_): fs(fs_) { fs->cmd = 0; } virtual int visit_linkedit_data( const struct linkedit_data_command *lc, const char *, const char *) override { if ( lc->cmd == LC_FUNCTION_STARTS ) { *fs = *lc; return 1; } return 0; } }; linkedit_data_command fs; myvisitor vfs(&fs); if ( visit_load_commands(vfs) && fs.datasize != 0 ) { bytevec_t data; data.resize(fs.datasize); size_t newsize = fs.datasize; if ( !load_linkedit_data(fs.dataoff, &newsize, data.begin()) || newsize == 0 ) { v.handle_error(); return; } uint64_t address = base_addr != BADADDR64 ? base_addr : 0; const uchar *p = data.begin(); const uchar *end = p + newsize; while ( p < end ) { uint64_t delta; if ( !unpack_uleb128(&delta, &p, end) ) { display_wrong_uleb(p); v.handle_error(); return; } address += delta; v.visit_start(address); } } } //-------------------------------------------------------------------------- void macho_file_t::visit_shared_regions(shared_region_visitor_t &v) { struct myvisitor: macho_lc_visitor_t { struct linkedit_data_command *sr; myvisitor(struct linkedit_data_command *sr_): sr(sr_) { sr->cmd = 0; } virtual int visit_linkedit_data( const struct linkedit_data_command *lc, const char *, const char *) override { if ( lc->cmd == LC_SEGMENT_SPLIT_INFO ) { *sr = *lc; return 1; } return 0; } }; linkedit_data_command sr; myvisitor vsr(&sr); if ( visit_load_commands(vsr) && sr.datasize != 0 ) { bytevec_t data; data.resize(sr.datasize); size_t newsize = sr.datasize; if ( !load_linkedit_data(sr.dataoff, &newsize, &data[0]) || newsize == 0 ) { msg("Error loading segment split info\n"); return; } uint64_t base = base_addr != BADADDR64 ? base_addr : 0; const uchar *p = &data[0]; const uchar *end = p + data.size(); // see void DyldInfoPrinter::printSharedRegionInfo() in ld64 src/other/dyldinfo.cpp if ( *p == DYLD_CACHE_ADJ_V2_FORMAT ) { p++; // Whole :== FromToSection+ // FromToSection :== ToOffset+ // ToOffset :== FromOffset+ // FromOffset :== uint64 sectionCount; if ( !unpack_uleb128(§ionCount, &p, end) ) { WRONG_ULEB2: display_wrong_uleb(p); return; } for ( uint64 i = 0; i < sectionCount; i++ ) { uint64 fromSectionIndex, toSectionIndex, toOffsetCount; section_64 fromsect, tosect; memset(&fromsect, 0, sizeof(fromsect)); memset(&tosect, 0, sizeof(tosect)); if ( !unpack_uleb128(&fromSectionIndex, &p, end) || !unpack_uleb128(&toSectionIndex, &p, end) || !unpack_uleb128(&toOffsetCount, &p, end) ) { goto WRONG_ULEB2; } if ( !get_section_or_hdr(&fromsect, fromSectionIndex) || !get_section_or_hdr(&tosect, toSectionIndex) ) { #ifdef EFD_COMPILE printf("from sect=%" FMT_64 "d, to sect=%" FMT_64 "d, count=%" FMT_64 "d:\n", fromSectionIndex, toSectionIndex, toOffsetCount); printf(" bad section index!\n"); #endif return; } uint64 toSectionOffset = 0; for ( uint64 j = 0; j < toOffsetCount; ++j ) { uint64 toSectionDelta; uint64 fromOffsetCount; if ( !unpack_uleb128(&toSectionDelta, &p, end) || !unpack_uleb128(&fromOffsetCount, &p, end) ) { goto WRONG_ULEB2; } toSectionOffset += toSectionDelta; for ( uint64 k = 0; k < fromOffsetCount; ++k ) { uint64 kind, fromSectDeltaCount; uint64 fromSectionOffset = 0; if ( !unpack_uleb128(&kind, &p, end) || !unpack_uleb128(&fromSectDeltaCount, &p, end) ) { goto WRONG_ULEB2; } for ( uint64 l = 0; l < fromSectDeltaCount; ++l ) { uint64_t delta; if ( !unpack_uleb128(&delta, &p, end) ) goto WRONG_ULEB2; fromSectionOffset += delta; int ok = v.visit_regionv2(kind, fromsect.addr + fromSectionOffset, tosect.addr + toSectionOffset); if ( ok != 0 ) return; } } } } } while ( p < end ) { uchar kind = unpack_db(&p, end); if ( p >= end ) return; uint64_t address = 0; uint64_t delta; if ( !unpack_uleb128(&delta, &p, end) ) { WRONG_ULEB1: display_wrong_uleb(p); return; } while ( p < end && delta != 0 ) { address += delta; int ok = v.visit_region(kind, base + address); if ( ok != 0 ) return; if ( !unpack_uleb128(&delta, &p, end) ) goto WRONG_ULEB1; } } } } //-------------------------------------------------------------------------- bool macho_file_t::get_id_dylib(qstring *id) { struct myvisitor: macho_lc_visitor_t { qstring *dyld_id; bool found; myvisitor(qstring *_id): dyld_id(_id), found(false) {} virtual int visit_dylib( const struct dylib_command *dl, const char *begin, const char *end) override { if ( dl->cmd == LC_ID_DYLIB ) { const char *p = begin + dl->dylib.name.offset; if ( p < end ) *dyld_id = qstring(p, end-p); found = true; return 0; } return 1; } }; if ( id == NULL ) return false; myvisitor v(id); visit_load_commands(v); return v.found; } //-------------------------------------------------------------------------- /*const DYLDCache::ArchType DYLDCache::architectures[] = { { CPU_TYPE_X86_64, CPU_SUBTYPE_MULTIPLE, "dyld_v1 x86_64", "x86_64", littleEndian }, { CPU_TYPE_X86, CPU_SUBTYPE_MULTIPLE, "dyld_v1 i386", "i386", littleEndian }, { CPU_TYPE_POWERPC, CPU_SUBTYPE_MULTIPLE, "dyld_v1 ppc", "rosetta", bigEndian }, { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, "dyld_v1 armv6", "armv6", littleEndian }, { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, "dyld_v1 armv7", "armv7", littleEndian }, { CPU_TYPE_ARM64, CPU_SUBTYPE_MULTIPLE, "dyld_v1 armv64","armv64", littleEndian }, { 0 } };*/ //-------------------------------------------------------------------------- bool dyld_cache_t::open(const char *path) { li = open_linput(path, false); if ( li == NULL ) return false; should_close_linput = true; return true; } //-------------------------------------------------------------------------- bool dyld_cache_t::parse_header(uint32 flags) { if ( li == NULL ) return false; qlseek(li, 0); if ( qlread(li, &header, sizeof(header)) != sizeof(header) ) return false; if ( !strneq(header.magic, "dyld_v", strlen("dyld_v")) ) return false; if ( strneq(header.magic, "dyld_v0", strlen("dyld_v0")) ) return false; // deduce endianness and bitness from the arch name const char *p = strchr(header.magic, ' '); while ( *p == ' ' ) p++; mf = strneq(p, "ppc", 3); m64 = strneq(p, "x86_64", 6) || strneq(p, "arm64", 5); // x86_64(h) or arm64(e) if ( header.mappingOffset <= offsetof(dyld_cache_header, slideInfoOffset) ) { // old format, without slide info header.slideInfoOffset = header.slideInfoSize = 0; } if ( header.mappingOffset <= offsetof(dyld_cache_header, branchPoolsCount) ) { // no island info header.branchPoolsOffset = header.branchPoolsCount = 0; } if ( header.mappingOffset <= offsetof(dyld_cache_header, imagesTextOffset) ) { // no text info header.imagesTextOffset = header.imagesTextCount = 0; } if ( (flags & PHF_MAPPINGS) != 0 ) { if ( qlseek(li, header.mappingOffset) != header.mappingOffset ) return false; mappings.resize(header.mappingCount); validate_array_count_or_die(li, header.mappingCount, sizeof(dyld_cache_mapping_info), "count of cache mapping infos"); if ( qlread(li, mappings.begin(), sizeof(dyld_cache_mapping_info) * header.mappingCount) != sizeof(dyld_cache_mapping_info) * header.mappingCount ) return false; if ( header.mappingOffset <= offsetof(dyld_cache_header, maxSlide) + sizeof(uint64_t) ) { // no shared region/max slide info, set defaults if ( mappings.size() > 2 ) { header.sharedRegionStart = mappings[0].address; header.sharedRegionSize = 0x100000000LL; header.maxSlide = header.sharedRegionSize - mappings[2].address - mappings[2].size + header.sharedRegionStart; } } } if ( (flags & PHF_IMAGES) != 0 ) { if ( qlseek(li, header.imagesOffset) != header.imagesOffset ) return false; image_infos.resize(header.imagesCount); validate_array_count_or_die(li, header.imagesCount, sizeof(dyld_cache_image_info), "count of images in the cache file"); if ( qlread(li, image_infos.begin(), sizeof(dyld_cache_image_info) * header.imagesCount) != sizeof(dyld_cache_image_info) * header.imagesCount ) return false; for ( size_t i = 0; i < header.imagesCount; i++ ) { size_t name_off = image_infos[i].pathFileOffset; char namebuf[MAXSTR]; if ( qlgetz(li, name_off, namebuf, sizeof(namebuf)) == NULL ) return false; image_names.push_back(namebuf); } } if ( (flags & PHF_SYMBOLS) != 0 ) { parse_local_symbols(); } if ( (flags & PHF_ISLANDS) != 0 && header.branchPoolsCount != 0 && header.branchPoolsOffset != 0 ) { if ( qlseek(li, header.branchPoolsOffset) != header.branchPoolsOffset ) return false; validate_array_count(li, &header.branchPoolsCount, sizeof(uint64), "branch islands entries count"); island_addrs.resize(header.branchPoolsCount); if ( qlread(li, island_addrs.begin(), sizeof(uint64) * header.branchPoolsCount) != sizeof(uint64) * header.branchPoolsCount ) return false; } if ( (flags & PHF_SLIDE) != 0 && header.slideInfoOffset != 0 && header.slideInfoSize != 0 ) { if ( qlseek(li, header.slideInfoOffset) != header.slideInfoOffset ) return false; if ( qlread(li, &slide_version, sizeof(slide_version)) != sizeof(slide_version) ) return false; if ( qlseek(li, header.slideInfoOffset) != header.slideInfoOffset ) return false; if ( slide_version == 1 ) { dyld_cache_slide_info si; if ( qlread(li, &si, sizeof(si)) != sizeof(si) ) return false; slide_toc.resize(si.toc_count); if ( qlseek(li, header.slideInfoOffset + si.toc_offset) != header.slideInfoOffset + si.toc_offset ) return false; validate_array_count_or_die(li, si.toc_count, sizeof(uint16), "Slide info TOC entries count"); if ( qlread(li, slide_toc.begin(), sizeof(uint16) * si.toc_count) != sizeof(uint16) * si.toc_count ) return false; slide_entries_size = si.entries_size; slide_entries.resize(si.entries_count*si.entries_size); if ( qlseek(li, header.slideInfoOffset + si.entries_offset) != header.slideInfoOffset + si.entries_offset ) return false; if ( qlread(li, slide_entries.begin(), slide_entries.size()) != slide_entries.size() ) return false; } else if ( slide_version == 2 ) { dyld_cache_slide_info2 si; if ( qlread(li, &si, sizeof(si)) != sizeof(si) ) return false; if ( si.page_size == 0 ) return false; slide_page_size = si.page_size; slide_delta_mask = si.delta_mask; slide_value_add = si.value_add; slide_page_starts.resize(si.page_starts_count); if ( qlseek(li, header.slideInfoOffset + si.page_starts_offset) != header.slideInfoOffset + si.page_starts_offset ) return false; validate_array_count_or_die(li, si.page_starts_count, sizeof(uint16), "Slide info page starts count"); if ( qlread(li, slide_page_starts.begin(), sizeof(uint16) * si.page_starts_count) != sizeof(uint16) * si.page_starts_count ) return false; slide_page_extras.resize(si.page_extras_count); if ( qlseek(li, header.slideInfoOffset + si.page_extras_offset) != header.slideInfoOffset + si.page_extras_offset ) return false; validate_array_count_or_die(li, si.page_extras_count, sizeof(uint16), "Slide info page extras count"); if ( qlread(li, slide_page_extras.begin(), sizeof(uint16) * si.page_extras_count) != sizeof(uint16) * si.page_extras_count ) return false; } else if ( slide_version == 3 ) { dyld_cache_slide_info3 si; if ( qlread(li, &si, sizeof(si)) != sizeof(si) ) return false; if ( si.page_size == 0 ) return false; slide_page_size = si.page_size; slide_delta_mask = 0x7FFull << 51; slide_value_add = si.auth_value_add; slide_page_starts.resize(si.page_starts_count); validate_array_count_or_die(li, si.page_starts_count, sizeof(uint16), "Slide info page starts count"); if ( qlread(li, slide_page_starts.begin(), sizeof(uint16) * si.page_starts_count) != sizeof(uint16) * si.page_starts_count ) return false; } } if ( (flags & PHF_TEXT) != 0 && header.imagesTextOffset != 0 && header.imagesTextCount != 0 ) { if ( qlseek(li, header.imagesTextOffset) != header.imagesTextOffset ) return false; text_infos.resize(header.imagesTextCount); validate_array_count_or_die(li, header.imagesTextCount, sizeof(dyld_cache_image_text_info), "count of image text infos in the cache file"); if ( qlread(li, text_infos.begin(), sizeof(dyld_cache_image_text_info) * header.imagesTextCount) != sizeof(dyld_cache_image_text_info) * header.imagesTextCount ) return false; } return true; } //-------------------------------------------------------------------------- const char *dyld_cache_t::get_arch() const { const char *p = strchr(header.magic, ' '); if ( p != NULL ) while ( *p == ' ' ) p++; return p; } //-------------------------------------------------------------------------- static int tzcnt(uint64 x) { int b = 0; for ( ; x != 0 && (x & 1) == 0; x >>= 1 ) b++; return b; } //-------------------------------------------------------------------------- uint64 dyld_cache_t::untag(uint64 v) const { if ( slide_version == 2 ) return (v & ~slide_delta_mask) + slide_value_add; else if ( slide_version == 3 ) return tagged_pointer64(v).untag(slide_value_add); return v; } //-------------------------------------------------------------------------- int dyld_cache_t::parse_slid_chain(dyld_cache_slide_visitor_t *v, uint64 start) { QASSERT(20104, slide_version == 2 || slide_version == 3); uint64 off = 0; uint64 delta = 1; int code = 0; while ( code == 0 && delta != 0 && off < slide_page_size ) { uint64 addr = start + off; uint64 raw_value = read_addr_at_va(addr); if ( raw_value == BADADDR64 ) break; uint64 untagged = untag(raw_value); code = v->visit_pointer(addr, untagged); if ( slide_version == 2 ) delta = (raw_value & slide_delta_mask) >> (tzcnt(slide_delta_mask) - 2); else delta = ((raw_value & slide_delta_mask) >> 51) * 8; off += delta; } return code; } //-------------------------------------------------------------------------- int dyld_cache_t::visit_slid_pointers(dyld_cache_slide_visitor_t *v) { QASSERT(20068, mappings.size() > 1); int code = 0; uint64 dataStartAddress = mappings[1].address; if ( slide_version == 1 ) { size_t pagesize = slide_entries_size*8*4; for ( size_t i = 0, size = slide_toc.size(); code == 0 && i < size; i++ ) { size_t off = slide_toc[i] * slide_entries_size; if ( off >= slide_entries.size() ) { msg("Corrupted DYLD slide info"); break; } const uchar *entry = &slide_entries[off]; for ( size_t j = 0; code == 0 && j < slide_entries_size; j++ ) { uint64 page = dataStartAddress+i*pagesize; uchar b = entry[j]; if ( b != 0 ) { for ( int k = 0; code == 0 && k < 8; k++ ) { if ( ((1u<visit_pointer(addr, value); } } } } } } else if ( slide_version == 2 ) { for ( size_t i = 0; code == 0 && i < slide_page_starts.size(); i++ ) { uint64 page = dataStartAddress + (i * slide_page_size); uint16 start = slide_page_starts[i]; if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) { deb(IDA_DEBUG_LDR, "page %llx has no pointers for sliding\n", page); continue; } else if ( (start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) { uint64 chain = page + (start * 4); deb(IDA_DEBUG_LDR, "page %llx: single slide chain at %llx\n", page, chain); code = parse_slid_chain(v, chain); } else { for ( size_t j = start & 0x3FFF, size = slide_page_extras.size(); code == 0 && j < size; j++ ) { uint16 extra = slide_page_extras[j]; if ( (extra & DYLD_CACHE_SLIDE_PAGE_ATTR_END) != 0 ) break; uint64 chain = page + (extra * 4); deb(IDA_DEBUG_LDR, "page %llx: extra slide chain at %llx\n", page, chain); code = parse_slid_chain(v, chain); } } } } else if ( slide_version == 3 ) { for ( size_t i = 0; code == 0 && i < slide_page_starts.size(); i++ ) { uint64 page = dataStartAddress + (i * slide_page_size); uint16 start = slide_page_starts[i]; if ( start == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) { deb(IDA_DEBUG_LDR, "page %llx has no pointers for sliding\n", page); continue; } else { uint64 chain = page + start; deb(IDA_DEBUG_LDR, "page %llx: slide chain at %llx\n", page, chain); code = parse_slid_chain(v, chain); } } } else { code = -1; } return code; } //-------------------------------------------------------------------------- // Detecting the ASLR slide in dyld_shared_cache dumps. // Reference: http://www.iphonedevwiki.net/index.php?title=Dyld_shared_cache // Most of the caches retrieved from devices have the ASLR slide applied. This means most pointers are off by the slide amount. // We use a simple algorithm to detect it: // 1. In any cache module, find an import from another dylib // 2. Read the current pointer value (impptr) // 3. Find the exported symbol in the target dylib and get its address (expea) // 4. The difference between impptr and expea is the slide value // Once we know the slide value, we can walk all pointers in the cache (helpfully indexed in the slide info tables) and repair them by subtracting the slide value. //-------------------------------------------------------------------------- bool dyld_cache_t::calc_aslr_slide(int64 *slide, int n, bool verbose, qstring *errbuf) const { linput_t *dli = create_single_macho_input(n); if ( dli == NULL ) { if ( errbuf != NULL ) errbuf->sprnt("could not load image %d from the cache", n); return false; } linput_janitor_t li_janitor(dli); macho_file_t mfile(dli, 0, MACHO_HINT_SHARED_CACHE_LIB); if ( !mfile.parse_header() || !mfile.set_subfile(0) ) { if ( errbuf != NULL ) errbuf->sprnt("failed to parse macho header for image %d", n); return false; } impvec_t ximports; dyliblist_t dylibs; rangeset_t ranges; if ( mfile.get_import_info(&ximports, &dylibs, &ranges, verbose) == 0 ) { if ( errbuf != NULL ) errbuf->sprnt("no import info found for image %d", n); return false; } for ( size_t j = 0; j < ximports.size(); j++ ) { const import_info_t &iinf = ximports[j]; if ( iinf.dylib == 0 || iinf.dylib > dylibs.size() ) continue; qstring &dylibname = dylibs[iinf.dylib - 1]; uint64 expaddr = find_exported_symbol(dylibname.begin(), iinf.name.begin(), verbose); if ( expaddr == BADADDR64 ) { if ( verbose ) msg("WARNING: could not resolve symbol '%s' from dylib '%s'!\n", iinf.name.c_str(), dylibname.c_str()); continue; } uint64 _impptr = read_addr_at_va(iinf.impea); uint64 impptr = untag(_impptr); if ( verbose ) msg("0x%08" FMT_64 "X: symbol=%s expaddr=0x%08" FMT_64 "X, impptr(raw)=0x%08" FMT_64 "X, impptr(clean)=0x%08" FMT_64 "X\n", iinf.impea, iinf.name.begin(), expaddr, _impptr, impptr); QASSERT(1332, impptr != BADADDR64); if ( !ranges.contains(impptr) ) { *slide = impptr - expaddr; if ( verbose ) msg("detected dyld slide for symbol '%s' : 0x%08" FMT_64 "X\n", iinf.name.c_str(), *slide); return true; } } if ( errbuf != NULL ) errbuf->sprnt("failed to find an exported symbol for all imports in image %d", n); return false; } //-------------------------------------------------------------------------- void dyld_cache_t::parse_local_symbols() { if ( header.localSymbolsOffset == 0 || header.localSymbolsSize == 0 ) return; if ( qlseek(li, header.localSymbolsOffset) != header.localSymbolsOffset ) return; dyld_cache_local_symbols_info si; if ( qlread(li, &si, sizeof(si)) != sizeof(si) ) return; nlistvec_t symbols; qstring strings; uint64_t symoff = header.localSymbolsOffset + si.nlistOffset; uint64_t stroff = header.localSymbolsOffset + si.stringsOffset; size_t nsymbols = si.nlistCount; size_t size = si.nlistCount * ( m64 ? sizeof(struct nlist_64) : sizeof(struct nlist)); uint64_t stend = symoff + size; uint64 mach_size = qlsize(li); if ( stend < symoff || stend > mach_size ) { msg("WARNING: local symbol table extends past end of file\n"); size = mach_size - symoff; nsymbols = size / ( m64 ? sizeof(struct nlist_64) : sizeof(struct nlist)); } if ( nsymbols != 0 ) { symbols.resize(nsymbols); if ( m64 ) { size = sizeof(struct nlist_64) * nsymbols; qlseek(li, symoff); qlread(li, &symbols[0], size); if ( mf ) swap_nlist_64(&symbols[0], &symbols[0], nsymbols); } else { qvector syms32; syms32.resize(nsymbols); size = sizeof(struct nlist) * nsymbols; qlseek(li, symoff); qlread(li, &syms32[0], size); nlist_to64(&syms32[0], &symbols[0], nsymbols, mf); } } if ( stroff >= mach_size ) { msg("WARNING: string table offset is past end of file\n"); } else { size_t strings_size; size_t strend = stroff + si.stringsSize; if ( strend < stroff || strend > mach_size ) { msg("WARNING: string table extends past end of file\n"); strings_size = mach_size - stroff; } else { strings_size = si.stringsSize; } strings.resize(strings_size); if ( strings_size != 0 ) { qlseek(li, stroff); qlread(li, &strings[0], strings_size); } } localst_symbols = symbols; localst_strings = strings; uint64 entries_off = header.localSymbolsOffset + si.entriesOffset; if ( qlseek(li, entries_off) != entries_off ) return; localst_entries.resize(si.entriesCount); size_t entries_size = sizeof(dyld_cache_local_symbols_entry) * si.entriesCount; if ( qlread(li, localst_entries.begin(), entries_size) != entries_size ) return; } //-------------------------------------------------------------------------- const dyld_cache_local_symbols_entry *dyld_cache_t::get_symbols_entry(int n) const { if ( n < 0 || n >= image_infos.size() ) return NULL; uint64 off = va2off(get_image_info(n).address); for ( size_t i = 0, size = localst_entries.size(); i < size; i++ ) { const dyld_cache_local_symbols_entry *entry = &localst_entries[i]; if ( entry->dylibOffset == off ) return entry; } return NULL; } //-------------------------------------------------------------------------- const dyld_cache_mapping_info *dyld_cache_t::get_text_mapping(void) const { for ( size_t i = 0, n = mappings.size(); i < n; i++ ) { const dyld_cache_mapping_info *mi = &mappings[i]; if ( (mi->maxProt & (VM_PROT_EXECUTE|VM_PROT_READ)) == (VM_PROT_EXECUTE|VM_PROT_READ) ) return mi; } return NULL; } //-------------------------------------------------------------------------- uint32 dyld_cache_t::get_max_text_segm(void) const { uint32 text_segm_max = 0; for ( size_t i = 0, n = text_infos.size(); i < n; i++ ) { if ( text_infos[i].textSegmentSize > text_segm_max ) text_segm_max = text_infos[i].textSegmentSize; } return text_segm_max; } //-------------------------------------------------------------------------- uint64 dyld_cache_t::get_min_image_address(void) const { uint64 minea = BADADDR64; for ( size_t i = 0, n = image_infos.size(); i < n; i++ ) { const dyld_cache_image_info &ii = image_infos[i]; if ( ii.address < minea ) minea = ii.address; } return minea; } //-------------------------------------------------------------------------- bool dyld_cache_t::get_header_range(uint64 *start, uint64 *end) const { const dyld_cache_mapping_info *mtext = get_text_mapping(); if ( mtext == NULL ) return false; uint64 minea = get_min_image_address(); if ( minea == BADADDR64 ) return false; if ( minea < mtext->address || minea >= mtext->address + mtext->size ) return false; if ( start != NULL ) *start = mtext->address; if ( end != NULL ) *end = minea; return true; } //-------------------------------------------------------------------------- bool dyld_cache_t::is_header_address(uint64 address) const { if ( address == BADADDR64 ) return false; uint64 start, end; if ( !get_header_range(&start, &end) ) return false; return address >= start && address < end; } //-------------------------------------------------------------------------- struct dyld_single_macho_linput_t : public generic_linput_t { linput_t *li_dyld; qoff64_t max_rel_off; qoff64_t start_off; dyld_single_macho_linput_t(linput_t *li_dyld_, const dyld_cache_t &cache, const dyld_cache_image_info &ii) : li_dyld(li_dyld_) { // find the text (r-x) region const dyld_cache_mapping_info *mtext = cache.get_text_mapping(); if ( mtext == NULL ) { msg("Read/execute region not found in the cache"); max_rel_off = 0; filesize = 0; } else { const dyld_cache_mapping_info &mlast = cache.get_mapping_info(cache.get_nummappings()-1); start_off = ii.address - mtext->address + mtext->fileOffset; filesize = mlast.fileOffset + mlast.size; max_rel_off = cache.get_max_text_segm(); if ( max_rel_off == 0 ) max_rel_off = mtext->size; } blocksize = 0; // don't cache } virtual ssize_t idaapi read(qoff64_t off, void *buffer, size_t nbytes) override { // offsets for the __TEXT segment are relative to the start offset of the subfile. // other segments (__DATA and __LINKEDIT) seem to use absolute offsets. if ( off < max_rel_off ) off += start_off; if ( qlseek(li_dyld, off, 0) != off ) return -1; return qlread(li_dyld, buffer, nbytes); } }; //-------------------------------------------------------------------------- linput_t *dyld_cache_t::create_single_macho_input(size_t imgindex) const { if ( get_nummappings() < 1 ) return NULL; QASSERT(20069, imgindex < image_infos.size()); const dyld_cache_image_info &ii = get_image_info(imgindex); dyld_single_macho_linput_t *dsmli = new dyld_single_macho_linput_t(li, *this, ii); return create_generic_linput(dsmli); } //-------------------------------------------------------------------------- linput_t *dyld_cache_t::create_single_island_input(size_t n) const { if ( get_nummappings() < 1 ) return NULL; dyld_cache_image_info ii; QASSERT(20119, n < island_addrs.size()); ii.address = get_island_addr(n); dyld_single_macho_linput_t *dsmli = new dyld_single_macho_linput_t(li, *this, ii); return create_generic_linput(dsmli); } //-------------------------------------------------------------------------- // Object that will free an linput_t upon deletion class linput_janitor_verbose_t { public: linput_janitor_verbose_t(linput_t *r, const qstring &filename): name(filename), resource(r) { #if 0 msg("got linput %s(%p)\n", name.c_str(), resource); #endif } ~linput_janitor_verbose_t() { #if 0 msg("closing linput %s(%p)\n", name.c_str(), resource); #endif close_linput(resource); } private: qstring name; linput_t *resource; }; //-------------------------------------------------------------------------- uint64 dyld_cache_t::find_exported_symbol( const char *_dylib, const char *symname, bool verbose) const { if ( _dylib == NULL || _dylib[0] == '\0' || symname == NULL || symname[0] == '\0' ) return BADADDR64; qstack dylibs; dylibs.push(_dylib); linput_t *dlj = NULL; while ( !dylibs.empty() ) { const qstring &dylibname = dylibs.pop(); ssize_t j = get_image_index(dylibname); dlj = create_single_macho_input(j); QASSERT(1331, dlj != NULL); linput_janitor_verbose_t li_janitor(dlj, dylibname); macho_file_t mfile(dlj, 0, MACHO_HINT_SHARED_CACHE_LIB); if ( mfile.parse_header() && mfile.set_subfile(0) ) { if ( verbose ) msg("2 searching for symbol %s in %s\n", symname, dylibname.c_str()); uint64 tmp = mfile.find_exported_symbol(symname, verbose, this); if ( tmp != BADADDR64 ) return tmp; } // not found in this dylib; check linked reexported dylibs if ( (mfile.get_mach_header().flags & MH_NO_REEXPORTED_DYLIBS) == 0 ) { dyliblist_t wlibs = mfile.get_dylib_list(LC_REEXPORT_DYLIB); for ( dyliblist_t::const_iterator p = wlibs.begin(); p != wlibs.end(); ++p ) { if ( verbose ) msg("2 checking reexported dylib %s for %s\n", p->c_str(), dylibname.c_str()); dylibs.add_unique(*p); } } } return BADADDR64; } //-------------------------------------------------------------------------- uint64 macho_file_t::find_exported_symbol( const char *symname, bool verbose, const dyld_cache_t *dcache) { if ( symname == NULL || symname[0] == '\0' ) return BADADDR64; // first try using dyld export info (which is faster and also handles tricky // reexported symbols like _memset) uint64 tmp = find_exported_symbol_dyld(symname, verbose, dcache); if ( tmp != BADADDR64 ) return tmp; // else try the symtab approach bool arm32 = !m64 && get_mach_header().cputype == CPU_TYPE_ARM; qstring strings; nlistvec_t symbols; get_symbol_table_info(&symbols, &strings); size_t nsymbols = symbols.size(); size_t strings_size = strings.size(); for ( uint32 i = 0; i < nsymbols; i++ ) { const struct nlist_64 &nl = symbols[i]; int stype = nl.n_type & N_TYPE; if ( stype == N_UNDF || stype == N_PBUD ) continue; if ( nl.n_un.n_strx >= strings_size ) continue; const char *sname = &strings[nl.n_un.n_strx]; if ( streq(symname, sname) ) { uint64 v = nl.n_value; // add thumb bit if needed if ( arm32 && (nl.n_desc & N_ARM_THUMB_DEF) != 0 ) v |= 1; return v; } } return BADADDR64; } //-------------------------------------------------------------------------- void dyld_cache_t::get_dependencies(dyldlib_set_t *p, int n, int _max) { int max = _max == -1 ? get_numfiles() : _max; if ( p == NULL || n < 0 || n >= max ) return; if ( p->find(n) != p->end() ) return; p->insert(n); linput_t *dli = create_single_macho_input(n); macho_file_t mfile(dli); if ( !mfile.parse_header() // accept_file() must have verified it || !mfile.set_subfile(0, qlsize(dli), true) ) { return; } dyliblist_t dl = mfile.get_dylib_list(); close_linput(dli); // free the linput slot before we recurse for ( size_t idl = 0; idl < dl.size(); idl++ ) { const char *dfname = dl[idl].c_str(); int l; for ( l = 0; l < max; l++ ) { const char *filename = get_image_name(l).c_str(); if ( streq(filename, dfname) ) break; } if ( l != max ) get_dependencies(p, l, max); } } //-------------------------------------------------------------------------- static bool is_valid_sym(const nlist_64 *s) { return (s->n_type & N_TYPE) == N_SECT && (s->n_type & N_STAB) == 0; } // binary search in symbols[start..s+n] static const nlist_64 *find_sym(const nlistvec_t &symbols, uint32 start, uint32 n, uint64 address,int sectionidx) { const nlist_64 *best = NULL; uint32 l = start; uint32 r = l + n; if ( r > symbols.size() ) return NULL; while ( l < r ) { uint32_t m = ( r + l ) / 2; const nlist_64 *s = &symbols[m]; if ( is_valid_sym(s) ) { if ( s->n_value <= address && ( s->n_sect == sectionidx || sectionidx == 0 ) ) { if ( best == NULL || best->n_value < s->n_value ) best = s; } } if ( s->n_value < address ) l = m + 1; else if ( m < start + 1 ) break; else r = m - 1; } return best; } //-------------------------------------------------------------------------- bool macho_file_t::get_symbol_name( qstring *name, uint64 *offset, uint64 address, int sectionidx, bool verbose, const dyld_cache_t *dcache) { // inspired by DyldInfoPrinter::closestSymbolNameForAddress in dyldinfo.cpp dysymtab_command dyst; // get the symbol table nlistvec_t symbols; qstring strings; get_symbol_table_info(&symbols, &strings); const nlist_64 *best = NULL; if ( get_dyst(&dyst) ) { // find closest match in globals best = find_sym(symbols, dyst.iextdefsym,dyst.nextdefsym, address, sectionidx); // find closest match in locals const nlist_64 *s = find_sym(symbols, dyst.ilocalsym, dyst.nlocalsym, address, sectionidx); // take the best of the two if ( best == NULL || (s != NULL ) && best->n_value < s->n_value ) best = s; } else { //search the whole symbol table best = find_sym(symbols, 0, symbols.size(), address, sectionidx); } // TODO: use dcache and check local symbol table? qnotused(dcache); qnotused(verbose); if ( best != NULL ) { // check if a neighboring symbol is a closer match uint32 m = best - &symbols[0]; if ( m < symbols.size() ) { nlist_64 *s = &symbols[m+1]; if ( is_valid_sym(s) && address - s->n_value < address - best->n_value ) best = s; } *offset = address - best->n_value; const char *sname = &strings[best->n_un.n_strx]; *name = sname; return true; } return false; } //-------------------------------------------------------------------------- uint64 dyld_cache_t::find_exported_symbol_dyld( const char *_dylib, const char *symname, bool verbose) const { if ( _dylib == NULL || _dylib[0] == '\0' || symname == NULL || symname[0] == '\0' ) return BADADDR64; qstack dylibs; dylibs.push(_dylib); linput_t *dlj = NULL; while ( !dylibs.empty() ) { const qstring &dylibname = dylibs.pop(); ssize_t j = get_image_index(dylibname); dlj = create_single_macho_input(j); QASSERT(1330, dlj != NULL); linput_janitor_verbose_t li_janitor(dlj, dylibname); macho_file_t mfile(dlj, 0, MACHO_HINT_SHARED_CACHE_LIB); if ( mfile.parse_header() && mfile.set_subfile(0) ) { if ( verbose ) msg("1 searching for symbol %s in %s\n", symname, dylibname.c_str()); uint64 tmp = mfile.find_exported_symbol_dyld(symname, verbose, this); if ( tmp != BADADDR64 ) { if ( verbose && dylibname != _dylib ) msg("symbol '%s' found in dylib '%s', address: 0x%08" FMT_64"X\n", symname, dylibname.c_str(), tmp); return tmp; } } // not found in this dylib; check linked reexported dylibs dyliblist_t wlibs = mfile.get_dylib_list(LC_REEXPORT_DYLIB); for ( dyliblist_t::const_iterator p = wlibs.begin(); p != wlibs.end(); ++p ) { if ( verbose ) msg("1 checking reexported dylib %s for %s\n", p->c_str(), dylibname.c_str()); dylibs.add_unique(*p); } } return BADADDR64; } //-------------------------------------------------------------------------- uint64 macho_file_t::find_exported_symbol_dyld( const char *symname, bool verbose, const dyld_cache_t *dcache) { if ( symname == NULL || symname[0] == '\0' ) return BADADDR64; struct ida_local find_export_visitor_t : public macho_lc_visitor_t { macho_file_t &mfile; qstring symbol; const dyld_cache_t *dyldcache; uint64 expaddr; bool verbose; find_export_visitor_t( macho_file_t &_mfile, const char *_symbol, const dyld_cache_t *_dyldcache, bool _verbose) : mfile(_mfile), symbol(_symbol), dyldcache(_dyldcache), expaddr(-1), verbose(_verbose) { } virtual ~find_export_visitor_t() {} bool load_data(uint32_t offset, uint32_t size, bytevec_t &data) { if ( size == 0 ) return false; data.resize(size); size_t newsize = size; if ( !mfile.load_linkedit_data(offset, &newsize, &data[0]) || newsize == 0 ) { return false; } return true; } const uchar *trieWalk(const uchar *start, const uchar *end, const char *s) const { const uchar *p = start; bytevec_t visited; size_t len = end - start; visited.resize((len+7)/8, 0); uint32 nodeOffset = 0; while ( p != NULL ) { if ( p >= end || p < start ) return NULL; if ( visited.test_bit(nodeOffset) ) return NULL; // endless loop visited.set_bit(nodeOffset); uint32_t terminalSize = unpack_db(&p, end); if ( terminalSize > 127 ) { // except for re-export-with-rename, all terminal sizes fit in one byte --p; if ( !unpack_uleb128(&terminalSize, &p, end) ) return NULL; } if ( *s == '\0' && terminalSize != 0 ) return p; if ( p >= end || p < start ) return NULL; const uchar *children = p + terminalSize; if ( children >= end || children < start ) return NULL; uint8_t childrenRemaining = *children++; p = children; nodeOffset = 0; if ( p >= end || p < start ) return NULL; for ( ; childrenRemaining > 0; --childrenRemaining ) { const char *ss = s; bool wrongEdge = false; // scan whole edge to get to next edge // if edge is longer than target symbol name, don't read past end of symbol name char c = *p; while ( c != '\0' ) { if ( !wrongEdge ) { if ( c != *ss ) wrongEdge = true; ++ss; } ++p; c = *p; } if ( wrongEdge ) { // advance to next child ++p; // skip over zero terminator // skip over uleb128 until last byte is found while ( ( *p & 0x80 ) != 0 ) ++p; ++p; // skil over last byte of uleb128 } else { // the symbol so far matches this edge (child) // so advance to the child's node ++p; if ( !unpack_uleb128(&nodeOffset, &p, end) ) return NULL; s = ss; break; } } p = &start[nodeOffset]; } return p; } bool process_export_info(const bytevec_t &data) { const uchar *begin = &data[0]; const uchar *end = begin + data.size(); const uchar *foundNodeStart = trieWalk(begin, end, symbol.c_str()); if ( foundNodeStart == NULL ) return false; const uchar *p = foundNodeStart; uint32 flags; if ( !unpack_uleb128(&flags, &p, end) ) return false; if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) != 0 ) { // re-export from another dylib, lookup there if ( dyldcache == NULL ) { if ( verbose ) msg("symbol '%s' is reexported but cannot follow without the dyld cache!\n", symbol.c_str()); return false; } uint32 ordinal; if ( !unpack_uleb128(&ordinal, &p, end) ) return false; qstring importedName = (char*)p; if ( importedName[0] == '\0' ) importedName = symbol; dyliblist_t dylibs = mfile.get_dylib_list(); if ( ordinal > 0 && ordinal <= dylibs.size() ) { const qstring &rdylib = dylibs[ordinal - 1]; if ( verbose ) msg("symbol '%s': looking up in reexported dylib '%s'\n", importedName.c_str(), rdylib.c_str()); expaddr = dyldcache->find_exported_symbol(rdylib.begin(), importedName.begin(), verbose); if ( verbose ) msg("symbol '%s' found in dylib '%s', address: 0x%08" FMT_64"X\n", importedName.c_str(), rdylib.c_str(), expaddr); } } else { if ( !unpack_uleb128(&expaddr, &p, end) ) return false; expaddr += mfile.get_base(); } return true; } virtual int visit_dyld_info( const struct dyld_info_command *lc, const char *, const char *) override { bytevec_t data; if ( !load_data(lc->export_off, lc->export_size, data) || !process_export_info(data) ) { expaddr = BADADDR64; } return 1; } }; find_export_visitor_t v(*this, symname, dcache, verbose); visit_load_commands(v); return v.expaddr; } //------------------------------------------------------------------------ uint64 macho_file_t::va2off(uint64 addr) { const segcmdvec_t &segs = get_segcmds(); for ( auto s : segs ) { if ( addr >= s.vmaddr ) { uint64 off = addr - s.vmaddr; if ( off >= s.vmsize ) continue; return s.fileoff + off + mach_offset; } } return BADADDR64; } //------------------------------------------------------------------------ uint64 macho_file_t::read_dword_at_va(uint64 addr) { uint32 v; uint64 off = va2off(addr); if ( off != BADADDR64 && qlseek(li, off, 0) == off && qlread(li, &v, sizeof(v)) == sizeof(v) ) { return v; } return BADADDR64; } //------------------------------------------------------------------------ uint64 macho_file_t::read_qword_at_va(uint64 addr) { uint64 v; uint64 off = va2off(addr); if ( off != BADADDR64 && qlseek(li, off, 0) == off && qlread(li, &v, sizeof(v)) == sizeof(v) ) { return v; } return BADADDR64; } //------------------------------------------------------------------------ uint64 macho_file_t::read_addr_at_va(uint64 addr) { return m64 ? read_qword_at_va(addr) : read_dword_at_va(addr); } //------------------------------------------------------------------------ char *macho_file_t::read_string_at_va(uint64 addr, char *buf, size_t bufsize) { uint64 off = va2off(addr); if ( off != BADADDR64 && qlseek(li, off, 0) == off ) return qlgetz(li, off, buf, bufsize); return buf; } //-------------------------------------------------------------------------- uint64 macho_file_t::get_kmod_ver(uint64 kinfo_ea) { if ( m64 ) return read_dword_at_va(kinfo_ea + offsetof(kmod_info_64_v1, info_version)); else return read_dword_at_va(kinfo_ea + offsetof(kmod_info_32_v1, info_version)); } //-------------------------------------------------------------------------- uint64 macho_file_t::get_kmod_start(uint64 kinfo_ea) { if ( m64 ) return read_qword_at_va(kinfo_ea + offsetof(kmod_info_64_v1, address)); else return read_dword_at_va(kinfo_ea + offsetof(kmod_info_32_v1, address)); } //-------------------------------------------------------------------------- uint64 macho_file_t::get_kmod_size(uint64 kinfo_ea) { if ( m64 ) return read_qword_at_va(kinfo_ea + offsetof(kmod_info_64_v1, size)); else return read_dword_at_va(kinfo_ea + offsetof(kmod_info_32_v1, size)); } //-------------------------------------------------------------------------- uint64 macho_file_t::get_kmod_hdr_size(uint64 kinfo_ea) { if ( m64 ) return read_qword_at_va(kinfo_ea + offsetof(kmod_info_64_v1, hdr_size)); else return read_dword_at_va(kinfo_ea + offsetof(kmod_info_32_v1, hdr_size)); } //-------------------------------------------------------------------------- uint64 macho_file_t::get_kmod_start_func(uint64 kinfo_ea) { if ( m64 ) return read_qword_at_va(kinfo_ea + offsetof(kmod_info_64_v1, start_addr)); else return read_dword_at_va(kinfo_ea + offsetof(kmod_info_32_v1, start_addr)); } //-------------------------------------------------------------------------- qstring macho_file_t::get_kmod_name(uint64 kinfo_ea) { char name[KMOD_MAX_NAME + 1]; uint64 name_ea = kinfo_ea + (m64 ? offsetof(kmod_info_64_v1, name) : offsetof(kmod_info_32_v1, name)); return read_string_at_va(name_ea, name, sizeof(name)); } //------------------------------------------------------------------------ uint64 dyld_cache_t::va2off(uint64 addr) const { for ( size_t i = 0; i < mappings.size(); i++ ) { const dyld_cache_mapping_info &m = mappings[i]; if ( addr >= m.address ) { uint64 off = addr - m.address; if ( off > m.size ) continue; return m.fileOffset + off; } } return BADADDR64; } //------------------------------------------------------------------------ uint64 dyld_cache_t::read_addr_at_va(uint64 addr) const { uint64 off = va2off(addr); if ( off != BADADDR64 && qlseek(li, off, 0) == off ) { if ( m64 ) { uint64 v; if ( qlread(li, &v, 8) != 8 ) return BADADDR64; return v; } else { uint32 v; if ( qlread(li, &v, 4) != 4 ) return BADADDR64; return v; } } return BADADDR64; } //------------------------------------------------------------------------ char *dyld_cache_t::read_string_at_va(uint64 addr, char *buf, size_t bufsize) const { uint64 off = va2off(addr); if ( off != BADADDR64 && qlseek(li, off, 0) == off ) return qlgetz(li, off, buf, bufsize); return buf; } //-------------------------------------------------------------------------- void dyld_cache_t::get_modules(intvec_t *out) const { out->resize(image_infos.size()); std::iota(out->begin(), out->end(), 0); } //-------------------------------------------------------------------------- void dyld_cache_t::get_islands(intvec_t *out) const { out->resize(island_addrs.size()); std::iota(out->begin(), out->end(), 0); } //-------------------------------------------------------------------------- bool macho_file_t::get_prelink_info(section_64 *prelink_info) { return get_section("__PRELINK_INFO", "__info", prelink_info) || get_section("__PRELINK", "__info", prelink_info); } //-------------------------------------------------------------------------- bool macho_file_t::get_prelink_text(section_64 *prelink_text) { return get_section("__PRELINK_TEXT", SECT_TEXT, prelink_text) || get_section("__PRELINK", SECT_TEXT, prelink_text); } //------------------------------------------------------------------------ bool macho_file_t::get_prelink_data(section_64 *prelink_data) { return get_section("__PRELINK_DATA", SECT_DATA, prelink_data); } //-------------------------------------------------------------------------- uint64 macho_file_t::find_next_magic(uint64 off, uint64 endoff) { char buf[0x1000]; uint64 size = endoff - off; while ( size > 0 ) { if ( qlseek(li, off, 0) != off ) return endoff; size_t n = qmin(size, sizeof(buf)); size_t nread = qlread(li, buf, n); if ( nread != n || nread % 4 != 0 ) return endoff; const char *ptr = buf; const char *end = buf + nread; for ( ; ptr < end; ptr += sizeof(uint32) ) { uint32 magic = *(const uint32 *)ptr; if ( is_magic(magic) || is_cigam(magic) ) return off + (ptr-buf); } size -= nread; off += nread; } return endoff; } //------------------------------------------------------------------------ void macho_file_t::scan_for_kexts(void) { section_64 prelink_text; if ( !get_prelink_text(&prelink_text) || prelink_text.size == 0 ) return; uint64 startoff = prelink_text.offset + mach_offset; uint64 curoff = startoff; size_t maxsize = prelink_text.size; uint64 end = curoff + maxsize; for ( size_t i = 0; maxsize > 0 && curoff < end; i++ ) { uint64 nextoff = find_next_magic(curoff, end); if ( nextoff == end ) break; maxsize = end - nextoff; curoff = nextoff; size_t ksize = maxsize; // determine the subfile's size linput_t *kmodli = create_single_kmod_input(curoff); QASSERT(20125, kmodli != NULL); linput_janitor_t li_janitor(kmodli); macho_file_t mfile(kmodli); uint64 base = BADADDR64; uint64 maxea = 0; uint64 filesize = 0; if ( mfile.parse_header() && mfile.set_subfile(0, ksize, true) ) { const segcmdvec_t &segcmds = mfile.get_segcmds(); for ( size_t si = 0; si < segcmds.size(); si++ ) { // walk the segments, look for the base address const segment_command_64 &sg = segcmds[si]; if ( base == BADADDR64 && sg.fileoff == 0 ) base = sg.vmaddr; filesize += sg.filesize; uint64 send = sg.vmaddr + sg.vmsize; if ( maxea < send ) maxea = send; } } if ( filesize == 0 || maxea == 0 ) { // parsing failed. skip this macho magic maxsize -= sizeof(uint32); curoff += sizeof(uint32); continue; } // save the base address of the kext within the prelink text segment. // this is not necessarily the same as the final base address that is specified // in the kext's header, since it could be relocated. uint64 loadaddr = prelink_text.addr + (curoff - startoff); if ( base == BADADDR64 ) { // no segment had file offset of 0 // find the next mach-o to figure out the size of the current one uint64 next = find_next_magic(curoff+sizeof(uint32), end); ksize = next - curoff; base = loadaddr; } else { // align on page boundary maxea = (maxea + 0xFFF) & ~uint64(0xFFFu); ksize = maxea - base; } // see if we have a kmod_info uint64 kea = mfile.find_exported_symbol("_kmod_info"); kmod_params &kp = kpv.push_back(); kp.kstart = base; kp.kinfo_ea = kea; kp.ksize = ksize; kp.off = curoff; kp.loadaddr = loadaddr; mfile.get_uuid(kp.uuid); if ( kea != BADADDR64 && mfile.get_kmod_ver(kea) == 1 ) kp.name = mfile.get_kmod_name(kea); if ( kp.name.empty() ) kp.name.sprnt("prelink_mod_%u", int(i)); if ( ksize <= maxsize ) maxsize -= ksize; curoff += filesize; } } #ifndef BUILD_IDAPYSWITCH //-------------------------------------------------------------------------- // implementation of the file read callback for qstrings class qstring_read_callback_t : public IFileReadCallBack { qstring text; size_t pos; public: // construct from string qstring_read_callback_t(const qstring &t): text(t), pos(0) {} // read the specificed amount of bytes from the file virtual int read(void *buffer, int sizeToRead) override { if ( sizeToRead < 0 ) return 0; size_t end = pos + sizeToRead; if ( end < pos ) // overflow return 0; if ( end > text.length() ) { end = text.length(); sizeToRead = end - pos; } if ( sizeToRead > 0 ) memcpy(buffer, &text[pos], sizeToRead); pos += sizeToRead; return sizeToRead; } // return size of file in bytes virtual int getSize() override { return text.length(); } }; //--------------------------------------------------------------------------- static IrrXMLReader *createPrelinkReaderQstring(const qstring &text) { return new CXMLReaderImpl(new qstring_read_callback_t(text)); } //-------------------------------------------------------------------------- bool macho_file_t::parse_prelink_xml(void) { section_64 prelink_info; if ( !get_prelink_info(&prelink_info) ) return false; uint64 size = prelink_info.size; if ( size == 0 ) return false; qstring xml; xml.resize(size, '\0'); uint64 off = prelink_info.offset + mach_offset; if ( qlseek(li, off, 0) != off || qlread(li, xml.begin(), size) != size ) return false; xml.resize(qstrlen(xml.c_str())); if ( xml.empty() ) return false; struct ida_local prelink_xml_parser_t { IrrXMLReader *reader; macho_file_t *kcache; kmod_params_vec_t *kpv; qstring curnode; qstring curkey; prelink_xml_parser_t(const qstring &_xml, macho_file_t *_kcache, kmod_params_vec_t *_kpv) : reader(createPrelinkReaderQstring(_xml)), kcache(_kcache), kpv(_kpv) {} ~prelink_xml_parser_t(void) { delete reader; kpv = NULL; } bool parse_element(void) { while ( reader->read() ) { switch ( reader->getNodeType() ) { case EXN_ELEMENT: { if ( !reader->isEmptyElement() ) { curnode = reader->getNodeName(); parse_element(); } } break; case EXN_TEXT: { uint64 num = 0; qstring text = reader->getNodeData(); if ( curnode == "key" ) { curkey = text; } else if ( curnode == "integer" && curkey == "_PrelinkKmodInfo" ) { num = strtoull(text.c_str(), NULL, 0); } else if ( curnode == "data" && curkey == "OSBundlePrelink" ) { // get a big-endian uint32 from string bytevec_t kidata; if ( base64_decode(&kidata, text.c_str(), text.length()) && kidata.size() >= 16 ) swap_value(&num, kidata.begin(), 4); } // parse the kmod_info struct if ( num != 0 && kcache->get_kmod_ver(num) == 1 ) { kmod_params &kp = kpv->push_back(); kp.kinfo_ea = num; kp.ksize = kcache->get_kmod_size(num); kp.kstart = kcache->get_kmod_start(num); kp.name = kcache->get_kmod_name(num); kp.off = kcache->va2off(kp.kstart); kp.loadaddr = kp.kstart; // TODO: we assume the kext isn't relocated. this might be incorrect, but I don't have an example that proves it. // extract the UUID linput_t *kli = kcache->create_single_kmod_input(kp.off); QASSERT(20126, kli != NULL); linput_janitor_t lij(kli); macho_file_t mfile(kli); if ( mfile.parse_header() && mfile.set_subfile(0) ) mfile.get_uuid(kp.uuid); } } break; case EXN_ELEMENT_END: return true; default: break; } } return false; } }; prelink_xml_parser_t parser(xml, this, &kpv); bool ok = true; while ( ok ) ok = parser.parse_element(); return true; } #else bool macho_file_t::parse_prelink_xml(void) { return false; } #endif // BUILD_IDAPYSWITCH //------------------------------------------------------------------------ bool macho_file_t::parse_kmod_starts(void) { if ( !m64 ) return false; section_64 kmod_start; section_64 kmod_info; section_64 text_exec; if ( !get_section("__PRELINK_INFO", "__kmod_start", &kmod_start) || kmod_start.size == 0 ) return false; if ( !get_section("__PRELINK_INFO", "__kmod_info", &kmod_info) || kmod_info.size == 0 ) return false; if ( !get_section("__TEXT_EXEC", SECT_TEXT, &text_exec) || text_exec.size == 0 ) return false; uint32 off1 = kmod_start.offset + mach_offset; uint32 off2 = kmod_info.offset + mach_offset; uint32 end1 = off1 + kmod_start.size; uint32 end2 = off2 + kmod_info.size; // parse the array of kmod start addresses in __kmod_start. // assume an ordered 1-1 mapping between the start eas in __kmod_start // and the kmod_info structures __kmod_info. for ( ; off1 < end1 && off2 < end2; off1 += 8, off2 += 8 ) { uint64 kstart = 0; uint64 kinfo_ea = 0; if ( qlseek(li, off1, 0) != off1 || qlread(li, &kstart, sizeof(kstart)) != sizeof(kstart) || qlseek(li, off2, 0) != off2 || qlread(li, &kinfo_ea, sizeof(kinfo_ea)) != sizeof(kinfo_ea) ) { break; } kstart = untag(kstart); kinfo_ea = untag(kinfo_ea); // sanity check if ( kstart < text_exec.addr || kstart >= text_exec.addr + text_exec.size ) { continue; } uint64 off = va2off(kstart); linput_t *kmodli = create_single_kmod_input(off); QASSERT(20122, kmodli != NULL); linput_janitor_t li_janitor(kmodli); macho_file_t mfile(kmodli); if ( !mfile.parse_header() || !mfile.set_subfile(0) ) continue; // found a valid kext, determine its size uint64 ksize = 0; const segcmdvec_t &segcmds = mfile.get_segcmds(); for ( size_t si = 0; si < segcmds.size(); si++ ) { const segment_command_64 &sg = segcmds[si]; if ( sg.fileoff == 0 ) { ksize = sg.vmsize; break; } } if ( ksize == 0 ) continue; // append to list of kexts kmod_params &kp = kpv.push_back(); kp.kstart = kstart; kp.ksize = ksize; kp.kinfo_ea = kinfo_ea; kp.name = get_kmod_name(kinfo_ea); kp.off = off; kp.loadaddr = kp.kstart; // iOS 12 kexts aren't relocated mfile.get_uuid(kp.uuid); } return true; } //------------------------------------------------------------------------ void macho_file_t::parse_kmod_info(void) { // try to populate the array of kmod_params if ( !parsed_kmod_info ) { // There are 3 ways to detect prelinked KEXTs: // // 1. parse __kmod_start/__kmod_info. // these sections make our life easy, but they are only present in kernelcaches // for iOS 12 and later. // // 2. parse the XML in __PRELINK_INFO. // this approach would be ok if Apple didn't change the XML format // all the goddamn time. // // 3. if steps 1 and 2 fail, we fall back to scanning the __PRELINK_TEXT/__TEXT_EXEC // segment for Mach-O magic, and parse the subfiles. // if ( !parse_kmod_starts() || kpv.empty() ) { // the XML is highly unstable, allow users to override it if ( qgetenv("IDA_KCACHE_IGNORE_XML", NULL) || !parse_prelink_xml() || kpv.empty() ) scan_for_kexts(); } parsed_kmod_info = true; } } //------------------------------------------------------------------------ const kmod_params_vec_t &macho_file_t::get_kmod_info(void) { parse_kmod_info(); return kpv; } //-------------------------------------------------------------------------- int macho_file_t::get_kmod_idx(const qstring &name) const { for ( size_t i = 0, size = kpv.size(); i < size; i++ ) { if ( kpv[i].name == name ) return i; } return -1; } //-------------------------------------------------------------------------- // process tagged pointers in an ARM64 kernelcache // initial analysis was based on http://bazad.github.io/2018/06/ios-12-kernelcache-tagged-pointers/ // but it did not take into account the final format that includes PAC support. // the actual format is like following: // __TEXT:__thread_starts is an array of 32-bit ints // first one is flags, determining the skip size (4 or 8) // the rest are offsets to starts of tagged pointer chains from the image base // each chain contains pointers with tags in upper bits. // bit 63: 1 if pointer is authenticated (protected with PAC) // bit 62: 0 if pointer is tagged and needs to be rebased // bits 51..61: skip count to the next pointer in chain (0 if last one) // for PAC pointers: // 49..50: key index (IA/IB/DA/DB) // 32..47: diversity // 0..31: offset from base // for non-PAC pointers: // 50..0: sign-extended pointer value // TODO: unify processing with dyld_cache_t::parse_slid_chain/untag //------------------------------------------------------------------------ int macho_file_t::visit_threaded_pointers(kcache_pointer_visitor_t &v) { bytevec_t data; if ( !get_section(SEG_TEXT, "__thread_starts", &data) || data.empty() ) return -1; uint32 *items = (uint32*)data.begin(); size_t count = data.size() / sizeof(uint32); if ( count <= 1 ) return -1; // need to have at least one thread uint32 flags = items[0]; int skipsize = (flags & 1) ? 8 : 4; for ( size_t i=1; i < count; i++ ) { uint32 off = items[i]; if ( off == uint32(-1) ) break; uint64 ea = base_addr + off; int delta = 0; do { uint64 raw_value = read_addr_at_va(ea); if ( raw_value == BADADDR64 ) break; int code = v.visit_pointer(ea, raw_value, untag(raw_value)); if ( code != 0 ) return code; delta = (raw_value >> 51) & 0x7FF; ea += delta * skipsize; } while ( delta != 0 ); } return 0; } //------------------------------------------------------------------------ uint64 macho_file_t::untag(uint64 value) const { uint64 untagged = value; if ( (value & (1ull << 62)) == 0 ) { // signed pointer? if ( value & (1ull << 63) ) { // use low 32 bits as offset from imagebase untagged = base_addr + uint32(value); } else { // non-signed, adjust with 51-bit sign extension untagged = UNTAG_51BIT_SE(value); } } return untagged; } //------------------------------------------------------------------------ struct kcache_single_kmod_input_t : public generic_linput_t { linput_t *kcache_li; qoff64_t start_off; qoff64_t prelink_data_off; kcache_single_kmod_input_t(linput_t *_kcache_li, qoff64_t _start_off, uint64 _prelink_data_off) : kcache_li(_kcache_li), start_off(_start_off), prelink_data_off(_prelink_data_off) { filesize = qlsize(kcache_li); // kexts can have indeterminate size blocksize = 0; } virtual ssize_t idaapi read(qoff64_t off, void *buffer, size_t nbytes) override { // offsets in a kext's load commands are _usually_ relative to the base offset of the kext. // it seems there is one exception: if the kernelcache has a common data segment for all kexts. // offsets into this segment are absolute. if ( prelink_data_off == 0 || off < prelink_data_off ) off += start_off; if ( qlseek(kcache_li, off, 0) != off ) return -1; return qlread(kcache_li, buffer, nbytes); } }; //------------------------------------------------------------------------ linput_t *macho_file_t::create_single_kmod_input(qoff64_t start_off) { section_64 s; uint64 prelink_data_off = get_prelink_data(&s) ? s.offset : 0; kcache_single_kmod_input_t *kli = new kcache_single_kmod_input_t( li, start_off, prelink_data_off); return create_generic_linput(kli); } #if defined(LOADER_COMPILE) || defined(BUILD_DWARF) //---------------------------------------------------------------------- static linput_t *ida_export create_idb_linput(ea_t start, asize_t size) { struct idb_linput_t : public generic_linput_t { ea_t start; idb_linput_t(ea_t _start, asize_t _size) : start(_start) { filesize = _size; blocksize = 0; } virtual ssize_t idaapi read(qoff64_t off, void *buffer, size_t nbytes) override { return get_bytes(buffer, nbytes, start+off); } }; idb_linput_t *dml = new idb_linput_t(start, size); linput_t *li = create_generic_linput(dml); return li; } #endif