• 作为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 成果更便于阅览

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

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

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

  • g: 大局
  • l: 静态本地文件

这样履行指令好像有点繁琐

  • 需求进入终端输入指令
  • 参数:可履行二进制文件的路径

简化一点

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

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

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

还有个问题,每次检查的时分需求时不时该脚本,改完之后还需求 在编译信息列表里选中当时编译的时刻版别,

这仍是有点繁琐

更好的方法,把指令写入装备文件,在文件里能够随时修正指令,并且把指令履行的成果 直接输出到终端

也便是利用xcconfig装备文件装备相关变量,结合 script

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

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

编译,直接输出到终端

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

SO OSO 归于调试符号,但这种符号是咱们不需求的,更好的方法当然是终端输出信息不包括这些了

脱去(strip)不需求的符号

看下装备

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

终端输出的符号信息并没有脱去符号,是由于还没装备

xcconfig 文件中装备 OTHER_LDFLAGS = -Xlinker -S 能够脱去干扰的调试符号

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

  • 弥补常识

    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

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

    发现 DEPLOYMENT_POSTPROCESSING = NO

    STRIP_STYLE = all

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

    然后在装备文件里添加 DEPLOYMENT_POSTPROCESSING = YES

    咱们再看下装备

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

    装备已经依据 装备文件里的设置 变更了过来

    经过xcode装备文件修正setting 比起 咱们直接手动修正灵活了许多,并且详细做了哪些装备 也比较明确

有了这些衬托,接下来咱们就能够剖析符号本质及抵触问题了

了解strip指令的实际效果

一个大局函数

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

那么大局符号的效果域有多大 当时文件?当时app?当时进程?答案是当时进程

这儿能够验证一下

  • 静态库里只声明一个大局函数,没有完成

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

  • app里声明大局函数

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

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

你会发现,静态库里能拜访到 app里的大局符号,验证了大局符号的效果域应该是当时进程

static 函数 界说在什么地方,它的效果域就效果在什么规模 也便是文件规模

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

脱去干扰符号,你会发现

  • 界说的 global_func 归于大局符号
  • static_func 归于本地符号
  • 界说的OC符号,既有大局的,又有本地的

app当时进程里,除了app之外,还有没有其他的Mach-O格局的东西

还有动态库

那么当时app 当时进程 就不太好区分了

此刻咱们把 STRIP_STYLE 设置为all,脱去一切符号,可是 成果依然存在像 _NSLog 这样的符号

_NSLog 归于 Foundation 动态库

app运转时分,才会链接动态库,

所以在编译阶段,把整个的运用动态库的符号 保存下来

那么为什么编译阶段,运用的其他的动态库的符号,没有被脱掉?

  • 当咱们在函数里调用另外一个函数时

    实际上履行的是汇编, 经过汇编直接找到调用函数的入口地址就能够了

    这个过程中不需求用到符号,没有符号也能够调用

    符号最大的效果 供给 可视化 使程序猿知道调用的地址 是什么名称

    可是 咱们运用的动态库的符号 只有在运转时 才去找到动态库符号的真实地址,编译阶段不关注详细地址 所以编译时需求先保存下来 不能脱掉

    像这种运用了动态库的符号,咱们放在另一个符号表里存储,这个符号表 叫 直接符号表, 这儿的符号是咱们不能脱掉的 运转的时分还要盼望 直接符号去动态库里找到符号的地址

  • 依据以上剖析

    • Non-Global Symbols non-global 脱去非大局符号, 应该是供给给哪品种型的mach-o运用

      当然是供给给动态库来运用,动态库里供给给外界的一切符号都是 大局符号,动态库本地符号脱掉是为了减小动态库的大小

    • Debugging Symbols – debugging 是给静态库来运用

      静态库是.o的合集,.o经过下一步 链接器ld 的操作,才会变成可履行文件(动态库)

      编译器 经过 汇编器 生成.o时,汇编器会把符号都保存下来,可是没有给符号分配虚拟内存地址,假如脱掉了,今后经过链接器链接动态库的时分,就没方法依据符号找虚拟内存地址了

      所以静态库只能脱 调试符号

    • All Symbols – all 给 app运用

      all 并不是一切符号都脱掉

      而是除了动态库符号(也便是动态库露出出来的大局符号),其他的符号都能够脱掉

      其他的符号在app运转期过程中 只是地址

  • 动态库也能拜访 app的大局符号

    大局符号效果域是 整个进程

    动态库能拜访到 app大局符号,由于动态库也在当时app进程里

    • dyld把app 动态库 加载到进程之后,当发现未界说的符号之后,会在当时进程里挨个扫描app 和 动态库,查找有没有app或动态库拥有这个符号,直到找到为止

处理符号抵触

把之前 静态库里的global_func完成注释翻开

编译app报错,提示 duplicate symbol 符号抵触了

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

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

动态库跟 app包括相同的大局符号,可是没有出现抵触

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

既然大局符号的效果域为当时进程,为什么不报错呢

由于dyld在查找符号的时分,链接器ld引入了一个规矩,ld在把.o文件链接生成可履行文件(或动态库)时,依据两级命名空间来查找符号

  • dyld拜访符号,先拜访符号地点的Mach-o文件,再去拜访mach-o里的符号

    也便是 app.global_func 与 dylib.global_func,不会抵触

    而静态库 .o文件的合集,只经历了汇编器,跟app的.o合并在一起,最后生成app可履行文件, 最后都放到了app里,能够了解为前缀都是app,所以当然会抵触了, 重复界说的symbol

  • 切回静态库,咱们把app中运用静态库的代码注释

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

静态库与app都界说了相同的大局函数,可是没有抵触

为什么不运用静态库代码的时分,不会抵触呢?

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

  • ld会去判别app运用的静态库的代码,发现假如没有用到静态库代码的时分,就不会把静态库代码加载进app

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

  • 好处便是能够减小app的大小

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

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

    针对这个问题,ld供给了一个规矩,经过参数控制 -ObjC

    • OTHER_LDFLAGS = $(inherited) -ObjC

回归到链接器ld本身

man ld

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

依据链接器手册 看下 静态库 动态库的描绘

  • 静态库 .o文件的集合,.o文件里包括大局符号
  • 动态库 终究链接的镜像

持续在ld手册里搜索 ObjC

4种load方法

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

  • 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工程

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

找到llvm源码 llvm-objcopy 路径

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

在相同目录下 翻开 CMakeLists.txt

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

添加 add_llvm_tool_subdirectory(llvm-objcopy)

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

在编译的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

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

运用 llvm-objcopy 之后,大局符号 _global_func 被修正 为 _libIFLTestStaticLib_global_func

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

工程集成调整过的静态库编译经过,并没有发生抵触,静态库中的同名大局函数正常履行

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

直接经过符号调用

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

  • 文中测验项目链接地址

  • iOS中的符号抵触(二)- 了解进阶

  • 我正在参与技术社区创作者签约计划招募活动,点击链接报名投稿

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。