可执行文件 Mach-O

Mach-O 类型的文件:是一种用于可执行文件、目标代码、动态库、内核转储的文件格式;

使用工具 MachOView 查看 Mach-O 文件结构

Mach-O 文件的大致结构.png

分析上图可知,Mach-O 文件主要包含三个区域:

  • 1、mach_header_64 头部: 描述了 Mach-O 的CPU架构、文件类型以及加载命令等信息。
  • 2、Load Commands 加载命令: 描述了文件中数据的具体组织结构,不同的数据类型使用不同的加载命令表示
  • 3、Data: load_command 中定义的原始数据

1、头部mach_header_64

苹果源码 XNU 中的 loader.h 中找到结构mach_headermach_header_64 分别描述 32 位与 64 位的 Mach-O 头部信息。

struct mach_header {
    uint32_t    magic;// Mach-O 文件支持设备的CPU位数, 32位 CPU 取值 oxFEEDFACE 
    cpu_type_t  cputype;// CPU类型
    cpu_subtype_t   cpusubtype; // CPU 子类型
    uint32_t    filetype;   //文件类型,比如可执行文件、库文件、Dsym文件;
    uint32_t    ncmds;      //加载命令的数量
    uint32_t    sizeofcmds; //所有加载命令的大小
    uint32_t    flags;      //dyld 加载所需的标记:MH_PIE 表示启动地址空间布局随机化
};

struct mach_header_64 {
    uint32_t    magic;// Mach-O 文件支持设备的CPU位数,64位取值 xFEEDFACF
    cpu_type_t  cputype;    // CPU类型
    cpu_subtype_t   cpusubtype;// CPU 子类型
    uint32_t    filetype;   //文件类型,比如可执行文件、库文件、Dsym文件;
    uint32_t    ncmds;      //加载命令的数量
    uint32_t    sizeofcmds; //所有加载命令的大小
    uint32_t    flags;      //dyld 加载所需的标记:MH_PIE 表示启动地址空间布局随机化
    uint32_t    reserved;   //64 位的保留字段
};
1.1、结构成员 magic

结构成员 magic表示 Mach-O 文件支持设备的CPU位数, XNU 中有预定义的常量:

// mach_header(32位) 的 magic 字段的常量
#define MH_MAGIC    0xfeedface // 表示32位二进制
#define MH_CIGAM    NXSwapInt(MH_MAGIC)

//mach_header_64(64位) 的 magic 字段的常量
#define MH_MAGIC_64 0xfeedfacf //表示64位二进制
#define MH_CIGAM_64 NXSwapInt(MH_MAGIC_64)

上述四个常量很有用处,用于判断当前可执行文件能否支持设备的 CPU:比如 Runtime 库objc-os.mm文件中函数 bad_magic()

bool bad_magic(const headerType *mhdr){
    return (mhdr->magic != MH_MAGIC  &&  mhdr->magic != MH_MAGIC_64  &&
            mhdr->magic != MH_CIGAM  &&  mhdr->magic != MH_CIGAM_64);
}

该函数判断当前可执行文件,如果既不支持 32 位 CPU,又不支持 64 位 CPU,则返回 YES

1.2、结构成员 filetype

结构成员 filetype表示 Mach-O 文件的类型,关于它的常量有:

#define MH_OBJECT   0x1 //可重定位目标文件:.o文件 .a/.framework静态库
#define MH_EXECUTE  0x2 //请求分页的可执行文件: app/MyApp ; .out
#define MH_FVMLIB   0x3 //固定VM共享库文件
#define MH_CORE     0x4 //核心文件
#define MH_PRELOAD  0x5 //预加载可执行文件
#define MH_DYLIB    0x6 //动态库 .framework  .dylib
#define MH_DYLINKER 0x7 //动态链接器 usr/lib/dyld
#define MH_BUNDLE   0x8 //动态绑定 Bundle 文件

在本文,笔者分析的就是 .app 文件,通过运行一个 Demo 得到!从 Mach-O 文件的大致结构 图 可以看到该文件 .app 文件的类型就是 MH_EXECUTE

1.2.1、.app 文件

苹果是如何将一个.app文件加载到 dyld2 呢?笔者在 dyld2 库ImageLoaderMachO.cpp 文件中发现了函数 ImageLoaderMachO()

