fishhook(一):前导知识

相关的数据结构与函数

  • MachO 文件中的链接信息段(LinkEdit Segment)

    LC_SEGMENT_64(__LINKEDIT) 命令用于描述链接信息段,其在 MachOView 中对应的图形界面,如下所示:
    链接信息段
    LC_SEGMENT_64(__LINKEDIT) 命令对应的数据结构,如下所示:

    #import <mach-o/loader.h>
    
    // (64 位的)段
    // 一个段(segment)可以包含 0 到多个节(section)
    // 一个段(segment)的所有节(section),按顺序紧跟在该段(segment)之后
    struct segment_command_64 {
    	uint32_t	cmd;			/* 加载指令类型 */
    	uint32_t	cmdsize;		/* 加载指令的大小,包括 segment 所包含的所有 section 的大小,单位:Byte */
    	char		segname[16];	/* 16 Byte 的段名 */
    	uint64_t	vmaddr;			/* 段的虚拟内存起始地址(Byte) */
    	uint64_t	vmsize;			/* 段所占的虚拟内存的大小(Byte) */
    	uint64_t	fileoff;		/* 段数据在文件中的偏移(Byte) */
    	uint64_t	filesize;		/* 段数据在文件中的大小(Byte) */
    	vm_prot_t	maxprot;		/* 段页面(内存分页)所需要的最高内存保护(r=4, w=2, x=1) */
    	vm_prot_t	initprot;		/* 段页面(内存分页)初始的内存保护(r=4, w=2, x=1) */
    	uint32_t	nsects;			/* 段(segment)中所包含的节(section)的数量 */
    	uint32_t	flags;			/* 标志信息 */
    };
    

    关于空指针陷阱段 LC_SEGMENT_64(__PAGEZERO)
    这是一个不可读、不可写、不可执行的空间,能够在空指针访问时抛出异常(用于捕捉对空指针的引用)
    在 64 位的操作系统上,这个段的虚拟内存大小是 4GB
    4GB 并不是指该段物理文件的真实大小,也不是指该段所占物理内存的真实大小
    4GB 是规定了进程地址空间的前 4GB 被映射为:不可读、不可写、不可执行的空间
    这就是为什么当读写一个 NULL(0x0) 指针时会得到一个 EXC_BAD_ACCESS 错误
    注意:空指针陷阱段 LC_SEGMENT_64(__PAGEZERO) 的物理文件大小为 0

    特别地,对于代码段 LC_SEGMENT_64(__TEXT)、数据段 LC_SEGMENT_64(__DATA)、链接信息段 LC_SEGMENT_64(__LINKEDIT)
    都有:segment_command_64.vmaddr - segment_command_64.fileoff = 0x00000001 00000000 = 4GB = 空指针陷阱段在虚拟内存中的大小
    例如,在上述链接信息段 LC_SEGMENT_64(__LINKEDIT) 中:VM Address = 0x00000001 00010000File Offset = 0x00000000 00010000
    则有:VM Address - File Offset = 0x00000001 00010000 - 0x00000000 00010000 = 0x00000001 00000000 = 4GB = 空指针陷阱段在虚拟内存中的大小

    并且,空指针陷阱段在虚拟内存中的大小 + 程序本次运行时 ASLR 的偏移量 = 程序本次运行时在虚拟内存中的基地址

  • MachO 文件中的符号表(Symbol Table)

    LC_SYMTAB 命令用于描述符号表的位置和大小(即用于描述符号表的地址信息),其在 MachOView 中对应的图形界面,如下所示:
    LC_SYMTAB
    LC_SYMTAB 命令对应的数据结构,如下所示:

    #import <mach-o/loader.h>
    
    struct symtab_command {
    	uint32_t	cmd;		/* LC_SYMTAB */
    	uint32_t	cmdsize;	/* sizeof(struct symtab_command) */
    	uint32_t	symoff;		/* 符号表相对于基地址的偏移量 */
    	uint32_t	nsyms;		/* 符号表中元素的数量 */
    	uint32_t	stroff;		/* 字符串表相对于基地址的偏移量(字符串表记录了所有符号的名字) */
    	uint32_t	strsize;	/* 字符串表的总大小(Byte) */
    };
    

    符号表是一个 struct nlist 类型的数组,包含了用于静态链接与动态链接的符号的信息。其在 MachOView 中对应的图形界面,如下所示:
    Symbol Table
    符号表的元素对应的数据结构,如下所示:

    #import <mach-o/nlist.h>
    
    struct nlist_64 {
        union {
            uint32_t  n_strx;	/* 符号的名称在字符串表(String Table)中的索引 */
        } n_un;
        uint8_t n_type;			/* 符号的类型标识,可选的值有:N_STAB、N_PEXT、N_TYPE、N_EXT */
        uint8_t n_sect;        	/* 符号所在的 section 的索引。如果没有对应的 section,则为 NO_SECT */
        uint16_t n_desc;       	/* 符号的描述,see <mach-o/stab.h> */
        uint64_t n_value;      	/* 符号所在的地址 或 stab 的偏移量 */
    };
    

    因为 MachOView 进行的是静态分析,所以 MachO 文件并未真正地在内存中运行
    因此,ASLR 的偏移量 = 0基地址 = 空指针陷阱段在虚拟内存中的大小 + 程序本次运行时 ASLR 的偏移量 = 0x00000001 00000000 + 0x0 = 0x00000001 00000000
    因此,符号表的首地址 = 基地址 + 符号表相对于基地址的偏移量 = 0x00000001 00000000 + 0x00000000 00010438 = 0x00000001 00010438

  • MachO 文件中的间接符号表(Dynamic Symbol Table)

    LC_DYSYMTAB 命令用于描述间接符号表,间接符号表包含了:
    ① 一组指向符号表中符号的索引
    ② 一组定义了其他几个表位置的偏移量
    其在 MachOView 中对应的图形界面,如下所示:
    LC_DYSYMTAB
    LC_DYSYMTAB 命令对应的数据结构,如下所示:

    #import <mach-o/loader.h>
    
    struct dysymtab_command {
        uint32_t cmd; 			/* LC_DYSYMTAB */
        uint32_t cmdsize; 		/* sizeof(struct dysymtab_command) */
    	// 内部自行使用的符号在符号表(Symbol Table)中的索引与数量
        uint32_t ilocalsym; 	/* index to local symbols */
        uint32_t nlocalsym; 	/* number of local symbols */
    	// 导出给外部使用的符号在符号表(Symbol Table)中的索引与数量
        uint32_t iextdefsym;	/* index to externally defined symbols */
        uint32_t nextdefsym;	/* number of externally defined symbols */
    	// 用于懒绑定的符号在符号表(Symbol Table)中的索引与数量
        uint32_t iundefsym; 	/* index to undefined symbols */
        uint32_t nundefsym; 	/* number of undefined symbols */
    	// contents 表相对于基地址的偏移量与元素个数
        uint32_t tocoff; 		/* file offset to table of contents */
        uint32_t ntoc; 			/* number of entries in table of contents */
    	// module 表相对于基地址的偏移量与元素个数
        uint32_t modtaboff; 	/* file offset to module table */
        uint32_t nmodtab; 		/* number of module table entries */
    	// 引用符号表相对于基地址的偏移量与元素个数
        uint32_t extrefsymoff; 	/* offset to referenced symbol table */
        uint32_t nextrefsyms; 	/* number of referenced symbol table entries */
    	// 间接符号表相对于基地址的偏移量与元素个数
        uint32_t indirectsymoff; /* file offset to the indirect symbol table */
        uint32_t nindirectsyms;  /* number of indirect symbol table entries */
    	// 外部重定位元素相对于基地址的偏移量与元素个数
        uint32_t extreloff; 	/* offset to external relocation entries */
        uint32_t nextrel; 		/* number of external relocation entries */
    	// 内部重定位元素相对于基地址的偏移量与元素个数
        uint32_t locreloff; 	/* offset to local relocation entries */
        uint32_t nlocrel; 		/* number of local relocation entries */
    }; 
    

    间接符号表是一个 uint32_t 类型的数组,包含动态符号在符号表中的索引。间接符号表的元素是一个个 uint32_t 类型的索引值,用于标识动态符号在符号表中的位置。其在 MachOView 中对应的图形界面,如下所示:
    Dynamic Symbol Table
    因为 MachOView 进行的是静态分析,所以 MachO 文件并未真正地在内存中运行
    因此,ASLR 的偏移量 = 0基地址 = 空指针陷阱段在虚拟内存中的大小 + 程序本次运行时 ASLR 的偏移量 = 0x00000001 00000000 + 0x0 = 0x00000001 00000000
    因此,间接符号表的首地址 = 基地址 + 间接符号表相对于基地址的偏移量 = 0x00000001 00000000 + 0x00000000 00011128 = 0x00000001 00011128

    间接符号表如何根据所存储的索引值,定位动态符号在符号表中的位置:
    间接符号表的第 0 个元素所存储的索引值为 0x000000B8(184),其用于标识动态符号 _NSLog 为符号表的第 184 个元素
    符号表的基地址为 0x00000001 00010438,符号表的元素的大小为 sizeof(nlist_64) = 16
    因此,动态符号 _NSLog 在符号表中的地址为 0x00000001 00010438 + 16 * 184 = 0x00000001 00010FB8
    Dynamic Symbol Table
    间接符号表的作用

  • MachO 文件中的字符串表(String Table)

    LC_SYMTAB 命令也用于描述字符串表的位置和大小(即也用于描述字符串表的地址信息),其在 MachOView 中对应的图形界面,如下所示:
    LC_SYMTAB
    LC_SYMTAB 命令对应的数据结构,如下所示:

    #import <mach-o/loader.h>
    
    struct symtab_command {
    	uint32_t	cmd;		/* LC_SYMTAB */
    	uint32_t	cmdsize;	/* sizeof(struct symtab_command) */
    	uint32_t	symoff;		/* 符号表相对于基地址的偏移量 */
    	uint32_t	nsyms;		/* 符号表中元素的数量 */
    	uint32_t	stroff;		/* 字符串表相对于基地址的偏移量(字符串表记录了所有符号的名字) */
    	uint32_t	strsize;	/* 字符串表的总大小(Byte) */
    };
    

    字符串表用于存储符号的名称。字符串表在存储符号名称时,会在每一个符号名称的末尾增加一个 '\0',而在 C 语言中一次字符串的获取会以 '\0' 为结束。其在 MachOView 中对应的图形界面,如下所示:
    String Table
    因为 MachOView 进行的是静态分析,所以 MachO 文件并未真正地在内存中运行
    因此,ASLR 的偏移量 = 0基地址 = 空指针陷阱段在虚拟内存中的大小 + 程序本次运行时 ASLR 的偏移量 = 0x00000001 00000000 + 0x0 = 0x00000001 00000000
    因此,字符串表的首地址 = 基地址 + 字符串表相对于基地址的偏移量 = 0x00000001 00000000 + 0x00000000 000111A0 = 0x00000001 000111A0

    符号表如何根据所存储的字符串表索引值,定位符号名称在字符串表中的位置:
    符号表的第 0 个元素所存储的字符串表索引值为 String Table Index = 0x00000220(544),其用于标识符号 -[ViewController viewDidLoad] 的名称在字符串表中的起始索引为 544
    字符串表的基地址为 0x00000001 000111A0,每个字符的大小为 sizeof(char) = 1
    因此,符号 -[ViewController viewDidLoad] 的名称在字符串表中的起始地址为 0x00000001 000111A0 + 1 * 544 = 0x00000001 000113C0
    Symbol Table
    Symbol Table

  • 向 dyld 注册镜像加载的回调(_dyld_register_func_for_add_image)

    /*
    以下函数允许你向 dyld 注册回调函数,并且每当 dyld 加载或者卸载镜像时,dyld 将会调用这些注册的回调函数
    	1.在调用 _dyld_register_func_for_add_image() 向 dyld 注册回调函数期间,会对每个已加载的镜像调用注册的回调函数 func
    	  之后,每当加载和绑定新镜像时(此时该镜像还未执行初始化),也会调用注册的回调函数 func
    	2.每当镜像在调用终止器之后从内存中卸载之前,都会调用通过 _dyld_register_func_for_remove_image() 向 dyld 注册的回调函数
    	3.从以下两个函数的声明中可以看出:所注册的回调函数的参数列表一定是 (const struct mach_header* mh, intptr_t vmaddr_slide)
    */
    extern void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide));
    extern void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide));
    
  • intptr_t 和 uintptr_t

    /* Types for void * pointers. */
    #if __WORDSIZE == 64
    	# ifndef __intptr_t_defined
    	typedef long int intptr_t;
    	# define __intptr_t_defined
    	# endif
    	typedef unsigned long int uintptr_t;
    #else
    	# ifndef __intptr_t_defined
    	typedef int intptr_t;
    	# define __intptr_t_defined
    	# endif
    	typedef unsigned int uintptr_t;
    #endif
    

    intptr_tuintptr_t 这两种数据类型用于存储指针地址

    intptr_t 不是一个整型指针,而是一个整数。它在 64 位平台中被定义为 long int,在 32 位平台中被定义为 int
    我们都知道 C 语言的指针实际上就是变量的地址,在 64 位平台上指针的长度为 8 个字节,在 32 位平台上指针的长度为 4 个字节
    intptr_t 的容量刚好和这个字节数相等,使用 intptr_t 可以安全地进行整数与指针的转换运算,也就是说当需要将指针作为整数运算时,将指针转换成 intptr_t 进行运算才是安全的

    intptr_t 在不同的平台上编译时长度不同,但都是标准的平台字长的存储量

