关于symbol table的一篇埋了坑的文章

1,438 阅读8分钟

前言

学如逆水行舟,不进则退。共勉!!!!

闲来无事,我又来掘金摸鱼了。给大家讲讲Symbol Table吧,英文是不是显得很有逼格。这篇文章了,算不上文章吧。就算我的记录吧,是我闲下来打发时间的,如果有些地方有错误,还请指正。最后不要吝啬你的赞和关注啊,你的赞和关注就是我前进的动力,我的厚脸皮都有点遭不住了。

Symbol Table

通过两个 load commands ғ

  • LC_SYMTAB

  • LC_DYSYMTAB:描述动态链接器使用其他的Symbol Table 信息

用来描述Symbol Table的大小和位置,以及其他元数据。

LC_SYMTAB

用来描述该文件的符号表。不论是静态链接器还是动态链接器在链接此文件时,都要使用该load command。调试器也可以使用该load command找到调试信息。

symtab_command

定义LC_SYMTAB加载命令具体属性。在 usr/include/mach-o/loader.h中定义:

struct symtab_command {
     //共有属性。指明当前描述的加载命令,当前被设置为LC_SYMTAB
     uint32_t cmd;
     //共有属性。指明加载命令的大小,当前被设置为sizeof(symtab_command)
     uint32_t  cmdsize;
     //表示从文件开始到symbol table所在位置的偏移量。symbol table用[nlist]来表示
      uint32_t symbol
      //符号表内符号的数量
      uint32_t nsyms;
      //表示从文件开始到string table所在位置的偏移量
      uint32_t stroff
      //表示string table大小(以byte为单位)
      uint32_t strsize;
 };   

nlist

定义符号的具体表示含义:

struct nlist{
         //表示该符号在string table的索引
         union{
              //在Mach-0中不适用此字段
              char *n_name;
              //索引
              long  n_strx;
             }n_un;
             unsigned char n_type;    /*type flag, see below */
             unsigned char n_sect;   /* section number or NO_SECT */
             shortn_desc;   n_sesc;      /* see <mach-o/stab.h> */
             unsigned long n_value; /* value of this symbol (or stab offset) */
             };
  • n_type

1字节,通过四位掩码保存数据:

    • N_STAB(0xe0):如果当前n_type包含这3位中的任何一位,则该符号为调试符号表(stab)。在这种情况下,整个
    • n_type 字段将被解释为stab value。请参阅/usr/include/mach-o/stab.h 以获取有效的stab value。
    • N_PEXT(0x10):如果当前的n_type包含此位。则将此符号标记为私有外部符号__ private_extern__(visibility=hidden),只在程序内可引用和访问。当文件通过静态链接器链接的时候,不要将其转换成静态符号(可以通过ld的 -keep_private_externs关闭静态链接器的这种行为)。
    • N_TYPE(0x0e) : 如果当前的n_type包含此位。则此符号为外部符号。该符号在该文件外部定义或在该文件中定义,但可在其他文件中使用。#####N_TYPE N_TYPE 字段的值包括:
    • N_UNDF(0x0):该符号未定义。未定义符号是在当前模块中引用,但是被定义在其他模块中的符号。n_sect字段设置为NO_SECT 。
    • N_ABS(0x2) :该符号是绝对符号。链接器不会更改绝对符号的值。n_sect 字段设置为NO_SECT
    • N_SECT(0xe):该符号在n_sect中指定的段号中定义。
    • N_PBUD(0xc) :该符号未定义,镜像使用该符号的预绑定值。n_sect字段设置为NO_SECT。
    • N_INDR(0xa) :该符号定义为与另一个符号相同。n_value字段是string table中索引,用于指定另一个符号的名称。链接该符号时,此符号和另一个符号都具有相同的定义类型和值。 stab value 包括:
#define N_GSYM 0x20    /*全局符号 :name,,NO_SECT,type,0 */  
#define N_FNAME 0x22   /* procedure name (f77 kludge): name,,NO_SECT,0,0 */
#define N_FUN 0x24     /* 方法/函数 name,,n_sect,linenumber,address */
#define N_STSYM 0x26   /*静态符号 :name,,n_sect,type,address */
#define N_LCSYM 0x28   /* .lcomm  符号ݩ :name,,n_sect,type,address */
#define N_BNSYM 0x2e   /* nsectᒧݩ୏ত: 0,,n_sect,0,address */
#define N_OPT 0x3c     /* emitted with gcc2_compiled and in gcc source */
#define N_RSYM 0x40    /* 寄存器符号 :name,,NO_SECT,type,register */
#define N_SLINE 0x44   /* 代码行数: 0,,n_sect,linenumber,address */
#define N_ENSYM 0x4e    /* nsect符号结束: 0,,n_sect,0,address */
#define N_SSYM 0x60     /* 结构体符号 :name,,NO_SECT,type,struct_offset */
#define N_SO 0x64       /* 源码名称: name,,n_sect,0,address */
#define N_OSO 0x66      /* 目标代码名称: name,,0,0,st_mtime */
#define N_LSYM 0x80     /* 本地符号:name,,NO_SECT,type,offset */
#define N_BINCL 0x82    /* include file 开始: name,,NO_SECT,0,sum */
#define N_SOL 0x84      /* #included file 名称: name,,n_sect,0,address */
#define N_PARAMS 0x86   /* 编译器参数: name,,NO_SECT,0,0 */
#define N_VERSION 0x88  /* 编译器版本: name,,NO_SECT,0,0 */
#define N_OLEVEL 0x8A   /* 编译器- O 级别 :name,,NO_SECT,0,0 */
#define N_PSYM 0xa0     /* ݇参数: name,,NO_SECT,type,offset */
#define N_EINCL 0xa2    /* include file 结束: name,,NO_SECT,0,0 */
#define N_ENTRY 0xa4    /* alternate entry: name,,n_sect,linenumber,address */
#define N_LBRAC 0xc0    /*左括号:0,,NO_SECT,nesting level,address */
#define N_EXCL 0xc2     /* deleted include file: name,,NO_SECT,0,sum */
#define N_RBRAC 0xe0    /* ݦ右括号:0,,NO_SECT,nesting level,address */
#define N_BCOMM 0xe2    /* 通用符开始: name,,NO_SECT,0,0 */
#define N_ECOMM 0xe4    /* 通用符结束: name,,n_sect,0,0 */
#define N_ECOML 0xe8    /* end common (local name): 0,,n_sect,0,address */
#define N_LENG 0xfe     /* second stab entry with length information */



/*
  * for the berkeley pascal compiler, pc(1):
  */
  #define N_PC 0x30 /* global pascal symbol: name,,NO_SECT,subtype,line */
  • n_sect

整数,用来在指定编号的section中找到此符号;如果在该image的任何部分都找不到该符号,则为NO_SECT。根据section在 LC_SEGMENT加载命令中出现的顺序,这些section从1开始连续编号。

  • n_desc

