iOS中的符号冲突(一)- 基础原理

  • 作为iOS开发人员,经常会碰到符号的问题,确切的说在调试,收集崩溃时,避免不了

  • 乍一看,是个抽象的东西,不像我们处理逻辑业务问题那样,直接依托于语言本身,逻辑漏洞分析

  • 你面对的项目不可能是独立的很小的项目,往往依赖很多库,动态库等等,冲突无可避免,至于如何解决,那么前提你就需要多多少少理解符号的本质了

  • 虽然研究符号主要为了解决符号冲突问题

  • 但在分析符号问题阶段,你会涉及到编译,链接的基本知识,在此博客里,你会复习到这些知识,当然就不会很细节展开了,但是对于iOS开发人员分析来讲,是够用的,这些关联知识需要深入的话,就需要靠自己了

理解符号的种类与作用

按照功能划分

Type 说明
f File
F Function
O Data
d Debug
'ABS' Absolute
'COM' Common
'UND' ?

按照符号种类划分

Symbol Type 说明 ①:小写 代表local symbol
U undefined (未定义)
A absolute (绝对符号)
T① text section symbol(__TEXT.__text)
D① data section symbol(__DATA.__text)
B① bss section symbol(__DATA.__bss)
C common symbol(只能出现在 MH_OBJECT 类型的 Mach-O 文件中)
- debugger symbol table
S① 除了上面所述的,存放在其他 section 的内容,例如未初始化的全局变量存放在(__DATA.__common)中
I indirect symbol(符号信息相同,代表同一符号)
U 动态共享库总的小写u表示一个未定义引用对同一库中另一个模块中私有外部符号

查看符号的两个命令 nm & objdump

objdump 结果更便于阅读

image.png
image.png
image.png
  • g: 全局
  • l: 静态本地文件

这样执行命令似乎有点繁琐

  • 需要进入终端输入命令
  • 参数:可执行二进制文件的路径

简化一点

image.png

build 就会执行添加的脚本了, 输出信息就会出现在编译信息里了

image.png

还有个问题,每次查看的时候需要时不时该脚本,改完之后还需要 在编译信息列表里选中当前编译的时间版本

这还是有点繁琐

更好的办法,把命令写入配置文件,在文件里可以随时修改命令,并且把命令执行的结果 直接输出到终端

也就是利用xcconfig配置文件配置相关变量,结合 script

image.png
image.png

编译,直接输出到终端

image.png

SO OSO 属于调试符号,但这种符号是我们不需要的,更好的方式当然是终端输出信息不包含这些了

脱去(strip)不需要的符号

看下配置

image.png

终端输出的符号信息并没有脱去符号,是因为还没配置

xcconfig 文件中配置 OTHER_LDFLAGS = -Xlinker -S 可以脱去干扰的调试符号

image.png
  • 补充知识

    strip命令 执行的时机

    strip究竟是在编译.o 还是链接可执行文件 脱去符号呢?

    都不是。

    而是在 生成exe可执行文件之后,再去对可执行文件里面的符号表进行修改

    Xcode中,strip配置 在我们打包ipa包的时候才起作用,默认debug下是不生效的

    此时我们把上面 截图里 Strip Debug Symbols During Copy 改为 YES

    但我期望的是有条件的strip符号,不然就得 来回切换选择Strip Style 3种方式

    • All Symbols
    • Non-Global Symbols
    • Debugging Symbols
  • 更好的strip配置方式

    继续回到.xcconfig文件 (后缀全程 - xcode配置文件)

    修改手动的繁琐的 build setting 手动设置

    换成利用 xccondig 声明好各种条件的配置项 一劳永逸

    现在测试下这种方式

    终端 - 进入工程根目录 - 执行命令 xcodebuild -showBuildSettings | grep DEPLOY

image.png

发现 DEPLOYMENT_POSTPROCESSING = NO

STRIP_STYLE = all

image.png

然后在配置文件里添加 DEPLOYMENT_POSTPROCESSING = YES

我们再看下配置

image.png

配置已经根据 配置文件里的设置 变更了过来

通过xcode配置文件修改setting 比起 我们直接手动修改灵活了很多,而且具体做了哪些配置 也比较明确

有了这些铺垫,接下来我们就可以分析符号本质及冲突问题了

理解strip命令的实际作用

一个全局函数

在底层被解析出来,也是一个全局符号

那么全局符号的作用域有多大 当前文件?当前app?当前进程?答案是当前进程

这里可以验证一下

  • 静态库里只声明一个全局函数,没有实现
image.png
  • app里声明全局函数
image.png
image.png

你会发现,静态库里能访问到 app里的全局符号,验证了全局符号的作用域应该是当前进程