// 为主程序创建镜像
ImageLoaderMachO::ImageLoaderMachO(const struct mach_header* mh, uintptr_t slide, const char* path, const LinkContext& context)
 : ImageLoader(path)
{
    this->init();
    fMachOData = (const uint8_t*)mh;
    this->instantiateSegments((const uint8_t*)mh);
    this->setSlide(slide);
    this->parseLoadCmds();
    this->adjustSegments();
#if __i386__
    if ( fReadOnlyImportSegment != NULL )
        fReadOnlyImportSegment->tempWritable(context, this);
#endif  
    if ( mh->flags & MH_PIE )
        Segment::fgNextPIEDylibAddress = (uintptr_t)this->getEnd(); 
    this->setMapped(context);
    if ( context.verboseMapping ) {
        dyld::log("dyld: Main executable mapped %s\n", this->getPath());
        for (ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) {
            Segment* seg = *it;
            if ( (strcmp(seg->getName(), "__PAGEZERO") == 0) || (strcmp(seg->getName(), "__UNIXSTACK") == 0)  )
                dyld::log("%18s at 0x%08lX->0x%08lX\n", seg->getName(), seg->getPreferredLoadAddress(), seg->getPreferredLoadAddress()+seg->getSize());
            else
                dyld::log("%18s at 0x%08lX->0x%08lX\n", seg->getName(), seg->getActualLoadAddress(this), seg->getActualLoadAddress(this)+seg->getSize());
        }
    }
}

此处笔者不对该函数做过多解释,以免跑题! 读者需要知道的就是:本文分析的 Mach-O 文件就是为了在 dyld2 中加载的!

1.2.2、MH_BUNDLE 文件

MH_BUNDLE 类型的文件 是不能加载到共享缓存之中的,所以在加载Mach-O 文件时 Runtimeobjc-runtime-new.mm 文件中函数 mustReadClasses(),会被调用来检查是否有 Bundle类:

bool mustReadClasses(header_info *hi){
    const char *reason;
    if (!hi->isPreoptimized()) {
        reason = nil;
        goto readthem;
    }    
#if TARGET_OS_SIMULATOR
    reason = "the image is for iOS simulator";
    goto readthem;
#endif
    assert(!hi->isBundle());  // no MH_BUNDLE in shared cache    
    if (!noMissingWeakSuperclasses()) {
        reason = "the image may contain classes with missing weak superclasses";
        goto readthem;
    }    
    if (haveFutureNamedClasses()) {
        reason = "there are unresolved future classes pending";
        goto readthem;
    }    
    return NO;
readthem:
    if (PrintPreopt  &&  reason) {
        _objc_inform("PREOPTIMIZATION: reading classes manually from %s because %s", hi->fname(), reason);
    }
    return YES;
}

我们可以看到该函数的关键代码 assert(!hi->isBundle()) ,如果是MH_BUNDLE类型文件,则无法通过断言!确保 Bundle 类不会被放到共享缓存!

1.3、结构成员 flags

结构成员 flags有以下几个常量

#define MH_NOUNDEFS 0x1 //目标文件没有未定义的引用,可以执行
#define MH_INCRLINK 0x2  //目标文件是针对基本文件的增量链接的输出,不能再次链接编辑
#define MH_DYLDLINK 0x4 //目标文件是动态链接器的输入,不能再次静态链接编辑
#define MH_BINDATLOAD   0x8 //加载时,目标文件的未定义引用由动态链接器绑定。
#define MH_PREBOUND 0x10 //该文件具有预先绑定的动态未定义引用。
1.4、结构成员 ncmdssizeofcmds

Mach-O 文件的大致结构 图 可以看到:

  • load_command 紧跟mach_header_64 之后;
  • 图中 Size of load Commands的取值是 3496,该值是结构mach_header_64 的成员sizeofcmds,表示所有加载命令load_command所占内存之和;
  • 图中 Number of load Commands的取值是 21,该值是结构mach_header_64 的成员ncmds,表示所有加载命令load_command的个数;
ncmds 与 sizeofcmds.png

2、加载命令部分Load commands

Load commands紧跟mach_header_64 之后;告诉操作系统应当如何加载文件中的数据,对系统内核加载器和动态链接器起指导作用。
一个 Mach-O 文件中有多个加载命令Load commands,当然这些load_command也具有不同的类型!

load_command 分析.png
2.1、加载命令load_command

无论查看哪种类型的load_command,我们都可以发现前两个字段总是cmdcmdsize,如上图所示:相同的 cmd下它们的结构成员也是相同的!

