符号的种类与作用

2021-02-21  本文已影响0人  猿人
全局符号 和本地符号

我们先看一段代码


屏幕快照 2021-02-21 下午8.29.33.png

查看Mach-O的符号

屏幕快照 2021-02-21 上午9.59.21.png

通过如下命令

objdump --macho --syms patch 
屏幕快照 2021-02-21 下午7.36.01.png

所以 l :就是 local 本地的意思 ,g 就是global 全局的意思

按照功能划分

Type 说明
f File
F Function
O Data
d Debug
*ABS* Absolute
*COM* Common
*UND* ?
调试符号

可以通过 连接器的参数 再链接的时候 不把调试符号放入我们可执行文件

OTHER_LDFLAGS=$(inherited) -Xlinker -S 
屏幕快照 2021-02-21 下午7.50.09.png

编译一下 再次查看符号表


屏幕快照 2021-02-21 下午8.30.55.png
visibility
可通过visibility属性,控制文件导出符号,限制符号可见性。 截屏2021-01-22 下午4.52.16.png

__attribute__ : 可以把编译器支持的一些参数传递给编译器(如上面是传入的是控制符号的可见性,那最好的隐藏符号是什么?就是是把全局符号变成本地符号)。


屏幕快照 2021-02-21 下午8.31.59.png
拓展

当我在本地写一个实现:


屏幕快照 2021-02-21 下午9.56.20.png

发现本地的被调用起来了

为什么 会调用起来呢 为啥不冲突 因为连接器默认采用二级命名空间 修改为一级就报错了。

截屏2021-01-22 下午5.02.24.png
导入和导出符号

是导入 还是导出 相对于来说的,比如你调用了NSLog 那相对于Foundation 库来说就是 导出,相对于你来说就是导入。

那导出符号一定是 全局符号了?从字面上是这样的 那么我来看一下

查看mach-O的导出符号命令

objdump --macho --exports-trie 黑不溜秋的
截屏2021-01-22 下午5.11.11.png

那导出符号一定是全局符号吗?按道理是的 ,但是我们可以通过连接器控制它。

动态库 是在运行的时候加载,那就意味这它在编译连接阶段的时候提供符号就可以了。
间接符号表,保存着当前使用的其他动态库的符号

查看间接符号表

objdump --macho --indirect-symbols 黑不溜秋的
截屏2021-01-22 下午5.23.47.png

好像只认识NSlog 但是它是Fundation 给我们提供的导出符号
从上面我们可以理解:
1、全局符号可以变成导出符号 ,那么变成导出符号之后 可以给外面使用
2、当我们处理符号,因为符号存在我们Mach-o中是占用一定体积的 。 那能处理能删除间接符号吗?那肯定必须的不能删除啊,为什么?因为对于我们项目mach-o来说 间接符号表 里保存着当前使用着的动态库的符号,而动态库是运行的时候去加载。所以也就意味着 我们在编译链接的时候只需要提供它所用到的符号就行,而这符号就存在 间接符号表里。
3、当我们站在动态库的角度去看,全局符号 可以变成导出符号,供外界使用,那我要去脱符号也就意味着,只能去脱不是全局符号的符号。

这里会有一个问题 假如你的一个动态库 有好多好多的全局符号,并且 是OC 这里提一点 OC默认的都是导出符号。那么为了缩小体积该怎么办?
这就需要链接器给我们提供的一个参数不导出符号

-Xlinker -unexported_symbol  -Xlinker _OBJC_CLASS_$_LGOneObjc

     -unexported_symbols_list file
                 The specified filename contains a list of global symbol names
                 that will not remain as global symbols in the output file.
                 The symbols will be treated as if they were marked as __pri-
                 vate_extern__ (aka visibility=hidden) and will not be global
                 in the output file. The symbol names listed in filename must
                 be one per line.  Leading and trailing white space are not
                 part of the symbol name.  Lines starting with # are ignored,
                 as are lines with only white space.  Some wildcards (similar
                 to shell file matching) are supported.  The * matches zero or
                 more characters.  The ? matches one character.  [abc] matches
                 one character which must be an 'a', 'b', or 'c'.  [a-z]
                 matches any single lower case letter from 'a' to 'z'.


 -map map_file_path
                 Writes a map file to the specified path which details all
                 symbols and their addresses in the output image.

具体信息可以通过 来查看

man ld 

按照符号种类划分

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

通过如下命令真实看一下

nm -pa  地址
截屏2021-03-01 下午2.20.23.png

Weak Symbol

Weak defintion Symbol :

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


截屏2021-03-04 下午1.45.49.png
截屏2021-03-04 下午1.50.06.png