static 函数 定义在什么地方,它的作用域就作用在什么范围 也就是文件范围

image.png

解决符号冲突

把之前 静态库里的global_func实现注释打开

编译app报错,提示 duplicate symbol 符号冲突了

如果 把静态库 改为 动态库 编译正常

image.png

动态库跟 app包含相同的全局符号,但是没有出现冲突

可能困惑了,为什么动态库反而没有冲突呢,如何理解?

既然全局符号的作用域为当前进程,为什么不报错呢

因为dyld在查找符号的时候,链接器ld引入了一个规则,ld在把.o文件链接生成可执行文件(或动态库)时,根据两级命名空间来查找符号

  • dyld访问符号,先访问符号所在的Mach-o文件,再去访问mach-o里的符号

    也就是 app.global_func 与 dylib.global_func,不会冲突

    而静态库 .o文件的合集,只经历了汇编器,跟app的.o合并在一起,最后生成app可执行文件, 最后都放到了app里,可以理解为前缀都是app,所以当然会冲突了, 重复定义的symbol

  • 切回静态库,我们把app中使用静态库的代码注释
image.png

静态库与app都定义了相同的全局函数,但是没有冲突

为什么不使用静态库代码的时候,不会冲突呢?

连接器ld 在链接静态库的时候,专门针对静态库提出一个规则

  • ld会去判断app使用的静态库的代码,发现如果没有用到静态库代码的时候,就不会把静态库代码加载进app

  • 编译时,并没有把静态库代码链接进去,同名的函数当然不会冲突

  • 好处就是可以减小app的大小

  • 坏处:对于开发中的分类,分类是运行时才动态创建加载的

    如果判断到app没有使用静态库里的代码,分类的代码就会被优化掉了,就会产生问题

    针对这个问题,ld提供了一个规则,通过参数控制 -ObjC

    • OTHER_LDFLAGS = $(inherited) -ObjC

回归到链接器ld本身

man ld

image.png

根据链接器手册 看下 静态库 动态库的描述

  • 静态库 .o文件的集合,.o文件里包含全局符号
  • 动态库 最终链接的镜像

继续在ld手册里搜索 ObjC

4种load形式

image.png
  • all_load 所有静态库里的所有内容全部链接 只要能编译成macho,就全部链接
  • ObjC 静态库里的OC class 或分类 链接
  • force_load 多个静态库,只需要指定一个静态库里的所有内容链接到当前app
  • load_hidden
    • 当前app链接静态库,静态库里正好有一个全局符号,本来在静态库里是全局符号,链接到app后依然是全局符号
    • 导致问题:本来不想暴露给外边使用,经过app一链接,直接暴露给所有外部使用了
    • load_hidden 在链接到app之后,就可以把所有静态库里的全局符号变成本地符号,这样就可以隐藏符号,同时可以减小app体积

避免冲突分析

我们可以修改全局符号 增加前缀

但是如果我们拿到的是别人打包好的静态库,我们根本不可能修改源文件

很多情况下,静态库里包含分类,这个时候 参数 -ObjC 是一定要使用的

那么通过链接器参数控制的方式也不可取

  • 只能考虑其他的方式 把静态库里产生冲突的符号给修改掉 llvm-objcopy

llvm源码编译工具 - llvm-objcopy

为了不至于显得突兀,稍微简单说下 工具的编译

因为之前利用llvm做过一些插件,所以我本地已经有编译过的llvm xcode工程

image.png

找到llvm源码 llvm-objcopy 路径

image.png

在相同目录下 打开 CMakeLists.txt

image.png

添加 add_llvm_tool_subdirectory(llvm-objcopy)

image.png

在编译的xcode工程目录下 执行命令 cmake -G Xcode ../llvm-project/llvm

编译 llvm-objcopy

源码很大,编译后20多个G,而且下载源码不是轻松的事情,以后有机会,关于llvm源码的编译单独更新一篇博客出来

然后执行编译后的工具命令

  • llvm-objcopy --redefine-sym _global_func=_libIFLTestStaticLib_global_func libIFLTestStaticLib.a

结果出错 unsupported load command

源码做了些小调整,可直接下载使用 修改编译后的llvm-objcopy工具

使用 llvm-objcopy 之前,静态库 libIFLTestStaticLib.a 有个全局符号 _global_func

image.png

使用 llvm-objcopy 之后,全局符号 _global_func 被修改 为 _libIFLTestStaticLib_global_func

image.png

工程集成调整过的静态库编译通过,并没有发生冲突,静态库中的同名全局函数正常执行

image.png

直接通过符号调用

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

推荐阅读更多精彩内容