深入理解Symbol

前言

符号(Symbol)是日常开发中经常接触的一个概念,虽然日常开发中直接应用的场景比较少,但符号编译期和运行时都扮演了重要的角色。

符号是什么

维基百科的定义

A symbol in computer programming is a primitive data type whose instances have a unique human-readable form.

直观理解,符号是一个数据结构,包含了名称(String)和类型等元数据,符号对应一个函数或者数据的地址。

Symbol Table

符号表存储了当前文件的符号信息,静态链接器(ld)和动态链接器(dyld)在链接的过程中都会读取符号表,另外调试器也会用符号表来把符号映射到源文件。

如果把调试符号裁剪掉(Deployment Postprocessing选择为YES),那么文件里的断点会失效:

在这里插入图片描述

Release模式下是可以裁剪掉符号的,因为release模式下默认有dsym文件,调试器仍然可以从中获取到信息正常工作。

符号表中存储符号的数据结构如下:

struct nlist_64 {
    union {
        uint32_t  n_strx; /* index into the string table */
    } n_un;
    uint8_t n_type;        /* type flag, see below */
    uint8_t n_sect;        /* section number or NO_SECT */
    uint16_t n_desc;       /* see <mach-o/stab.h> */
    uint64_t n_value;      /* value of this symbol (or stab offset) */
};

字符串存储在String Table里,String Table的格式很简单,就是一个个字符串拼接而成。符号的n_strx字段存储了符号的名字在String Table的下标。

在这里插入图片描述

Dynamic Symbol Table

Dynamic Symbol Table是动态链接器(dyld)需要的符号表,是符号表的子集,对应的数据结构很简单,只存储了符号位于Symbol Table的下标:

➜ otool -I main 
main:
...
Indirect symbols for (__DATA,__la_symbol_ptr) 1 entries
address            index
0x000000010000c000     4 //对应符号表的idx为4的符号
....

在这里插入图片描述

感兴趣的同学可能会问,既然Dynamic Symbol Table只存储了下标,这里otool是如何知道这个Indirect symbol属于__DATA,__la_symbol_ptr

答案是用section_64的reserved字段:如果一个section是__DATA,__la_symbol_ptr,那么它的reserved1字段会存储一个Dynamic Symbol Table下标。

struct section_64 { /* for 64-bit architectures */
  char    sectname[16]; /* name of this section */
  char    segname[16];  /* segment this section goes in */
  uint64_t  addr;   /* memory address of this section */
  uint64_t  size;   /* size in bytes of this section */
  uint32_t  offset;   /* file offset of this section */
  uint32_t  align;    /* section alignment (power of 2) */
  uint32_t  reloff;   /* file offset of relocation entries */
  uint32_t  nreloc;   /* number of relocation entries */
  uint32_t  flags;    /* flags (section type and attributes)*/
  uint32_t  reserved1;  /* reserved (for offset or index) */
  uint32_t  reserved2;  /* reserved (for count or sizeof) */
  uint32_t  reserved3;  /* reserved */
};

所以,对于位于__la_symbol_ptr的指针,我们可以通过如下的方式来获取它的符号名:

  1. 遍历load command,如果发现是__DATA,__la_symbol_ptr,那么读取reserved1,即__la_symbol_ptr的符号位于Dynamic Symbol Table的起始地址。
  2. 遍历__DATA,__la_symbol_ptr处的指针,当前遍历的下标为idx,加上reserved
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值