struct load_command {
    unsigned long cmd;
    unsigned long cmdsize;
};
  • 结构成员cmd表示该条加载命令的类型,每种 load_command 类型都有专门针对它的结构;
  • 结构成员cmdsize表示load_command 的总大小:结构体大小 + 加上包含的节、字符串等;

要前进到下一个 load_command,可以将 cmdsize 添加到当前 load_command 的偏移量或指针: 32位的 cmdsize 必须是4个字节的倍数,64位的 cmdsize ,必须是8个字节的倍数;永远是 load_command 的最大对齐:sizeof(long)
填充字节必须为零。

目标文件中的所有表也必须遵循这些规则,以便文件可以进行内存映射;否则,指向这些表的指针在某些 CPU 将无法正常工作或根本无法正常工作。所有填充为 0 的对象将逐个字节进行比较。

2.2.1、加载命令load_command的类型

在上图可以看到不同的 cmd下它们的结构成员大不相同,那么都有哪些不同的类型呢?苹果为 load_command 的类型定义了一些常量:

#define LC_SEGMENT  0x1 //该文件被映射的段
#define LC_SYMTAB   0x2 //为文件定义符号表和字符串表,在连接文件时被链接器使用,同时也用于调试器映射符号到源文件。符号表定义的本地符号仅用于本地测试,而已定义和未定义的 external 符号被链接器使用
#define LC_SYMSEG   0x3 //符号表信息,符号表中详细说明了代码中所用符号的信息等(过时)
#define LC_THREAD   0x4 //线程
#define LC_UNIXTHREAD   0x5 //unix线程(包括堆栈)
#define LC_LOADFVMLIB   0x6 //加载指定的固定VM共享库
#define LC_IDFVMLIB 0x7 //固定VM共享库的标识
#define LC_IDENT    0x8 //object 标识信息(已过时)
#define LC_FVMFILE  0x9 /* fixed VM file inclusion (internal use) */
#define LC_PREPAGE      0xa     //prepage 命令(内部使用)
#define LC_DYSYMTAB 0xb //将符号表中给出符号的额外符号信息提供给动态链接器
#define LC_LOAD_DYLIB   0xc //依赖的动态库,包括动态库名称、当前版本号、兼容版本号,(可以使用 otool -L xxx 命令查看)
#define LC_ID_DYLIB 0xd //动态链接共享库的标识
#define LC_LOAD_DYLINKER 0xe //默认的加载器路径
#define LC_ID_DYLINKER  0xf //动态链接器识别
#define LC_PREBOUND_DYLIB 0x10  /* modules prebound for a dynamicly */
LC_SEGMENT :段的映射

LC_SEGMENT类型的加载命令是最常见的 load_command 了,该命令被映射到段segment_command_64,关于段的详细讲解,笔者放到下一节再提及!

2.2、段segment_command_64

先在 XNU 中的 loader.h 中找到结构segment_command_64的实现:

struct segment_command {// 32 位
    unsigned long   cmd;//load_command结构成员cmd的取值,取值 LC_SEGMENT 将文件中的段映射到进程地址空间
    unsigned long   cmdsize;//load_command结构大小
    char        segname[16];// 16字节的段名字
    unsigned long   vmaddr; //映射到虚拟地址的偏移
    unsigned long   vmsize; //映射到虚拟地址的大小
    unsigned long   fileoff;//对应于当前架构文件的偏移(注意:是当前架构,不是整个 FAT 文件)
    unsigned long   filesize;//文件大小
    vm_prot_t   maxprot;//段里面的最高内存保护
    vm_prot_t   initprot;//初始内存保护
    unsigned long   nsects;//该段包含的节个数
    unsigned long   flags;//段页面标志
};

struct segment_command_64 {//64 位
    uint32_t    cmd;//load_command结构成员cmd的取值,取值 LC_SEGMENT 将文件中的段映射到进程地址空间
    uint32_t    cmdsize;//load_command结构大小
    char        segname[16];// 16字节的段名字
    uint64_t    vmaddr; //映射到虚拟地址的偏移
    uint64_t    vmsize; //映射到虚拟地址的大小
    uint64_t    fileoff;//对应于当前架构文件的偏移(注意:是当前架构,不是整个 FAT 文件)
    uint64_t    filesize;//文件大小
    vm_prot_t   maxprot;//段里面的最高内存保护
    vm_prot_t   initprot;//初始内存保护
    uint32_t    nsects;//该段包含的节个数
    uint32_t    flags;//段页面标志 : 表示节的标志
};

