这里会涉及到 ELF 中的相关变量成员如下:
Elf32_Ehdr
Elf32_Shdr
e_shnum
sh_name
e_shstrndx
.shstrtab
sh_offset
Elf32_Ehdr 是描述 ELF 头部信息的结构体,定义如下:
[C++] 纯文本查看 复制代码 typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
这里,用到的结构体成员为 e_shnum 和 e_shstrndx 。
正如注释所说,e_shnum 表示的是节区头部表格的条目数。这个值可以用 readelf -S xxxx.o 这样的命令可以看到,比如:[beyes@beyes c]$ readelf -S do_nofail.o
There are 13 section headers, starting at offset 0x21c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 0000ad 00 AX 0 0 4
[ 2] .rel.text REL 00000000 000520 000058 08 11 1 4
[ 3] .data PROGBITS 00000000 0000e4 000000 00 WA 0 0 4
[ 4] .bss NOBITS 00000000 0000e4 000000 00 WA 0 0 4
[ 5] .rodata PROGBITS 00000000 0000e4 000052 00 A 0 0 1
[ 6] .comment PROGBITS 00000000 000136 00002d 01 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 00000000 000163 000000 00 0 0 1
[ 8] .eh_frame PROGBITS 00000000 000164 000058 00 A 0 0 4
[ 9] .rel.eh_frame REL 00000000 000578 000010 08 11 8 4
[10] .shstrtab STRTAB 00000000 0001bc 00005f 00 0 0 1
[11] .symtab SYMTAB 00000000 000424 0000d0 10 12 9 4
[12] .strtab STRTAB 00000000 0004f4 00002a 00 0 0 1 前面的第 1 列中的 12 即表示 e_shnum 这个数值。
e_shoff 成员由注释 “Section header table file offset” 知道,它表示的是节区头部表格的偏移。也就是说,通过它可以定位到 “节区头部表格”。通过下图可以看到 "节区头部表格“ 的位置:
![]()
实际上,在 ELF 中只有 "ELF头部" 区域是放在 ELF 文件的开始处,而其它的节区则不强制顺序,但这里可以认为它就是放在其它所有节区的底部。
”节区头部表格“ 里存放着各个节区头部的描述信息,这些信息由下面的结构体来表示:
[C++] 纯文本查看 复制代码 /* Section header. */
typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;
再看e_shstrndx 这个变量由注释 “Section header string table index” 知道,它是 “节区头部字符串表格索引” 。由这个变量就可以从 ”节区头部表格“ 中找到上面输出中的第 [10] 个条目所表示 .shstrtab 节区头部。
.shstrtab 节区里存放着其它节区的名字(字符串表示)。那么是如何从 .shstrtab 里面找出某个节区的名字呢?答案就是由 ”节区头部表格“ 里的 sh_name 这个成员(该成员是一个整数)作为索引来找到的。比如在 .rodata 这个节区的头部结构里,利用它的成员 sh_name 就可以从 .shstrtab 里找到 .rodata 这个字符串。
下面用一段程序来演示上面的描述:
[C++] 纯文本查看 复制代码 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <elf.h>
#include <sys/mman.h>
int main(void)
{
void *map;
int fd;
struct stat st;
unsigned long size;
int i;
Elf32_Ehdr *hdr; //Elf header struct
Elf32_Shdr *secheader; //Section header
fd = open ("do_nofail.o", O_RDONLY);
if (fd < 0 || fstat(fd, &st) != 0) {
perror ("open");
exit (EXIT_FAILURE);
}
size = st.st_size;
map = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
close (fd);
if (map == MAP_FAILED)
return 1;
hdr = map;
secheader = (void *)hdr + hdr->e_shoff; //section header table
/* section header string table section (.shstrtab) */
const char *secstrings = (void *)hdr + secheader[hdr->e_shstrndx].sh_offset;
for (i = 0; i < hdr->e_shnum; i++) {
printf ("%s\n", secstrings + secheader[i].sh_name);
}
munmap(map, size);
return 0;
}
运行输出:[beyes@beyes c]$ ./sectionname
.text
.rel.text
.data
.bss
.rodata
.comment
.note.GNU-stack
.eh_frame
.rel.eh_frame
.shstrtab
.symtab
.strtab 在上面程序中,我们从 .shstrtab 里遍历了所有节区的名称。
为了形象的表示上述过程,我画了个图:
![]() |