iOS 系统的懒绑定机制

  • iOS 的懒绑定流程 && MachO 相关的数据结构

    在 XCode 中新建一个 iOS Project:LazyBindingDemo
    并使用如下代码来探究 iOS 系统动态库 Foundation.framworkNSLog 函数是如何被懒绑定的
    准备 - 00
    使用 Release 配置构建项目 LazyBindingDemo,并使用 MachOView 打开主程序的 MachO 文件
    准备 - 01
    ① 在 iOS 系统中,当程序调用动态库的函数时,它实际上执行的是桩节区 Section64(__TEXT, __stubs) 处的代码
    下图红框标出的部分便是用来调用 NSLog 函数的桩(Symbol Stub
    它的地址是 0x100006524 (先记住这个地址,后面通过 LLDB 调试验证的时候会用到)
    1 - Section(__TEXT, __stubs)
    ② 外部函数的地址被存储在 Section64(__DATA, __la_symbol_ptr) 中,而桩(Symbol Stub)的作用便是获取相应的 Lazy Symbol Pointer 并跳转到它所包含的地址
    此处 NSLog 函数的 Lazy Symbol Pointer 所记录的地址为 0x00000001 000065E4
    2 - Section(__DATA, __la_symbol_ptr)
    ③ 当我们第一次调用 NSLog 函数时, Lazy Symbol Pointer 尚未记录 NSLog 函数的真实地址,而是指向 Section64(__TEXT, __stub_helper) 中相关的内容。在 Section64(__TEXT, __stub_helper) 中,它将懒绑定函数 dyld_stub_binder 所需的参数放到 寄存器 w16 中,之后跳转到地址 0x000065CC 处,也就是 Section64(__TEXT, __stub_helper) 的头部,然后调用懒绑定函数 dyld_stub_binder 进行符号绑定,懒绑定函数 dyld_stub_binder 负责找到 NSLog 函数的真实地址并将其回写到 Section64(__DATA, __la_symbol_ptr) 中对应的 Lazy Symbol Pointer
    3 - Section(__TEXT, __stub_helper)
    ④ 仔细观察后发现,寄存器 w16 实际上是存放一个 int 类型的值,那么这个 int 类型的值究竟代表什么呢?为什么懒绑定函数 dyld_stub_binder 可以利用它来绑定符号?实际上,它是相对于 Lazy Binding Info 的偏移量(在 LINKEDIT 段的 Dynamic Loader Info 中)
    懒绑定函数 dyld_stub_binder 根据这个偏移量便可从 Lazy Binding Info 中找到绑定过程所需的信息(比如:到系统的 Foundation 动态库中寻找 NSLog 函数)
    4 - Dynamic Loader Info

  • 通过 LLDB 的调试,验证懒绑定流程

    上面介绍了 iOS 的懒绑定流程 && MachO 文件中用于支持懒绑定的数据结构
    接下来通过 LLDB 调试项目:LazyBindingDemo 来验证以上的内容
    在两个 NSLog 输出语句中都下断点
    并在调试的时候显示汇编代码(XCode - Debug - Debug Wrokflow - Always Show Disassembly
    0
    ① 程序运行到 NSLog(@"First"); 处,我们可以看到程序实际上是跳转到地址 0x104116524 处的桩(Symbol Stub)代码。但是等等,我们之前在 MachOView 中观察到程序此时应该跳转到地址 0x100006524 处才对,但是为什么这里的地址却是 0x104116524 呢?
    这是因为 iOS 系统在加载 MachO 文件的时候,使用了 ASLR 技术(地址空间布局随机化)。通过计算 0x104116524 - 0x100006524 可以得到程序此次加载的偏移量为 0x04110000
    1
    ② 根据上一小节的介绍:
    当程序调用动态库的函数时,它实际上是执行桩节区 Section64(__TEXT, __stubs) 处的代码
    那么地址 0x104116524 处对应的汇编代码,应该就是 NSLog 函数的桩(Symbol Stub
    我们通过 LLDB 打印地址 0x104116524 处对应的汇编代码
    LLDB 的输出结果,与前面用 MachOView 显示的 NSLog 函数的桩(Symbol Stub)是一致的
    2
    ③ 因为是首次调用 NSLog 函数,所以地址 0x00000001 041165e4 处记录的,应该是 Section64(__TEXT, __stub_helper)NSLog 函数执行懒绑定前,用于准备懒绑定的参数的代码
    我们通过 LLDB 打印地址 0x00000001 041165e4 处对应的汇编代码
    LLDB 的输出结果,与前面用 MachOView 显示的 NSLog 函数执行懒绑定前,准备参数的代码是一致的
    3
    ④ 那么可想而知,地址 0x1041165cc 应该就是 Section64(__TEXT, __stub_helper) 的首地址,其记录的应该就是对懒绑定函数 dyld_stub_binder 的调用
    我们通过 LLDB 打印地址 0x1041165cc 处对应的汇编代码
    果不其然,地址 0x1041165cc 记录的就是对懒绑定函数 dyld_stub_binder 的调用
    出于好奇,我们通过地址 0x00000001 a986e08c 进去看一下懒绑定函数长什么样子
    4
    ⑤ iOS 系统首次调用 NSLog 函数进行懒绑定的流程,我们已经验证完了
    接下来清空 LLDB 的输出并过掉第一个断点,程序运行到 NSLog(@"Second");
    我们接着探索 iOS 系统第二次调用 NSLog 函数的流程
    可想而知,地址 0x104116524 记录的应该还是 NSLog 函数在桩节区 Section64(__TEXT, __stubs) 的桩(Symbol Stub
    我们通过 LLDB 打印地址 0x104116524 处对应的汇编代码,LLDB 的输出结果与预期相符
    5
    ⑥ 我们注意到, 此时 Section64(__TEXT, __stubs)NSLog 函数的桩(Symbol Stub)获取到的不再是指向 Section64(__TEXT, __stub_helper) 的调用,而是 NSLog 函数的真实地址
    这也证实了,懒绑定只会在外部函数首次调用的时候执行一次
    最后,出于严谨,我们还是要验证一下地址 0x00000001 a9e6253c 处是不是存储着 NSLog 函数对应的汇编代码
    6

如何获取到 Lazy Symbol Pointers 对应的函数名

  • 思考

    先回忆起上节在讲 iOS 系统的懒绑定机制 时的这张图
    这里是通过 MachOView 解析的,项目 LazyBindingDemo 的 MachO 文件中的 Lazy Symbol Pointers
    在调用 NSLog 函数时,会到这里获取 NSLog 函数的地址,然后跳转执行

    根据上节对 iOS 系统懒绑定机制的介绍:
    第一次从这里获取到的地址值会指向 stub_helper
    第二次从这里获取到的地址值会指向 NSLog 函数的入口
    Lazy Symbol Pointers
    留意到上图最右边的 Value 列,这里 MachOView 已经帮我们解析出:地址 0x10000C000 存储的是指向 NSLog 函数的调用
    这里需要注意一点:MachOView 仅仅是帮我们解析出了地址 0x10000C000 调用的函数名是 NSLog,而不是解析出了 NSLog 函数的真实地址。因为 MachOView 打开的仅仅是存储在磁盘上的静态 MachO 文件,而不是装载到内存中的动态进程,所以 MachOView 是无法获取到位于动态库中的 NSLog 函数的真实地址的

    言归正传:在 Lazy Symbol Pointers 中,MachOView 是如何解析出地址 0x10000C000 对应的函数名就是 NSLog 的呢?

  • Section64 Header 中的 Indirect Sym Index

    在开始介绍函数名获取的流程之前,这里先补充一个知识点

    我们知道,MachO 文件的 Data 区域是分段(Segment)管理的,每个段(Segment)会有 0 到 多个节(Section
    其中,用于描述节(Section)的数据结构如下所示:

    // (64 位的)节
    struct section_64 {
    	char		sectname[16];	/* 16 Byte 的节名 */
    	char		segname[16];	/* 16 Byte 的段名,该节所属的段 */
    	uint64_t	addr;			/* 节的虚拟内存起始地址 */
    	uint64_t	size;			/* 节所占内存空间的大小(Byte) */
    	uint32_t	offset;			/* 节数据在文件中的偏移 */
    	uint32_t	align;			/* 节的内存对齐边界(2 的次方) */
    	uint32_t	reloff;			/* 重定位信息在文件中的偏移 */
    	uint32_t	nreloc;			/* 重定位信息的条数 */
    	uint32_t	flags;			/* 标志信息(节的类型与属性。一个节只能有一个类型,但是可以有多个属性,可以通过位运算分别获取节的类型和属性) */
    	uint32_t	reserved1;		/* 保留字段 1(可以用来表示偏移量或者索引,一般用来表示 Indirect Symbol Index,也就是当前节的首元素在间接索引表的位置) */
    	uint32_t	reserved2;		/* 保留字段 2(可以用来表示数量或者大小,比如,在 Section64(__TEXT, __sutbs) 中就用来表示 stub 的个数 */
    	uint32_t	reserved3;		/* 保留字段 3(无任何用处,真正的保留字段)*/
    };
    

    留意到 uint32_t reserved1 字段:保留字段 1(可以用来表示偏移量或者索引,一般用来表示 Indirect Symbol Index,也就是当前节的首元素在间接索引表的位置)。什么意思呢?uint32_t reserved1 字段其实是一个索引偏移量,指的是当前 Section 的第 0 个元素对应 Indirect Symbols 表中的第几个元素。以 Section64 Header(__stubs) 为例进行说明:
    Section64 Header(__stubs) 的 Reserved1 属性
    Symbol Stubs 与 Indirect Symbols 的对应关系

  • 由 Lazy Symbol Pointers 获取函数名的过程

    ① 由 LoadCommands 区域的 LC_SEGMENT_64(__DATA).Section64 Header(__la_symbol_ptr).Indirect Sym Index = 0x0000000F(15) 可知,Lazy Symbol Pointers 的第 0 个元素对应 Indirect Symbols 的第 15 个元素。因为 NSLog 函数正好为 Lazy Symbol Pointers 的第 0 个元素,所以 NSLog 函数对应 Indirect Symbols 的第 15 个元素,如下图所示:
    Section64 Header(__la_symbol_ptr) 的 Reserved1 属性
    Lazy Symbol Pointers 与 Indirect Symbols 的对应关系
    ② 留意上图 NSLog 函数在 Indirect Symbols 中对应的条目,其 Data 列的值为 0x00000C0(192),说明 NSLog 函数的符号在符号表 Symbol Table 中的索引为 192,找到 Symbol Table 的第 192 个元素,如下图所示:
    2 - Symbol Table
    ③ 留意上图 NSLog 函数在 Symbol Table 中对应的条目,其 String Table Index0x16,说明 NSLog 函数在字符串表 String Table 中的起始位置为 0x16,找到 String Table 的第 0x16 个位置,正好为 _NSLog,如下图所示:
    3 - String Table
    ④ 整体的解析顺序为:
    Section64 Header(__la_symbol_ptr) -> Lazy Symbol Pointers -> Indirect Symbols -> Symbols -> String Table
    Section64 Header(__stubs) -> Symbol Stubs -> Indirect Symbols -> Symbols -> String Table

懒绑定函数 dyld_stub_binder 的执行流程

  • ① 前面我们在讲解 iOS 系统的懒绑定机制时,知道了:MachO 在进行 Lazy Symbol 的绑定时,会调用位于 Section64(__TEXT, __stub_helper) 中的懒绑定函数 dyld_stub_binder
    实际上对于 MachO 文件来说,dyld_stub_binder 也是一个外部符号,其实现位于 dyld 的源码中
    0

  • dyld::fastBindLazySymbol(...) 函数用于获取需要进行懒绑定的镜像,并调用 ImageLoader::doBindFastLazySymbol(...) 函数执行懒绑定
    1

  • ImageLoader::doBindFastLazySymbol(...) 是一个虚函数,会根据不同的镜像类型调用不同的实现

    ImageLoaderMachOCompressed::doBindFastLazySymbol(...) 函数为压缩类型的镜像进行懒绑定并返回真实的符号地址
    2
    因为经典类型的镜像的 LinkEdit 段没有 LC_DYLD_INFOLC_DYLD_INFO_ONLY,不能进行压缩类型的懒绑定
    所以 ImageLoaderMachOClassic::doBindFastLazySymbol(...) 函数的实现只有一句代码:抛出异常
    3

  • ImageLoaderMachOCompressed::bindAt(...) 函数用于(进行符号表的解析)以及(进行最终的绑定操作)
    4

  • ImageLoaderMachOCompressed::resolve(...) 函数用于解析符号表,返回符号地址
    5

  • ImageLoaderMachO::bindLocation(...) 函数用于根据不同的绑定类型完成最终的符号地址绑定操作
    6

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值