符号链接

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 ##符号## 屏蔽指定的符号。

符号占的空间比较大,符号称号是字符串。由于App链接的静态库除了直接符号表都能够进行strip,动态库的大局符号会变成导出符号,在对动态库进行符号strip的时分,只能剥离非大局符号的符号。app运用的一切的动态库的符号都存放在直接符号表里。所以相对而言,动态库占用的体积大。
关于包体积的优化,App的本地符号大局符号都能够剥离,只留下直接符号表的符号。

在源码编译成.o方针文件的时分,没有生成实在的内存地址,运用0填充占位,把需求重定位的当地放到重定位表里面。静态库的重定位符号表不能删去,能够剥离调试符号。趁便带一笔,运用XCFramework的方法,能够将调式符号给集成者。

关于大局符号,能够跨macho文件运用,例如在一个framework的.m中界说一个办法foo,而在主工程文件的一个.m文件,声明了foo办法后,能够直接运用,代码会履行到上面的foo。

关于静态库的strip方法,将MachO文件解析成模型Object,遍历LoadCommands找到segment==”__DWARF”的LoadCommand,移除section,从符号表移除Symbol,将修改后的模型Object重新写入MachO文件。动态库n_type的判断。

DWARF是一种被很多编译器和调试器运用的用于支持源码级别调试的调试文件格局。dsym文件便是按DWARF格局保存调试信息的文件。dsym是在编译链接中生成的,保存的是实在的虚拟内存地址。ASLR是在Rebase中增加的。

终端输入"man ld",可查找
dead_strip:Removefunctionsanddatathatareunreachablebytheentrypointorexportedsymbols.

剥离准则:(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”可检查详细指令,简略罗列:

-fprintthefatheaders
-aprintthearchiveheader  
-hprintthemachheader  
-lprinttheloadcommands  
-Lprintsharedlibrariesused  
-Dprintsharedlibraryidname  
-tprintthetextsection(disassemblewith-v)  
-xprintalltextsections(disassemblewith-v)

弱引证+弱符号

Weak Reference Symbol: 表明此未界说符号是弱引证。假如动态链接器找不到该符号的界说,则将其设置为0。链接器会将此符号设置为弱链接标志。能够把整个动态库声明为弱引证。关于library或许framwork,当装备OTHER_LDFLAGS = -Xlinker -weak_framework -Xlinker ##库名## 时,会将这个库声明弱引证,在运用时假如不存在并不会报错,而是输出nil。

Weak defintion Symbol: 表明此符号为弱界说符号。假如链接器找到此符号的一个(非弱)界说,则弱界说将被疏忽。只能将兼并中部分的符号标记为弱界说。例如一个类文件界说一个弱符号办法,而在另一个文件也界说此符号办法,编译不会报错。在调用此符号对应的办法时,会走强符号的办法逻辑。

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 ##可履行文件途径##

DYLD链接库将会check是否射中发动缓存,假如没有,则加载一切的手动插入动态库(经过装备环境参数DYLD_INSERT_LIBRARIES,动态库注入),然后链接程序需求的动态库,然后链接前面插入的库,然后应用插入函数(例如自界说sectionData),绑定符号,读取LC_MAIN,找到进口函数,在此步之前,调用当时程序与动态库的initializers/constructors结构办法,设置胶水地址,dyld装备完结,然后履行main函数。

ps: Swift是一门编译型的言语,不是动态运转的,编译的时分就能够确认一个符号的类型。

动态库+静态库

经过file指令和注释use_frameworkd的方法检查AFNetworking库:

(1)动态库 => “Mach-O64-bitdynamicallylinkedsharedlibraryarm64″。

(2)静态库 => “current ar archive”
经过ar -t的指令检查动态库构成。显现库文件中有哪些方针文件,只显现称号。

image not found的原因=>生成动态库的时分,途径不对,需求装备install_name”;
经过指令otool -l ##动态库称号## | grep ‘LC_ID_DYLIB’ -A 5 可检查动态库的途径name值。然后可经过指令“install_name_tool -id ##库途径## ##所要增加的动态库名,即哪一个动态库##”;也可在生成动态库的时分,经过装备install_name ##库途径##链接器参数处理。

关于库途径的处理方法,引出了@rpath相对途径,谁链接这个库,谁提供@rpath.。而这个谁,咱们称为链接者。关于库的rpath装备时,@rpath/库的上层级直到和链接者同级地点的文件名…直到库名要在自己的macho中也保存一个@rpath对应。增加rpath到macho文件中的指令”install_name_tool -add_rpath ##链接者地点的上一级目录##”

@executable_path表明可履行程序地点的目录,解析为可履行文件的绝对途径。
@load_path表明被加载的macho文件地点的目录,每次加载都可能被设置为不同的途径,由上层指定。
这些是loadCommand的信息,可经过指令检查。

静态库是.o文件的合集,动态库是.o文件链接后的产物

clang链接一个libray库或framework的参数装备与Xcode build-setting的对应key项:

-I ##directoryPath## : 在指定目录寻觅头文件 相当于xcodeheader search path
-L ##directoryPath## : 指定库文件途径(.a/.dyld) => library search path
-l ##library_name## : 指定链接的库文件称号 => other link flags。例如-lAFNetworking-F ##directoryPath## 在指定目录寻觅framework => framework serach path
-framework ##framework_name## 指定链接的framework称号 =>other link flags 例如 -framework AFNetworking

当链接两个同名静态库,可装备OTHER_LDFLAGS 一个“-force_load” 一个“-load_hidden”;

xcframework:1,主动处理头文件;2,调试符号的保存;3,相同架构的处理

动态库内部导入*库

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动态库信息,然后同动态库导入动态库的逻辑。

拓宽: podfile中关键字“workspace ##指定途径的xcworkspace途径##” 能够对不同的target装置pod,库;关于App项目能够需求project途径。

Module

Module:经过编译单个源文件生成的方针文件,例如一个.m被编译成方针文件.o,此.o就代表一个module。 mudole避免了一个.h文件被多个.m文件引入发生的多次编译。是clang专门处理头文件的解析格局,能够将.h头文件编译成一个二进制,然后偷偷缓存到体系的目录中。

一个swift库调用OC文件,需求经过装备mapmodule的方法处理。关于具有同名符号的两个静态库,在兼并后,能够经过装备xcconfig来处理同名问题。

//OTHER_CFLAGS:传递给用来编译C或许OC的编译器,当时便是clang
OTHER_CFLAGS="-fmodule-map-file=${SRCROOT}/.../module.modulemap""-fmodule-map-file=${SRCROOT}/.../module.modulemap"  
//SWIFT_INCLUDE_PATHS:传递给SwiftC编译器,告知他去下面的途径中查找module  
SWIFT_INCLUDE_PATHS="${SRCROOT}/.../module.modulemap""${SRCROOT}/.../module.modulemap"