符号链接
SymbolTable:用来保存符号的,存放地址。StringTable:用来保存符号的名称。
Indirect Symbol Table:间接符号表。保存使用的外部符号。更准确一点就是使用外部动态库的符号,是Symbol Table的子集,不可进行strip。例如使用NSLog函数后,这个符号就会被放进此表。
间接符号表保存了当前可执行文件使用的其他动态库的符号。NSLog默认是未定义的符号,符号表的标识是UND。
Common Symbol:定义时未初始化的全局符号,可以重复定义,一旦找到有值的,将把无值的移除。
链接器会默认把未定义的符号的强制初始化一个值例如0。
static是本地符号,标志符号:l。global是全局符号,标志符号:g。
链接过程就是处理目标文件符号的过程
源代码生成.o的时候,会对符号进行归类,生成重定位符号表(.m或.o文件用到的API), .o文件链接,把多个文件合并到一起中后,重定位符号表、符号表也会合并到一张表中。
OC默认的都是导出符号,所以体积比较大;可以配置OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker ##符号## 屏蔽指定的符号。
在源码编译成.o目标文件的时候,没有生成真实的内存地址,使用0填充占位,把需要重定位的地方放到重定位表里面。静态库的重定位符号表不能删除,可以剥离调试符号。顺便带一笔,使用XCFramework的方式,可以将调式符号给集成者。
对于全局符号,可以跨macho文件使用,例如在一个framework的.m中定义一个方法foo,而在主工程文件的一个.m文件,声明了foo方法后,可以直接使用,代码会执行到上面的foo。
对于静态库的strip方式,将MachO文件解析成模型Object,遍历LoadCommands找到segment=="__DWARF"的LoadCommand,移除section,从符号表移除Symbol,将修改后的模型Object重新写入MachO文件。动态库n_type的判断。
终端输入"man ld",可查找
dead_strip:Remove functions and data that are unreachable by the entry point or exported symbols.
剥离原则:(1)没有被入口函数使用的;(2)没有被导出符号使用的。
dead strip在链接的过程中生效,也就是在没有对OtherFlags配置的情况下,会对分类的代码进行死代码剥离。因分类的代码是运行时调用生效,而在链接时根据dead strip的原则而被干掉。
OTHER_LDFLAGS=-Xlinker -all_load:通过clang编译器,传给ld链接器all_load参数。
-all_load:加载找到的所有目标文件。
-Objc:是告诉链接器除了Objc代码,其他代码正常剥离。
-force_load ##库路径##:告诉ld哪些静态库不要dead strip。
默认是-noall_load,
[Link-Time Optimization](开启Link Time Optimization(LTO)后到底有什么优化?)的优化是在dead code stripping之后的。
deadCodeStripping 死代码剥离是在链接的时候,strip Style是已经生成machO文件的时候。
执行命令objdump --macho --exports-tire ##macho文件## 可查看导出符号。
通过nm的方式还会打印出源代码中 __attribute__((weak, visibility("hidden")))修饰的符号。
__mh_execute_header符号延申:
"对于标准的UNIX链接编辑器符号,程序可以使用符号__mh_execute_header并遍历它的程序的加载命令来确定程序中任何节或段的结束(或开始)"
"The value of the link editor defined symbol [__mh_execute_header] is the address of the mach header in a Mach-O executable file type. It does not appear in any file type other than a MH_EXECUTE file type. The type of the symbol is absolute as the header is not part of any section."
翻译:"链接编辑器定义的符号[__mh_execute_header]的值是mach - o可执行文件类型中mach头的地址。它不会出现在MH_EXECUTE文件类型以外的任何文件类型中。符号的类型是绝对的,因为头不属于任何节。"
`dyld_stub_binder`就是`dyld`库中进行延迟绑定的方法,要告诉函数我们要寻找哪个方法的调用地址。
通过MachO工具可以查看到可执行文件的Symbol Table详细内容。
otool是Xcode自带的常用工具,可以打印出目标文件的相应细节,终端输入"otool help"可查看具体命令,简单列举:
-f print the fat headers
-a print the archive header
-h print the mach header
-l print the load commands
-L print shared libraries used
-D print shared library id name
-t print the text section (disassemble with -v)
-x print all text sections (disassemble with -v)
man ld:查看更多Xlinker配置信息
//弱符号
void weak_function(void) __attribute__((weak))
//弱引用
void weak_import_function(void) __attribute__((weak_import))
—————————————————————man objdump———————————————————————————————————————
//全部符号
objdump --macho --syms ##可执行文件路径##
//导出符号
objdump --macho --exports-trie ##可执行文件路径##
//间接符号表
objdump --macho --indirect-symbols ##可执行文件路径##
动态库内部导入*库
1,当A动态库导入B动态库的时候,主工程在安装A动态库,可以通过sh脚本的方式将B动态库安装至指定的目录frameworks文件夹中,也可直接主工程poddile配置“pod B”。
2, A动态库导入B静态库时,A将B整个库链接进去。 也就是B静态库中的导出符号,在A中将会全部导出。因此,工程可以直接使用B静态库的符号功能。当A并不想暴露B静态库的符号时,可以使用-hidden-l ##库名##配置链接器参数隐藏B的符号。
静态库导入*库
1,A静态库导入B静态库,当主工程使用A静态库时,也要配置B静态库的header search path + library search path + library name(Other Linker Flags中配置-lB);
2,A静态库导入B动态库, 主工程需要配置B动态库信息,然后同动态库导入动态库的逻辑。