通过对比第2节的 load_command 分析图可以看出,LC_SEGMENT类型的load_command中的结构成员对应着段segment_command_64的成员,其中:

segment_command_64的结构成员 cmd 取值一定为 LC_SEGMENT,表示是段命令,该cmd 也对应着load_command中的cmd
cmdsize 也是如此!

2.2.1、结构成员segname

结构成员segname将段分为不同类型的段,在第2节的 load_command 分析图中,可以看到相同LC_SEGMENT类型的load_command有四个,但是这四个load_commandsegname不同。

结构成员segname代表该段的名字,是一个 16字节的字符串!苹果为其提供了几个常量

//空指针陷阱段,映射到虚拟内存空间的第 1 页,用于捕捉 MH_EXECUTE 文件对 NULL 指针的引用
#define SEG_PAGEZERO "__PAGEZERO" 

#define SEG_TEXT    "__TEXT" //传统 UNIX 代码段,只读不可写
#define SEG_DATA    "__DATA" //读取和写入数据的程序数据段
#define SEG_OBJC    "__OBJC"    //objective-C runtime segment
#define SEG_ICON     "__ICON"   //icon segment
#define SEG_LINKEDIT    "__LINKEDIT" //由链接编辑器创建和维护的所有结构的段,仅为 MH_EXECUTE 和 FVMLIB 类型的文件使用 ld(1) 的- seglinkedit 选项创建
#define SEG_UNIXSTACK   "__UNIXSTACK" //unix堆段
segment_command_64 分析.png

在不同segname的段中,还有一堆更细的分类,这些细致分类是一个个的节section_64 !不同的segname各自存储着它们自己的节section_64

a、__TEXT

__TEXT段中常见的节类型:

__text //程序可执行的代码区域
__stubs //间接符号存根,跳转到懒加载指针表
__stub_helper //帮助解决懒加载符号加载的辅助函数
__objc_methname //OC方法名称
__objc_methtype //OC方法签名
__objc_classname //OC类名
__cstring //只读的 C 字符串,包含 OC 的部分字符串和属性名
__const //常量
b、__DATA

__DATA段中常见的节类型

__nl_symbol_ptr //非懒加载指针表,在 dyld  加载时会立即绑定值
__la_symbol_ptr //懒加载指针表,第一次调用时才会绑定值
__got // 非懒加载全局指针表
__mod_init_func // constructor 函数
__mod_term_func // destructor 函数
__cfstring //OC 字符串
__objc_classlist //文件中类的列表
__objc_nlclslist //文件中自己实现了 +load 方法的类
__objc_protolist //协议列表
__objc_classrefs // 被引用的类列表
__objc_imageinfo // OC镜像信息
__objc_protollist //OC原型列表
__objc_const //OC常量
__objc_selfrefs //OC类自引用(self)
__objc_superrefs //OC类超类引用(super)
__objc_protolrefs // OC原型引用
__objc_selrefs //被引用SEL对应的字符串
__objc_msgrefs //
__objc_nlclslist//获取非懒加载的所有的类的列表
__objc_catlist//获取文件中的 category
__objc_nlcatlist //获取非懒加载的所有的分类的列表
__objc_protorefs //OC 协议引用
__objc_init_func //
__bss //没有初始化和初始化为0 的全局变量
__data //真正初始化的数据节没有填充,没有bss重叠
__common //链接编辑器在节中分配公共符号
2.3、节 section_64

说了这么多的节类型,我们还是先看看节 section_64的具体实现吧:

struct section {// 32 位
    char        sectname[16];//节的名字
    char        segname[16];//节所在段的名字
    unsigned long   addr;//映射到虚拟地址的偏移
    unsigned long   size;//节的大小
    unsigned long   offset;//节在当前架构文件中的偏移
    unsigned long   align;//节的字节对齐大小 n ,计算结果为 2^n
    unsigned long   reloff;//重定位入口的文件偏移
    unsigned long   nreloc; //重定位入口的个数
    unsigned long   flags;//节的类型和属性
    unsigned long   reserved1;  //保留位
    unsigned long   reserved2;  //保留位
};

struct section_64 { // 64 位
    char        sectname[16];//节的名字
    char        segname[16];//节所在段的名字
    uint64_t    addr;//映射到虚拟地址的偏移
    uint64_t    size;//节的大小
    uint32_t    offset;//节在当前架构文件中的偏移
    uint32_t    align;  //节的字节对齐大小 n ,计算结果为 2^n
    uint32_t    reloff;//重定位入口的文件偏移
    uint32_t    nreloc; //重定位入口的个数
    uint32_t    flags;//节的类型和属性
    uint32_t    reserved1;  //用于偏移量或索引
    uint32_t    reserved2;  //数量或大小
    uint32_t    reserved3;//保留位
};

