Mach-O的符号与链接

Mach-O符号表

点击了解更多关于Mach-O

Symbol Table 符号表,符号名称和地址
String Table 符号名称
Indirect Symbol Table 间接符号表。保存使用的外部符号,也就是使用的外部动态库的符号。Symbol Table的子集。

常用命令

查看符号:objdump --macho -t (mach-o文件);
nm -m (mach-o文件);objdump --macho --syms (mach-o文件)。
恢复符号号:./restore-symbol /Users/lenny/Desktop/CompileTest -o symbolDemo.symbol。
查看文件: file。
查看mach-header: objdump --macho -private-header (mach-o文件)。
查看 __TEXT: objdump --macho -d (mach-o文件)。
查看导出符号:objdump --macho --exports-trie (mach-o文件)。
查看间接符号表:objdump --macho --indirect-symbols (mach-o文件)。
``

符号分类

Mach-O文件中的符号 = 全局(给别人用) + 本地(自用)+ 间接符号(别人的动态库)。将全局符号导出给其他文件访问的符号叫导出符号,使用其他Mach-O文件导出符号叫导入符号,比如间接符号。此外还可以分为弱符号和非弱符号。以下将一一讲解。

全局符号(Global Symbol)

允许全局访问的符号,默认情况下其他文件是可以访问的。通过objdump --macho --syms 命令可以查看全局符号,比如("g"标记的表示全局(global)符号):


全局符号.jpg
本地符号(Local Symbol)

只允许内部文件访问的符号,比如下面的demo,用static 声明的静态变量,通过objdump --macho --syms 命令查看编译后的Mach-O文件(本地符号的标记是"l"):


本地符号.png
间接符号(Indirect Symbol)

本文件使用的外部符号,也就是使用的外部动态库的符号。Mach-O文件的间接符号可以通过objdump --macho --indirect-symbols命令进行查看,示例如下:


间接符号.jpg
导出符号

导出符号是Mach-O文件提供外部文件访问的符号。默认情况下全局符号一般都是导出符号,但是有时候可以通过链接器来控制导出符号。通过命令objdump --macho --exports-trie可以查看导出符号,比如:


隐藏全局符号.jpg
  • 过滤掉不需要的导出符号
    有一些符号我们不希望导出提供外部访问,比如下面我们自定义的一个OC类:
#import "MyObject.h"

@interface MyObject : NSObject

- (void)doSomething;
@end

@implementation MyObject

- (void)doSomething {
    NSLog(@"%s",__func__);
}
@end

查看导出符号:


OC导出符号.jpg

假设我们不希望MyObject被导出,可以通过Other linker flags配置如下过滤MyObject的符号:

-Xlinker -unexported_symbol -Xlinker _OBJC_CLASS_$_MyObject 
-Xlinker -unexported_symbol -Xlinker _OBJC_METACLASS_$_MyObject

通过命令objdump --macho --exports-trie查看导出符号:


过滤导出符号.jpg

已经没有MyObject的符号。
作用:优化包体积大小。

导入符号

导入的外部符号,也就是导入外部动态的符号。就比如间接符号表的符号,实际上就是当前Mach-O文件导入其他文件的符号。

弱符号(Weak Symbol)

弱符号(Weak Symbol)又分为弱引用符号(Weak Reference Symbol )和弱定义符号(Weak Defined Symbol)。

  • 弱定义符号
    表示此符号为弱定义符号。如果静态链接器或动态链接器为此符号找到另一个(非弱)定义,则弱定义将被忽略。只能将合并部分中的符号标记为弱定义。
    弱定义符号.jpg

    如果静态链接器或动态链接器为此符号找到另一个(非弱)定义,则弱定义将被忽略,比如下面demo中,在main.m中定义一个弱定义变量weak_def_variable,在MyObject.h中定义一个相同名称非弱定义变量,在mian函数中打印这个变量:
// 非弱定义符号
int weak_def_variable = 20;

@interface MyObject : NSObject

- (void)doSomething;
@end
NS_ASSUM
// 弱定义符号
int weak_def_variable __attribute__((weak)) = 10;
int main(int argc, char *argv[]) {
    NSLog(@"%d", weak_def_variable);
  return 0;
}

打印结果是20。这里表示weak_def_variable使用的是MyObject.h里的定义。

弱定义符号也是可以隐藏的,比如:

// 隐藏弱定义符号
void weak_hidden_function(void) __attribute__((weak, visibility("hidden")));

