Mach-O LC_SYMTAB 详解
LC_SYMTAB 是 Mach-O 文件中用于描述符号表(Symbol Table)的加载命令。符号表包含了程序中定义和引用的函数、变量等符号信息,是链接和调试过程中的关键数据结构。
LC_SYMTAB 结构
LC_SYMTAB 使用 symtab_command结构来描述符号表的信息:
c
struct symtab_command {
uint32_t cmd; /* LC_SYMTAB */
uint32_t cmdsize; /* sizeof(struct symtab_command) */
uint32_t symoff; /* 符号表在文件中的偏移 */
uint32_t nsyms; /* 符号数量 */
uint32_t stroff; /* 字符串表在文件中的偏移 */
uint32_t strsize; /* 字符串表大小 */
};
字段详解
1. cmd 和 cmdsize
cmd:命令类型,值为LC_SYMTABcmdsize:命令大小,固定为sizeof(struct symtab_command)
2. symoff 和 nsyms
symoff:符号表在文件中的字节偏移量nsyms:符号表中符号的数量
3. stroff 和 strsize
stroff:字符串表在文件中的字节偏移量strsize:字符串表的大小(字节数)
符号表结构
符号表中的每个符号使用 nlist或 nlist_64结构表示:
32位 nlist 结构
c
struct nlist {
union {
uint32_t n_strx; /* 字符串表索引 */
} n_un;
uint8_t n_type; /* 符号类型 */
uint8_t n_sect; /* 符号所在节 */
int16_t n_desc; /* 符号描述 */
uint32_t n_value; /* 符号值/地址 */
};
64位 nlist_64 结构
c
struct nlist_64 {
union {
uint32_t n_strx; /* 字符串表索引 */
} n_un;
uint8_t n_type; /* 符号类型 */
uint8_t n_sect; /* 符号所在节 */
uint16_t n_desc; /* 符号描述 */
uint64_t n_value; /* 符号值/地址 */
};
符号字段详解
1. n_strx(字符串索引)
指向字符串表中的索引,通过该索引可以找到符号的名称字符串。
2. n_type(符号类型)
符号的类型和属性信息,包括:
N_UNDF:未定义符号N_ABS:绝对符号N_SECT:节定义符号N_PBUD:预绑定未定义符号N_INDR:间接符号
通过位运算可以获取更多属性:
N_EXT:外部符号位N_STAB:调试符号位
3. n_sect(节索引)
当符号类型为 N_SECT 时,该字段表示符号所在的节索引。值从1开始,NO_SECT(0)表示符号不在任何节中。
4. n_desc(符号描述)
符号的附加描述信息,包括:
- 引用类型(reference type)
- 库序号(library ordinal)
- 符号属性(如弱引用、私有符号等)
5. n_value(符号值)
符号的值或地址,具体含义取决于符号类型:
- 对于节定义符号,表示符号的地址
- 对于未定义符号,通常为0
- 对于调试符号,可能表示偏移量
字符串表
字符串表是一个连续的字符序列,存储所有符号的名称。每个字符串以null字符('\0')结尾。符号结构中的 n_strx 字段是字符串在字符串表中的索引。
例如,如果字符串表内容为:
"\0_main\0_printf\0_hello\0"
那么:
- 索引0:""(空字符串)
- 索引1:"main"
- 索引6:"printf"
- 索引13:"hello"
工作原理
当链接器或调试器需要处理 Mach-O 文件时:
- 通过 LC_SYMTAB 命令找到符号表和字符串表在文件中的位置
- 读取符号表中的每个符号条目
- 通过符号条目中的
n_strx索引在字符串表中查找符号名称 - 根据符号类型和值进行相应的处理
实际应用示例
使用 otool 查看符号表
bash
# 查看符号表
otool -s __LINKEDIT [offset] executable_file
# 查看LC_SYMTAB信息
otool -l executable_file | grep -A 6 LC_SYMTAB
使用 nm 查看符号
bash
# 列出所有符号
nm executable_file
# 列出详细符号信息
nm -a executable_file
示例输出:
0000000100000f40 T _main
0000000100000f80 t _hello
U _printf
其中:
T:在文本(代码)节中的外部符号t:在文本(代码)节中的本地符号U:未定义的外部符号
与其他命令的关系
LC_SYMTAB vs LC_DYSYMTAB
LC_SYMTAB:描述基本的符号表信息LC_DYSYMTAB:描述动态链接器使用的额外符号表信息,如间接符号表等
在 __LINKEDIT 段中
符号表和字符串表通常存储在 __LINKEDIT 段中,该段包含链接和调试所需的各种信息。
重要性
LC_SYMTAB 是 Mach-O 文件中非常重要的加载命令,它:
- 为静态链接器和动态链接器提供符号解析所需的信息
- 为调试器提供符号到源代码的映射
- 支持动态库的符号绑定过程
- 是程序运行时符号查找的基础
理解 LC_SYMTAB 对于进行逆向工程、安全分析、调试和开发底层系统工具非常重要。它是程序链接和调试机制的核心数据结构之一。