将其加入到Compilp Sources里 并编译


截屏2021-03-04 下午1.55.37.png

查看可执行文件的导出符号

objdump -macho --exports-trie  地址
截屏2021-03-04 下午1.59.25.png

那我在main函数里写一个同名实现方法,编译会报错吗?

截屏2021-03-04 下午2.02.57.png
Weak Reference Symbol:

表示此未定义符号是弱引用。如果动态链接器找不到该符号的定义,则将其符号为0.静态链接器会将此符号设置弱链接标志。

截屏2021-03-04 下午1.46.51.png
截屏2021-03-04 下午2.21.06.png 截屏2021-03-04 下午2.28.03.png

此时 运行成功,那有什么意义呢?往下看

此时我们可以把 下图标红的地方去掉,在运行。


截屏2021-03-04 下午2.41.52.png

运行


截屏2021-03-04 下午2.44.21.png

咦报错了? 为啥报错,因为在ld连接的过程中 找不到当前符号的地址。

那我们可以通过告诉连接器 这个符号你别管,它是动态链接的,通过如下命令

   -U symbol_name
                 Specified that it is ok for symbol_name to have no defini-
                 tion.  With -two_levelnamespace, the resulting symbol will be
                 marked dynamic_lookup which means dyld will search all loaded
                 images.
截屏2021-03-04 下午2.54.57.png

再次运行


截屏2021-03-04 下午2.56.43.png

我们想一下 此时他们在同一片作用域空间中,假如 我编写的是一个 动态库,我将所有的符号变为弱引用符号,那是不是意味着 延迟到dyld加载的时候 找到某个符号的时候 再去做事情 Σ(⊙▽⊙"a

重新导出符号

我们先看一下当前可执行文件的符号表

objdump --macho --syms
截屏2021-03-04 下午3.12.12.png

上面我们将的NSLog 对于我们当前的可执行文件来说,

我们看到了 它的符号表它是一个未定义的符号 *UND*,

那假如说别的 库也想使用我的这个可执行文件中的NSLog怎么办?所以就需要重新导出一下。

同样连接器也给我们提供了相对的接口


     -alias symbol_name alternate_symbol_name
                 Create an alias named alternate_symbol_name for the symbol
                 symbol_name.  By default the alias symbol has global visibil-
                 ity.  This option was previous the -idef:indir option.
 

我们试一下


截屏2021-03-04 下午3.23.21.png

我们先看一下当前的符号表

objdump --macho --syms 
截屏2021-03-04 下午3.29.09.png

还可以用nm 命令查看一下 可更友好的查看

nm -m 
截屏2021-03-04 下午3.31.38.png

再次查看导出符号

objdump -macho --exports-trie  地址
截屏2021-03-04 下午3.24.22.png

OC 的符号 默认都是导出符号,因为它是动态型语言
swift是编译型语言,所以很多符号在编译期就知道是什么类型,可通过public private open 等控制。

Common Symbol

在定义时,未初始化的全局符号


截屏2021-03-01 下午2.25.27.png

未定义符号作用:
1、当它找到定义之后,在编译连接的时候会把未定义的删掉。
2、 如果是未定义的符号 链接器会把它强制已经定义的 ,比如直接赋值为 0;这就是为什么我定义了一个符号,没有赋值也没有使用的时候xcode会报警告 ,这就是链接器会识别common 符号 按照一定规则 ,如你有未定义的 要么给你强制变成 已定义的, 要么给你报警告 要么报错这是可以选择的。

strip

首先通过 上面文章 我们知道
对于动态库 我们只能去脱不是全局符号的符号。
那对于app来说 我们分析:
1.首先你要考虑要不要给别人使用。那这里这个奇奇怪怪的问题,要不是奇奇怪怪的app 也用不到。
2.通过上面我们也知道 除了间接符号表里的符号不能脱,那是不是意味着别的都可以脱掉 本地的 全局的 弱定义的 都可以干掉 只留下 间接符号表中的。

那对于静态库来说呢?
静态库 是 .o的合集 也就意味着,它里边还有重定位符号表,那重定位符号表里面是啥来,是保存着你项目用到的符号,那它能删吗? 删了那我连接还怎么重定位。那还有什么可以脱 是不是还有调试符号

大家不妨想一想:
那就符号来说
app 使用静态库体积小 还是动态库体积小?
答案是静态库 因为 静态库你只能去脱掉调试符号,那app 是不是除了间接符号表 其他的都可以脱掉?
那动态库 的符号 是不是最终都放在了app的间接符号表中?

上一篇 下一篇

猜你喜欢

热点阅读