这样它就不会出现在导出符号表中。

  • 弱引用符号
    表示此未定义符号是弱引用。如果动态链接器找不到该符号的定义,则将其置为0,链接器会将此符号设置为弱连接标志。
    比如下面的弱引用方法weak_reference_function,只有声明,没有定义:
// 弱引用
void weak_reference_function(void) __attribute__((weak_import));
int main(int argc, char *argv[]) {
    
//    NSLog(@"%d", weak_def_variable);
    if(weak_reference_function){
        weak_reference_function();
    }
  return 0;
}

在Other Linker Flags 加入:

-Xlinker -U -Xlinker _weak_reference_function

这个告诉链接器weak_reference_function为未定义符号,只在运行时使用。这时候的weak_reference_function被连接器置为0,所以访问时没有报错。
弱引用读好的可以单个指定,也可以将整个动态库的导出符号全部制定为弱引用,这样即使存在未定义的符号也不会报错,有运行时处理(慎用,有风险)。

未定义符号(undefined)

未定义符号一般是在编译时无法确定它的定义符号。当dyld加载动态库时会根据undefined的符号加载对应的动态库。加载后,将未定义的符号绑定到动态库里对应的地址上。以下"UND"标记的是未定义符号:

未定义符号.jpeg

Swift符号

Swift是静态语言,其符号跟OC不一样,当然它也有全局和本地符号之分,比如下面的demo,定义了一个public类和private类:

public class SwiftPublicClass{

    func testFunc() { 
    }
}

private class SwiftPrivateClass {
   
    func testFunc() {
    }
}

通过objdump --macho --syms命令查看Swift符号:


swift符号.jpeg

可以看出Swift代码的符号比OC多得多。

符号剥除(Strip)

动态库:对于动态库来说,全局符号不能剥除掉,因为它是给外部文件访问的;
静态库对于静态库,它是.o文件和重定位符号表的合集。重定位符号符号不能剥除;
app:对于app来说除了间接符号表(访问其他动态库的符号表)不能剥除,其他如全局符号和本地符号都可以剥除以优化app的体积。
dead_code_stripping:连接器去除无用代码。剥除本地符号以及未使用的符号;
strip_style:

Debugging Symbols 剥除调试符号
Non-Global Symbols 保留全局符号(动态库)
All Symbols 剥除所有符号(除了间接符号,app)

问题:就符号来说,使用静态库体积和动态体积谁会比较小?
答:静态库。因为静态库直接编译连接到app中,除了间接符号都可剥除,而动态库不行。

符号冲突

动态库:动态库使用的是二维命名空间,符号调用先找库再找符号,所以没有符号冲突。
静态库:

1、没有调用的符号,静态库不会连接。
2、链接器在找到符号之后就不会再链接相同的符号了。
3、对OC来说,连接的最小单位是类。
4、ObjC告诉编译器添加(链接)所有的分类,解决冲突使用(-force_load 库路径),force_load表示在库重复时优先链接指定路径的库。

链接

Mach-O文件在编译时会生成相应的符号,链接是处理目标文件(.o文件)的过程。多个目标文件合并到到一起,多张表合并到一张表中。链接是根据符号表链接;LLVM是编译器工具链的一个集合:先对每个文件进行编译生成Mach-O(可执行文件);连接器会将多个Mach-O文件合并成一个。

连接器做了什么?

(1)去项目文件里查找目标代码文件里没有定义的变量;
(2)扫描项目中的不同文件,将所有符号定义和引用地址收集起来;
(3)计算合并后长度和位置,生成同类型的段并进行合并,建立绑定;
(4)对项目中不同文件里的变量进行重定位。

动态链接库和静态链接库

静态链接库是编译时链接的库,需要链接进你的Mach-O文件里,如果需要更新就要重新编译一次,无法动态加载和更新。
动态链接库是运行时进行链接的库,使用dyld实现动态加载。使用dyld加载动态库的两种方式:程序加载时绑定和符号第一次被用到时绑定。dyld做了什么事:

(1)先执行Mach-O文件,根据Mach-O文件里的undefined的符号加载对应的动态库,系统会设置一个共享缓存来解决加载的递归依赖问题。
(2)加载后,将undefined的符号绑定到动态库里对应的地址上;
(3)最后再处理+load方法,main函数返回后运行static terminator。

注:运行时通过dlopen和dlsym导入动态库时,先根据记录的路径找到对应的库,再通过符号名称在符号表找到绑定的地址。dlopen打开动态库后返回的是引用的指针,dlsym的作用就是通过dlopen返回的动态库指针和函数符号,得到函数的地址然后使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容