开发一定要搞懂的符号与符号表

符号

符号(Symbol)常用来表示一个地址,这个地址可能是一端程序的起始地址,也可能是一个变量的起始地址,简而言之,将它当做是标记或名称即可。
上一节聊到的链接 过程,实质上就是将不同的目标文件汇集在一起。链接构成中,目标文件的相互拼合汇集,实际上是目标文件之间对地址的引用。链接过程中,我们将函数变量 统一称作为符号 ,函数名与变量名称就是符号名 ,其记录的地址信息就是其符号值

符号分类

符号是有分类的,根据其特点,可分为内部符号和外部符号。

  • 内部符号:内部函数或方法、变量名称(当前Mach-O)
    • 如:AppDelegate,编译期就知道其地址,分为:
      • 全局符号 :全局作用域的函数、符号,整个项目可见
      • 本地符号 :当前文件内的符号,当前文件可见
  • 外部符号:外部函数或方法、变量名称(非当前Mach-O)
    • 如:NSLog,这类符号又分为:
      • 导入符号:对于当前 mach-O文件,导入了 NSLog 符号,称为:导入符号
      • 导出符号:对于Foundation 来说,导出了 NSLog 符号,称为:导出符号
    • 导出符号,一定是全局符号,可推理出,全局符号不能被脱去
    • 编译链接时不知道其地址,在动态链接的时候才能确定它的内存地址
    • 统一位于重定位符号表又叫**间接符号表**
  • 特殊符号:链接器生成可执行文件时生成的特殊符号等
    • __executable_start, 程序的起始地址,非入口地址
    • _end 或 end, 程序结束地址

? 为什么外部符号需要在动态链接时才能确定它的内存地址?

本地符号通过地址偏移可以得到,但是由于Mach-O 还没被加载,程序虚拟内存空间还没有被分配,导入的外部符号还没有内存地址。

全局符号

为了说明全局符号,在上述hello.c文件中添加如下代码

#include <stdio.h>
#define MAX_AGE 120

int globalVal = 99;
int globalUndefineVal;

int main() {
    printf("%s\n", "hello world~");
    printf("%d\n", MAX_AGE); // 简单输出
    return 0;
}
复制代码

执行编译输出a.out 查看,可见

  • _globalVal 和 _globalUndefinedVal 符号签名都是 g 说明是全局符号
  • _globalVal 和 _globalUndefinedVal 符号都在_DATA 段,但是一个在 __data 一个在 __common

image.png

本地符号

本地符号又叫静态符号,它和全局符号的区别是在可见性方面。为了对比本地符号,在上述hello.c文件中添加如下代码

int globalUndefineVal;

static int staticVal = 88;		# 静态变量
static int staticUndefineVal;	# 未初始化的静态变量

int main() {
printf("%d %d\n", staticVal, staticUndefineVal); // 简单输出
...
}
复制代码

注意:如果静态变量没有进行引用的话,会变成调试符号,无法输出
image.png

导入导出符号

导入与导出是相对的,一方的导出符号,可以作为另一方的导入符号,查看一下 a.out 的导出符号

objdump --macho --exports-trie  a
复制代码

image.png
可见,导出符号就是前面见到的 全局符号 ,全局符号默认是导出符号 ,当然也可以隐藏全局符号 ,如下示例

int globalVal2 __attribute__((visibility("hidden"))) = 99;
复制代码

通常,用到的外部符号会被放入到间接符号表 中,查看一下 a.out 的间接符号表

objdump --macho --indirect-symbols a.out
复制代码

image.png

符号表

链接过程中,我们需要对各种符号 进行管理,每一个目标文件 都会有一个对应的 符号表 Symbol Table ,用来记录目标文件中所有用到的符号,在静态链接时候,会进行相同性质段的合并。
符号表分类

  • Symbol Table: 所有符号表
    • 存储所有的符号信息,包含动态链接符号表内的符号
  • Dynamic Symbol Table:动态链接符号表,间接符号表(Indirect Symbol)
    • 存储着所有使用到的位于其他外部动态库中的符号信息,动态链接时进行绑定
  • String Table:符号的名称

image.png
image.png

常用命令

-----------------------------------------
# -a输出全部符号表,-p 不用排序

$ nm -pa hello.o
0000000000000000 T _main
                 U _printf
                 
$ nm -pa a
0000000100008008 d __dyld_private
0000000100000000 T __mh_execute_header
0000000100003f30 T _main
                 U _printf
                 U dyld_stub_binder
-----------------------------------------
# 查看 Mach-Header 内容
$ otool -h a
a:
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x00           2    16       1368 0x00200085
 
$ objdump --macho --private-header a
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00     EXECUTE    16       1368   NOUNDEFS DYLDLINK TWOLEVEL PIE
-----------------------------------------
# 查看 __TEXT 代码段 内容
$ objdump --macho -d  a
a:
(__TEXT,__text) section
_main:
100003f30:	55	pushq	%rbp
100003f31:	48 89 e5	movq	%rsp, %rbp
100003f38:	c7 45 fc 00 00 00 00	movl	$0, -4(%rbp)
...
100003f75:	5d	popq	%rbp
100003f76:	c3	retq
-----------------------------------------
# 查看符号
objdump --macho -syms hello.o
hello.o:
SYMBOL TABLE:
0000000000000048 g     O __DATA,__data _globalVal
0000000000000000 g     F __TEXT,__text _main
0000000000000004         *COM*	0000000000000004 _globalUndefineVal
0000000000000000         *UND* _printf
-----------------------------------------
# 查看导出符号
$ objdump --macho --exports-trie  a
a:
Exports trie:
0x100000000  __mh_execute_header
0x100003F30  _main
0x100008010  _globalVal
0x100008014  _globalUndefineVal
-----------------------------------------
# 查看间接符号
$ objdump --macho --indirect-symbols a
a:
Indirect symbols for (__TEXT,__stubs) 1 entries
address            index name
0x0000000100003f78     5 _printf
Indirect symbols for (__DATA_CONST,__got) 1 entries
address            index name
0x0000000100004000     6 dyld_stub_binder
Indirect symbols for (__DATA,__la_symbol_ptr) 1 entries
address            index name
0x0000000100008000     5 _printf
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享