笔者在上节分析的那么多的节类型,就是 section_64的结构成员sectname,不同的节名sectname代表不同的含义!

2.3.1、节 section_64flags
/* section_64 结构的flags字段表示属性部分的常量
 */
#define SECTION_ATTRIBUTES_USR   0xff000000 /* User setable attributes */
#define S_ATTR_PURE_INSTRUCTIONS 0x80000000 /* section contains only true machine instructions */
#define SECTION_ATTRIBUTES_SYS   0x00ffff00 /* system setable attributes */
#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 /* section contains some machine instructions */
#define S_ATTR_EXT_RELOC     0x00000200 /* section has external relocation entries */
#define S_ATTR_LOC_RELOC     0x00000100 /* section has local relocation entries */
2.3.2、虚拟地址
虚拟地址.png

虚拟地址section_64-> addr,表示该节的内容映射到虚拟内存时相对该模块加载基地址的偏移。
运行程序,获取该模块加载基地址 (该地址每次运行程序,都是不同的),然后加上偏移的虚拟地址:

(lldb) im li -o -f RuntimeUseDemo
[  0] 0x0000000001584000 /Users/longlong/Library/Developer/Xcode/DerivedData/SourceCode-gbviaxaumpwhkieshjwwgrxrxpva/Build/Products/Debug-iphonesimulator/RuntimeUseDemo.app/RuntimeUseDemo
(lldb) p/x 0x00000001000019C0 + 0x0000000001584000
(long) $0 = 0x00000001015859c0

0x00000001015859c0这个地址反汇编:

