1. 符号表
- 符号表(Symbol Table):保存符号的在字符串表的位置信息,包含方法名、函数名、类名.
- 字符串表(String Table): 保存符号名字的。
- 间接符号表(Indirect Symbol Table):保存间接符号的,即使用到的外部动态库的符号。例如使用到 Foundation 库里面的 NSLog方法。NSLog符号就会存在间接符号表中。间接符号表是符号表的子集。对于当前MACH-O ,NSLog是未定义的符号。如果这个MACH-O被另外的一个项目A使用,想在A中使用NSLog,则需要将NSLog重新导出,通过起别名的方式,可以将符号重新导出。
- 重定位符号表:在生成.o文件的时候,使用到的别的库的符号,会放到重定位符号表中,等链接后,会被放到间接符号表中。
2. 命名空间
flat_namespace: 一级命名空间, 符号表中存储的符号名就是自身符号名
two_levelnamespace: 二级命名空间, 符号表中存储的符号名 = 库名 + 符号名
动态库的链接器默认采用二级命名空间,也就是除了会记录符号名称,还会记录符号属于哪个 Mach-O 的,比如会记录下来 _NSLog 来自 Foundation。
例如在当前项目 A 中定义了一个全局函数test(), 如果引入的动态库 B 中也有这样一个全局函数,并不会报错。如果当前项目有该函数的实现,会调用当前项目的实现,没有则会调用动态库 B 中的函数实现。
3. 读符号
1. 间接符号
// 显示间接符号, 也就是引用的动态库里的全局符号// --indirect-symbols - Print indirect symbol table for Mach-O objects (requires -macho)objdump -m --indirect-symbols ${path}
2.导出符号
// 显示导出符号
// --exports-trie - Display mach-o exported symbols
objdump -m --exports-trie ${path}
3.重定位符号
// 显示重定位符号, 缩写为 -r (后面接目标文件的Mach-O文件)
--reloc - Display the relocation entries in the file
// 显示动态库重定位符号, 缩写为 -R
--dynamic-reloc - Display the dynamic relocation entries in the file
4.恢复符号
// 恢复被加密处理的符号
--demangle - Demangle symbols names
5.读取完整符号表的 2 种方法
1. objdump命令, 展示符号可见性、符号功能、所在位置等信息
// --syms 可缩写为 -t
objdump -m --syms test
2. nm命令,展示符号种类、可见性、是否导出等信息
- -p: 不排序
- -a: 显示所有符号,包含调试符号
-m:显示(N_SECT)符号,格式(seg-ment_name, section_name) (external non-
external) (undefined), (common), (absolute) (indirect)

