iOS 可执行文件- Universal (fat) binaries

Executables

    A process is created as a result of loading a specially crafted file into memory. This file has to be in a format that is understood by the operating system, which in turn can parse the file, set up the required dependencies (such as libraries), initialize the runtime environment, and begin execution.

    翻译过来,可执行文件是指 可以被进程加载到内存    可被操作系统解析、设置依赖库、初始化运行时环境以及可执行的文件。

    iOS主要用到两种格式的可执行文件:

  1. Universal (fat) binaries:Multiple-architecture binaries used exclusively in OS X.
  2. Mach-O:OS X native binary format.

Universal (fat) binaries

    由于设备架构的不断变化,由以前的32位到64位,arm也在随之升级,一种二进制文件无法同时兼容32位和64设备,因此产生了通用(胖)二进制文件,可以兼容所有设备。之所以能够兼容是因为含有多个不同架构的独立二进制文件,执行时, 只会选择一种架构的二进制文件。不过也会带来一个弊端:体积较大,这也是为何又称之为胖二进制的原因。

    下面我们通过MachOView看一个通用二进制文件,其中包含了ARM_V7(4s)和ARM64_ALL两种架构的可执行文件,如下图


通用二进制格式的定义在   <mach-o/fat.h>,如下图

操作系统根据当前设备的cputype 和 cpusubtype 去自动的选择一个最适合的去加载。虽然胖二进制文件在磁盘上可能会占据很大一部分空间,但是真正加载到内存的并不大。如下图:



Mach-O Binaries

Mach-O,是Mach object文件格式的缩写,是一种可执行文件、目标代码、共享程序库、动态加载代码和核心DUMP。类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)。

Mach-O格式的文件

mach-o/fat.h loader.h

#define MH_OBJECT   0x1     /* 目标文件*/
#define MH_EXECUTE  0x2     /* 可执行文件 */
#define MH_FVMLIB   0x3     /* fixed VM shared library file */
#define MH_CORE     0x4     /*核心转储文件 */
#define MH_PRELOAD  0x5     /* preloaded executable file */
#define MH_DYLIB    0x6     /* dynamically bound shared library */
#define MH_DYLINKER 0x7     /* dynamic link editor */
#define MH_BUNDLE   0x8     /* dynamically bound bundle file */
#define MH_DYLIB_STUB   0x9     /* shared library stub for static */
                    /*  linking only, no section contents */
#define MH_DSYM     0xa     /* companion file with only debug */
                    /*  sections */
#define MH_KEXT_BUNDLE  0xb     /* x86_64 kexts */
  • 常见的Mach-O格式的文件

    • MH_OBJECT目标文件

      • .o

      • .a/ .framework静态库

        • 静态库即多个.o文件存放在一起实现特定的功能
    • MH_EXECUTE可执行文件

      • .app/MyApp

      • .out

    • MH_DYLIB动态库

      • .framework/xxx

      • /dylib

    • MH_DYLINKER动态链接器

      • usr/lib/dyld
    • MH_DSYM存储二进制文件符号信息的文件

      • .dYSM/Contents/Resources/DWARF/MyApp

Mach-O文件的基本结构

Mach-O包含三个主要区域

  • Header: 文件类型, 目标架构

  • Load command: 描述文件在虚拟内存中的逻辑与布局

  • DataLoad command中定义的原始数据

                

Mach-O结构详解

Mach Header(arm64)

  • Magic Number: 魔数, 表示支持设备的CPU位数
  • oxFEEDFACE: 表示32位二进制
  • oxFEEDFACF: 表示64位二进制

  • cputype和 cpusubtypeCPU类型和子类型
  • filetypeMach-O文件类型
  • ncmds和 sizeofcmds: 用于加载器的 加载命令的条数和大小
  • flags: 动态链接器dyld的标志
  • reserved: 保留字,64位专用,暂时没什么用


Load commands

Mach-O header 已经包含了非常详细的指令信息,可以直接告诉内核加载器如何设置、加载二进制文件 。Load commands 紧跟在 mach_header之后,其中一些指令可以直接被内核加载器理解,其他的则由动态链接器处理
命令十六进制作用
LC_SEGMENT
LC_SEGMENT_64 
0x01/0x19 将这些段加载到对应的进程空间上去(区分32位和64位)
LC_LOAD_DYLINKER 0x0E 加载dyld, 值得注意的是,每个Mach-O文件只能有一个段
LC_UUID 0x1B 将UUID这个值保存到执行进程的上下文中,同样每个Mach-O文件只能有一个段
LC_THREAD 0x04 开启一个Mach线程, 但是不分配栈(这个不常见)
LC_UNIXTHREAD 0x05 开启一个UNIX线程,其实最主要的用途是告诉加载器当前主函数的位置.
这条命令在10.8之后被LC_MAIN取代。
LC_MAIN 0x80000028 在10.8之后代替LC_UNIXTHREAD, 告诉加载器当前主函数的位置
LC_CODE_SIGNATURE 0x1D 这个是数字签名段
LC_ENCRYPTION_INFO 0x21 

加密二进制文件, 貌似在IOS下使用的比较频繁。

  • 常见段

    • __PAGEZERO: 空指针陷阱段

    • _TEXT: 程序代码段

    • __DATA: 程序数据段

    • __RODATAread only程序只读数据段

    • __LINKEDIT: 链接器使用段