16-bit值,用来描述非调式符号。第三位使用 REFERENCE_TYPE :

  • REFERENCE_FLAG_UNDEFINED_NON_LAZY(0x0) : 该符号是外部非延迟(数据)符号的引用

  • REFERENCE_FLAG_UNDEFINED_LAZY(0x1) : 该符号是外部延迟性符号(即对函数调用)的引用

  • REFERENCE_FLAG_DEFINED(0x2) :该符号在该模块中定义

  • REFERENCE_FLAG_PRIVATE_DEFINED(0x3) :该符号在该模块中定义,但是仅对该共享课库中的模块可见。

  • REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY(0x4) : 该符号在该文件的另一个模块中定义,是非延迟加载(数据)符号,并且仅对该共享库中的模块可见

  • REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY(0x5) :该符号在该文件的另一件模块中定义,是延迟加载(函数)符号,仅对该共享库中的模块可见。 另外还可以设置如下标识位:

  • REFERENCED_DYNAMICALLY(0x10) : 定义的符号必须是使用在动态加载中(例如dlsym和NSLookupSymbolInImage)。而不是普通的未定义符号引用。strip使用该位来避免删除那些必须存在的符号(如果符号设置了该位,则strip不回剥离它)。

  • N_DESC_DISCARDED(0x20) :在完全链接的image在运行时动态链接器有可能会使用此符号。不要在完全链接的image中设置此位。

  • N_NO_DEAD_STRIP(0x20) :定义在可重定位目标文件(类型为 MH_OBJECT )中的符号设置时,指示静态链接器不对该符号进行dead-strip。(请注意,与 N_DESC_DISCARDED(0x20)用于两个不同的木。)

  • N_WEAK_REF(0x40) : 表示此未定义符号时弱引用。如果动态链接器找不到该符号的定义,则将其符号地址设置为0.静态链接器会将此符号设置弱链接标志。

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

如果该文件是两级命名two-level namespace image (即如果mach_header中设置了 MH_TWOLEVEL标志),则n_desc的高8位表示定义此未定义符号的库的编号。使用宏 GET_LIBRARY_ORDINAL来获取此值,或者使用宏SET_LIBRARY_ORDINAL来设置此值。0指定当前image。1到253根据文件中LC_LOAD_DYLIB命令的顺序表明库号。254用于需要动态查找的未定义符号(仅在OSX v10.3和更高版本中受支持)。对于从可执行程序加载符号的插件。255,用来指定可执行image。对于flat namespaceimages,高8位必须为0.

  • n_value

    符号值。对于symbol table 中的每一项,该值的表达的意思都不同(具体由n_type字段说明)。对于 N_SECT符号类型,n_value是符号的地址。有关其他可能值的信息,请参见 n_type 字段的描述。

    Common symbols 必须为N_UNDF类型,并且必须设置N_EXT 位。Common symbols的n_value 是符号表示的数
    据的大小(以字节为单位)。在c语言中,Common symbol是在该文件中声明但未初始化的变量。 Common 
    symbols 只能出现在MH_OBJECT 类型的 Mach-O文件中。
    

section名称与作用

 名称                                        作用
  • TEXT.text 可执行的机器码
  • TEXT.cstring 去重后的C字符串
  • TEXT.const 初始化过的常量
  • TEXT.stubs 符号桩。lazybinding的表对用项指针指向的地址的代码
  • TEXT.stub_helper 辅助函数。当在lazybinding的表中没有找到对应项的指针表示 的真正的符号地址
  • TEXT.unwind_info 存储处理异常情况信息
  • TEXT.eh_frame 调试辅助信息
  • DATA.data 初始化过的可变的数据
  • DATA.nl_symbol_ptr 非lazy-binding的指针表,每个表中的指针指向一个装载过程 DATA.la_symbol_ptr lazy-binding的指针表,每个表中的指针一开始指向stub_helper
  • DATA.const 没有初始化过的常量
  • DATA.mod_init_func 初始化函数,在main之前调用
  • DATA.mod_term_func 终止函数,在main返回之后调用
  • DATA.bss 没有初始化的静态变量
  • DATA.common 没有初始化过的符号声明(for example, int i;)

nm命令

打印nlist结构的符号表(symbol table)。

常用nm命令参数

nm -pa a.o
-a: 显示符号表的所有内容
-g:显示全局符号
-p:不排序。显示符号表本来的顺序
-r:逆转顺序
-u:显示未定义符号
-m:显示N_SECT类型的符号(Mach-O符号)显示。
  
埋了几个坑,看各位读者有没有发现。发现了可以再评论区评论不足啊,说不定你就成为那位未来iOS开发大佬的老
师了。不要吝啬哦。如果你需要iOS资料可以点击下方领取,最后求一波点赞和关注。

iOS资料:下载地址