(lldb) dis -a 0x00000001015859c0
RuntimeUseDemo`main:
    0x1015859c0 <+0>:   pushq  %rbp
    0x1015859c1 <+1>:   movq   %rsp, %rbp
    0x1015859c4 <+4>:   subq   $0x40, %rsp
    0x1015859c8 <+8>:   movl   $0x0, -0x4(%rbp)
    0x1015859cf <+15>:  movl   %edi, -0x8(%rbp)
    0x1015859d2 <+18>:  movq   %rsi, -0x10(%rbp)
    0x1015859d6 <+22>:  callq  0x10158a46e               ; symbol stub for: objc_autoreleasePoolPush
    0x1015859db <+27>:  leaq   0x7906(%rip), %rsi        ; @"RW_REALIZED|RW_REALIZING -------- %x"
    0x1015859e2 <+34>:  movl   $0x80080000, %edi         ; imm = 0x80080000 
    0x1015859e7 <+39>:  movl   %edi, -0x14(%rbp)
    0x1015859ea <+42>:  movq   %rsi, %rdi
    0x1015859ed <+45>:  movl   -0x14(%rbp), %esi
    0x1015859f0 <+48>:  movq   %rax, -0x20(%rbp)
    0x1015859f4 <+52>:  movb   $0x0, %al
    0x1015859f6 <+54>:  callq  0x10158a39c               ; symbol stub for: NSLog
    0x1015859fb <+59>:  leaq   0x7906(%rip), %rdi        ; @"RO_META-------- %x"
    0x101585a02 <+66>:  movl   $0x1, %esi
    0x101585a07 <+71>:  movb   $0x0, %al
    0x101585a09 <+73>:  callq  0x10158a39c               ; symbol stub for: NSLog
    0x101585a0e <+78>:  leaq   0x7913(%rip), %rdi        ; @"RO_ROOT-------- %x"
    0x101585a15 <+85>:  movl   $0x2, %esi
    0x101585a1a <+90>:  movb   $0x0, %al
    0x101585a1c <+92>:  callq  0x10158a39c               ; symbol stub for: NSLog
    0x101585a21 <+97>:  leaq   0x7920(%rip), %rdi        ; @"RW_REALIZED-------- %x"
    0x101585a28 <+104>: movl   $0x80000000, %esi         ; imm = 0x80000000 
    0x101585a2d <+109>: movb   $0x0, %al
    0x101585a2f <+111>: callq  0x10158a39c               ; symbol stub for: NSLog
    0x101585a34 <+116>: leaq   0x792d(%rip), %rdi        ; @"-------- %lx"
    0x101585a3b <+123>: movl   $0x1e08, %esi             ; imm = 0x1E08 
    0x101585a40 <+128>: movb   $0x0, %al
    0x101585a42 <+130>: callq  0x10158a39c               ; symbol stub for: NSLog
    0x101585a47 <+135>: movl   -0x8(%rbp), %edi
    0x101585a4a <+138>: movq   -0x10(%rbp), %rsi
    0x101585a4e <+142>: movq   0x9b63(%rip), %rcx        ; (void *)0x000000010158f6a0: AppDelegate
    0x101585a55 <+149>: movq   0x989c(%rip), %rdx        ; "class"
    0x101585a5c <+156>: movl   %edi, -0x24(%rbp)
    0x101585a5f <+159>: movq   %rcx, %rdi
    0x101585a62 <+162>: movq   %rsi, -0x30(%rbp)
    0x101585a66 <+166>: movq   %rdx, %rsi
    0x101585a69 <+169>: callq  *0x7601(%rip)             ; (void *)0x0000000101ec6d80: objc_msgSend
    0x101585a6f <+175>: movq   %rax, %rdi
    0x101585a72 <+178>: callq  0x10158a3a8               ; symbol stub for: NSStringFromClass
    0x101585a77 <+183>: movq   %rax, %rdi
    0x101585a7a <+186>: callq  0x10158a4d4               ; symbol stub for: objc_retainAutoreleasedReturnValue
    0x101585a7f <+191>: xorl   %r8d, %r8d
    0x101585a82 <+194>: movl   %r8d, %edx
    0x101585a85 <+197>: movl   -0x24(%rbp), %edi
    0x101585a88 <+200>: movq   -0x30(%rbp), %rsi
    0x101585a8c <+204>: movq   %rax, %rcx
    0x101585a8f <+207>: movq   %rax, -0x38(%rbp)
    0x101585a93 <+211>: callq  0x10158a3b4               ; symbol stub for: UIApplicationMain
    0x101585a98 <+216>: movl   %eax, -0x4(%rbp)
    0x101585a9b <+219>: movq   -0x38(%rbp), %rcx
    0x101585a9f <+223>: movq   %rcx, %rdi
    0x101585aa2 <+226>: callq  *0x75d8(%rip)             ; (void *)0x0000000101ec4010: objc_release
    0x101585aa8 <+232>: movq   -0x20(%rbp), %rdi
    0x101585aac <+236>: callq  0x10158a468               ; symbol stub for: objc_autoreleasePoolPop
    0x101585ab1 <+241>: movl   -0x4(%rbp), %eax
    0x101585ab4 <+244>: addq   $0x40, %rsp
    0x101585ab8 <+248>: popq   %rbp
    0x101585ab9 <+249>: retq   

使用 Xocde 的 Debug -> DebugWorkflow -> ViewMemory ,输入地址0x00000001015859c0

Xcode 查看指定地址在文件中的内容.png

而我们使用MachOView 查看文件偏移 0x000019C0处的数据如下图所示:

使用MachOView查看.png

可以看到,这两处看到的内容是一样的!

3、数据部分

我们分析了 Mach-O 的头部mach_header_64头部,知道了 Mach-O 的CPU架构、文件类型以及加载命令等信息;接着又分析了加载命令Load Commands ,知道了 应当如何加载文件中的数据。

我们的目的是为了读取 Mach-O 的信息,这些信息就存储在 Mach-O 的数据部分,接下来,我们尝试去解析一些数据!

文件偏移地址 = 虚拟地址 - 模块在内存中地址 + 模块在文件中的偏移

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容

  • 介绍 Mach-O 的全称是 Mach Object File Format。可以是可执行文件,目标代码或共享库,...
    星光社的戴铭阅读 2,302评论 1 21
  • Mach-O 概述 和 部分命令介绍 我们知道Windows下的文件都是PE文件,同样在OS X和iOS中可执行文...
    青花瓷的平方阅读 14,906评论 2 52
  • 摘录:其字德安「Mach-O文件结构」(以此记录一下) iOS上的可执行文件相当于windows上的.exe可执行...
    NJ_墨阅读 1,758评论 0 4
  • 我们用 Xcode 构建一个程序的过程中,会把源文件 (.m 和 .h) 文件转换为一个可执行文件。这个可执行文件...
    skogt阅读 909评论 0 1
  • 这篇文章主要了介绍以下两点: 从源代码到可执行文件,编译器都做了什么? Mach-O 可执行文件里面是什么? 注:...
    ShannonChenCHN阅读 2,492评论 0 9