Load commands 中最主要的命令就是LC_SEGMENT (or LC_SEGMENT64) commands ,它会告诉内核如何为进程设置内存空间。这些segments 可以直接从Mach-O文件中被加载到内存。

  • section段常见字段

    • Segment Name: 该Segment的名称, 用于load_segment

    • VM Address: 该段的虚拟物理地址

    • VM Size: 该段所需要分配的虚拟内存大小(字节)

    • File Offset: 该段在文件中的偏移量

    • File Size: 该段在文件中占据的字节数

    • Maximum VM Protection: 段的页面所需要的最高内存保护

      • ox1: x 执行

      • ox2: w 写

      • 0x4: r 读

    • Initial VM Protection: 段页面初始化的内存保护

    • Number of Sections: 段中section区的数量

Flags: 其他标志位

tips: 小结:
根据 LC_SEGMENT 命令 设置进程虚拟内存
对于每一个段, 将其内容从 Mach-O 文件加载到内存中
即从 Mach-O 文件中的偏移量为  File Offset 处加载 File Size 字节内容到虚拟内存地址 VM Address VM Size 字节空间内

段中区section详解

    LC_SEGMENTs 提供了__PAGEZERO、_TEXT、_DATA和_LINKEDIT,Segments 有可能会选择性的进一步分解成sections
  • 常见区section

  • __text: 主程序代码
  • __stubs, __stub_helper: 用于动态链接的桩
  • __cstring: 程序中c语言字符串
  • __const: 常量
  • __RODATA,__objc_methnameOC方法名称
  • __RODATA,__objc_methntypeOC方法类型
  • __RODATA,__objc_classnameOC类名
  • __DATA,__objc_classlistOC类列表
  • __DATA,__objc_protollistOC原型列表
  • __DATA,__objc_imageinfoOC镜像信息
  • __DATA,__objc_constOC常量
  • __DATA,__objc_selfrefsOC类自引用(self)
  • __DATA,__objc_superrefsOC类超类引用(super)
  • __DATA,__objc_protolrefsOC原型引用
  • __DATA, __bss: 没有初始化和初始化为0 的全局变量

Load Commmands加载命令中其他信息
  • LC_MAIN

    • 设置程序主线程入口地址和 栈大小
image.png
  • LC_CODE_SIGNATURE

    • 包含Mach-O文件的代码签名

    • 没有签名或 签名不正确, 该进程会被kill, 程序崩溃

image.png
Mach-O中动态库的加载
  • 动态库来源

    • 系统提供的动态库

    • 第三方动态库

  • 如图: DingTalk使用大量的系统动态库

    • Mach-O镜像中有很多对外部库以及符号的引用

    • 这些引用将在程序启动时, 由动态链接器 /usr/lib/dyld来执行符号绑定


  • 加载动态链接器

    • LC_LOAD_DYLINKER: 内核执行该命令时, 启动dyld
image.png
  • 获取符号表

    • LC_SYMTAB: 符号地址表

    • LC_DYSYMTAB: 动态符号地址表

  • 加载动态库

    • LC_LOAD_WEAK_DYLIB

    • LC_LOAD_DYLIB

  • 动态库加载流程小结

  1.  首先启动dyld动态链接器; 内核根据LC_LOAD_DYLINKER启动/usr/lib/dyld
  2.  如果Mach-O文件中使用了外部定义的符号或函数, 则会在文本段__TEXT__stubs, __stub_helper区; 区内放着本地未被定义的符号; 编译器在编译源码时会创建对这些未定义符号桩区的调用
  3.  dyld运行时, 会在符号桩区调用地址上; 添加JMP 到 真实函数地址的指令
  4.  至于dyld怎么找到指定的动态库中指定的函数地址? 此时dyld将加载Load Command中的LC_LOAD_DYLIB命令
  5.  LC_LOAD_DYLIB(动态库)dyld将加载每一个指定的库且搜寻匹配的符号
  6.  当符号匹配时, 将在符号表(由dyld加载LC_SYMTABLC_DYSYMTAB获取)查找对应的函数/符号地址

  • app在手机上加载的过程:
    

.main()之前的过程有哪些?

1、main之前的加载过程

1)dyld 开始将程序二进制文件初始化

2)交由ImageLoader 读取 image,其中包含了我们的类,方法等各种符号(Class、Protocol 、Selector、 IMP)

3)由于runtime 向dyld 绑定了回调,当image加载到内存后,dyld会通知runtime进行处理

4)runtime 接手后调用map_images做解析和处理

5)接下来load_images 中调用call_load_methods方法,遍历所有加载进来的Class,按继承层次依次调用Class的+load和其他Category的+load方法

6)至此 所有的信息都被加载到内存中

7)最后dyld调用真正的main函数

注意:dyld会缓存上一次把信息加载内存的缓存,所以第二次比第一次启动快一点


参考文档:
https://www.jianshu.com/p/1f22d1e667e3

https://www.safaribooksonline.com/library/view/mac-os-x/9781118236055/9781118236055c04.xhtml

https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html#//apple_ref/doc/uid/20001880-BCICIHAB

https://blog.csdn.net/dongaxis/article/details/41114071

http://blog.sina.com.cn/s/blog_185268e880102xpd7.html

http://www.ruanyifeng.com/blog/2014/11/compiler.html

https://www.jianshu.com/p/90f5ec723175

https://blog.csdn.net/yanglei3kyou/article/details/48825917


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值