这些输出结果中的 l/g, F/O, t/d/s/U , external/non-external 代表的含义,下面马上讲到~3. 符号的分类
1. 按符号种类区分
Symbol Type:
U:undefined(未定义)
- A: absolute(绝对符号)
- -: debugger symbol table
- T①:text section symbol(TEXT.text)
- D①:data section symbol(DATA.data) ,已初始化的静态变量/全局变量
- B①: bss section symbol(DATA.bss) , **未初始化
- S①: common section symbol (DATA,common), 除了上面所述的,存放在其他
section的内容,例如未初始化的全局变量 - C: common symbol(只能出现在
MH_OBJECT类型的Mach-O文件中, 也就是.o目标文件中,表示未初始化的全局变量,上面的“S” 是指在可执行mach-o文件中表示未初始化的全局变量) - I: indirect symbol(符号信息相同,代表同一符号)
- u: 动态共享库中的小写u表示一个未定义引用对同一库中另一个模块中私有外部符号
2. 按照功能分类
- f: file 文件
- F: Function 函数/方法
- O: Data 数据
- d: Debug 调试
- ABS: Absolute 绝对
- COM: Common 普通
- UND: undefind 未定义
3. 按照符号的模块/可见性分类
- 本地符号(local symbol):只在当前文件内可用的符号
- 全局符号(global symbol):对当前所在项目可见,当打包成动态库以后,全局符号变成导出符号,就会对使用这个动态库的项目可见。也就是项目 A 使用了动态库 B,B 内的全局符号,A也可见。
如下图中,test 方法为本地符号,un_init_global 为全局符号:
动态库的全局符号默认为导出符号,可以修改成本地符号、也可以不导出。
动态库全局符号不可脱:因为只有在运行的时候才会去加载动态库,编译时会被存储到间接符号表,在链接的时候需要去间接符号表找到相应符号,就可以生成可执行文件了。由此动态库的全局符号,不能在优化的时候删掉。
1. 全局符号本地化的方法:
- 使用 static 修饰 ,在静态区测试中,我们可知 static 可使变量的更改在当前文件内有效,所以static 修饰的是本地符号
- 使用 Clang 编译器的 visibility 控制
/** __attribute__ 是给编译器传参数 visibility属性,控制文件导出符号,限制符号可见性(clang参数为-fvisibility) */ // 将全局符号隐藏为本地符号 int hidden_y __attribute__((visibility("hidden"))) = 99; // 默认属性,符号保持原可见性 double default_y __attribute__((visibility("default"))) = 100;2. 不导出符号
OC中的类,默认是全局符号即导出符号,当有的类不想对外暴露或者想减小库的体积,可以设置符号为不导出符号,转为本地符号。这样在脱符号的时候,就可以被脱掉了。 ``` // -unexportedsymbol 不导出符号_OBJC_METACLASS$LGOneObject OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_METACLASS$_LGOneObject
// unexported_symbols_list ${filepath} 多个符号需要不导出时,可以放到一个文件中,然后设置这个文件不导出 OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbols_list -Xlinker ${filepath}
<a name="Y4btM"></a>
## 4.按照存在空间分类
private: 私有<br />non-external: 非外部<br />external: 外部, 一般外部符号都是导出符号<br />
<a name="pLl8m"></a>
## 5. 弱符号: **Weak Reference Symbol & Weak defintion Symbol**
**Weak Reference Symbol**: 表示此未定义符号是**弱引用**。
- 如果动态链接器找不到该符号的定义,则**将其符号置为0**
- 可用来判断是否要执行该函数
- 也可以将某个动态库设置成弱引用,这样就避免报库找不到的错误
- 静态链接器会将此符号设置**弱链接标志**。
// 1. 定义为弱引用符号 void weakfunction() _attribute((weak_import));
// 1.1 设置为弱定义的符号,且没有实现时符号,使用后,在编译时会报出符号找不到的错误 // 这时需要设置链接器参数,如下所示:
// 2. config 文件中,设置链接器参数,忽略该弱引用符号,等待动态查找 OTHER_LDFLAGS=$(inherited) -Xlinker -U -Xlinker _weak_function
// 3.调用时,如果不为0, 再执行这个函数 if (weak_function) { weak_function(); }
符号表中,弱引用符号的标识是 `**w**`: <br /><br />**Weak defintion Symbol**: 表示此符号为**弱定义符号**。
- 如果静态链接器或动态链接器为此符号**找到另一个非弱定义,则弱定义符号将被忽略**。
- 只能将合并部分中的符号标记为弱定义。
- 🌰:同一个项目中,Test 文件声明并实现了一个弱定义的函数,Test2 文件中也实现了一个非弱定义的weak_function,在编译时,会编译Test2 文件中的函数,如下图所示:
// 弱定义全局符号 void weakfunction() attribute((weak)); // 弱定义本地符号 void weakfunction() __attribute((weak, visibility(“hidden”)));

<a name="4jqq9"></a>
## 6. 导入(Import)导出(Export)符号:
**export symbol**:导出符号意味着,告诉别的模块,我有一个这样的符号,你可以将 其导入(**Import**)。
<a name="j5dbG"></a>
# 5.设置链接器参数
**链接器参数查看 **`**man ld **`, 切换英文输入反斜线`/` 可以查找命令**:**
<a name="jL9L0"></a>
### 1. 忽略符号:
xconfig 指定某个类,使用动态查找,这样就是告诉编译器不要因为找不到而报错
OTHERLDFLAGS = -XLinker -U -XLinker 具体符号(_OBJC_CLASS$_类名或者方法名) $(inherited) // -U 是可以不定义一个符号 // -u 是必须要定义符号,才能编译通过
<a name="AFX87"></a>
### 2.将符号信息和使用的库的信息写入到指定的文件中
OTHER_LDFLAGS = -XLinker -map ${path}
<a name="NP42L"></a>
### 3.重新导出符号,或者库。
例如使用到 Foundation 库里面的 NSLog方法。NSLog符号就会存在间接符号表中。间接符号表是符号表的子集。对于当前MACH-O ,NSLog是未定义的符号。如果这个MACH-O被另外的一个项目A使用,想在A中使用NSLog,则需要将NSLog重新导出,通过起别名的方式,可以将符号重新导出。**只能对间接符号起别名。**
// 起别名, 使符号重新导出 OTHER_LDFLAGS=$(inherited) -Xlinker -alias -Xlinker _NSLog -Xlinker Xxs_NSLog
// 查看符号的类型 nm -m ${path} | grep ‘Xxs’ // 打印结果: 导出符号,外部符号,Xxs_NSLog是_NSLog的别名 (indirect) external Xxs_NSLog (for _NSLog)
// 查看符号表 objdum -m —exports-trie ${path} // 打印结果:重导出符号表Xxs_NSLog [re-export] Xxs_NSLog (_NSLog from Foundation)
// 重新导出库 OTHER_LDFLAGS=$(inherited) -Xlinker -reexport_framework -Xlinker ${framework_name}
```


