ios底层原理

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

2022-08-10  本文已影响0人  erlich

理解符号的种类与作用

按照功能划分

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

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

简化一点

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
image.png

发现 DEPLOYMENT_POSTPROCESSING = NO

STRIP_STYLE = all

image.png

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

我们再看下配置

image.png

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

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

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

理解strip命令的实际作用

一个全局函数

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

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

这里可以验证一下

image.png
image.png
image.png

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

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

image.png

解决符号冲突

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

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

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

image.png

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

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

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

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

image.png

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

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

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

回归到链接器ld本身

man ld

image.png

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

继续在ld手册里搜索 ObjC

4种load形式

image.png

避免冲突分析

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

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

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

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

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源码的编译单独更新一篇博客出来

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

结果出错 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
上一篇下一篇

猜你喜欢

热点阅读