如何像nm工具一样打印ELF文件的符号名?相关技术疑问
嗨,我来帮你理清这个问题~
实现类似nm工具打印ELF符号名的正确姿势
首先得先纠正一个小误区:你提到的shstrtab和符号名其实没啥直接关系,不用把它转换成Elf64_Sym结构体来获取符号名。下面我给你拆解清楚各个表的作用和正确的实现步骤:
1. 先搞懂几个关键ELF表的分工
- shstrtab(段头字符串表):它存储的是ELF文件中各个段(section)的名称,比如
.text、.symtab、.strtab这些段的名字,和符号本身的名字无关。 - symtab(符号表段):这才是存储符号信息的地方,里面是一个个
Elf64_Sym结构体,每个结构体记录了符号的地址、类型、名字偏移等信息。 - strtab(符号字符串表):真正存储符号名称的地方,
Elf64_Sym里的st_name字段是一个偏移量,指向这个表中的具体字符串。
2. 正确获取符号名的步骤
要实现类似nm的功能,核心是处理symtab和strtab,步骤大概是这样:
- 读取ELF文件的文件头(
Elf64_Ehdr),通过它找到段头表(section header table)的位置。 - 遍历段头表(每个元素是
Elf64_Shdr),定位两个关键段:- 找到类型为
SHT_SYMTAB的段(也就是symtab),它的内容是Elf64_Sym数组。 - 通过symtab段头的
sh_link字段,找到对应的strtab段(这个字段是段头表中strtab的索引)。
- 找到类型为
- 遍历symtab里的每个
Elf64_Sym:- 用
st_name的值作为偏移量,到strtab的起始地址处读取对应的字符串,这就是符号的名字。 - 还可以通过
st_info、st_shndx等字段判断符号类型(比如全局函数、局部变量),像nm那样输出对应的标记。
- 用
3. shstrtab的正确用法
虽然shstrtab不直接参与符号名获取,但它能帮你更直观地定位段:遍历段头表时,每个段头的sh_name是shstrtab里的偏移,通过它可以拿到段的名称(比如判断某个段是不是.symtab或.strtab),方便调试或灵活定位。
简单代码片段示意
// 假设已将ELF文件读取到内存,ehdr是Elf64_Ehdr指针,shdrs是段头表数组 Elf64_Shdr *symtab_shdr = NULL; Elf64_Shdr *strtab_shdr = NULL; char *strtab = NULL; Elf64_Sym *symtab = NULL; // 遍历段头表,定位symtab和对应的strtab for (int i = 0; i < ehdr->e_shnum; i++) { // 用shstrtab获取当前段的名称 char *sec_name = (char *)((uint8_t *)ehdr + shdrs[ehdr->e_shstrndx].sh_offset) + shdrs[i].sh_name; if (shdrs[i].sh_type == SHT_SYMTAB) { symtab_shdr = &shdrs[i]; // symtab的sh_link字段指向strtab的段索引 strtab_shdr = &shdrs[symtab_shdr->sh_link]; strtab = (char *)((uint8_t *)ehdr + strtab_shdr->sh_offset); symtab = (Elf64_Sym *)((uint8_t *)ehdr + symtab_shdr->sh_offset); break; } } // 遍历符号表打印符号名 if (symtab && strtab) { int sym_count = symtab_shdr->sh_size / symtab_shdr->sh_entsize; for (int i = 0; i < sym_count; i++) { if (symtab[i].st_name != 0) { // 跳过无名符号 char *sym_name = strtab + symtab[i].st_name; printf("%s\n", sym_name); // 这里还可以添加符号地址、类型等信息的打印 } } }
这样就能正确获取并打印ELF文件里的符号名啦,核心是别把shstrtab和strtab搞混哦~
内容的提问来源于stack exchange,提问作者SJS
相关产品推荐
相关产品推荐

