曲径通幽论坛
标题: ELF 编程 [打印本页]
作者: beyes 时间: 2011-7-28 23:59
标题: ELF 编程
这里会涉及到 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.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 里遍历了所有节区的名称。
为了形象的表示上述过程,我画了个图:

作者: beyes 时间: 2011-8-1 15:36
标题: 通过 symtab 找到相应的符号
符号表 (Symblo Table)
符号表中包含用来定位、重定位程序中 符号定义 和 引用信息。符号表索引是对此数组的索引。索引 0 表示表中的第一个表项,同时也作为未定义符号的索引。
符号表中的结构单元定义如下:
[C++] 纯文本查看 复制代码
/* Symbol table entry. */
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
在上面的结果中,我们在这里会用到 2 个成员:st_shndx 和 st_name 。
st_shndx 给出了节区头部表索引。
st_name 则给出了文件符号字符串表(strtab)的索引,在 strtab 中各个符号名用字符串表示。
下面通过一个实例程序来找出 strtab 中的相关符号:
[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;
unsigned int r_sym;
int i;
char *strtab;
char *dstrtab;
Elf32_Ehdr *hdr; //Elf header struct
Elf32_Shdr *sechdrs; //Section header
Elf32_Sym *symtab_start;
Elf32_Sym *symtab_stop;
Elf32_Sym *sym;
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;
sechdrs = (void *)hdr + hdr->e_shoff; //section header table
/* Find symbol table */
for (i = 1; i < hdr->e_shnum; i++) {
if (sechdrs.sh_type == SHT_SYMTAB) {
symtab_start = (void *)hdr + sechdrs.sh_offset; /* 符号表节区起始地址 */
symtab_stop = (void *)hdr + sechdrs.sh_offset + sechdrs.sh_size; /* 符号表节区终止地址 */
strtab = (void *)hdr + sechdrs[sechdrs.sh_link].sh_offset; /* strtab 节区起始地址 */
}
}
/* 找不到符号表则退出 */
if (!symtab_start) {
printf ("fatal:no symtab!\\n");
return 1;
}
/* 遍历符号表节区中的每个结构单元 */
for (sym = symtab_start; sym < symtab_stop; sym++) {
if (sym)
printf ("%s\\n", strtab + sym->st_name); /* 利用每个单元中的 st_name 索引找出 strtab 中的符号名 */
}
munmap(map, size);
return 0;
}
上面程序中,我们首先遍历了每个节区头部(在头部表里)。在每个节区头部中,都会有一个 sh_type 成员,该成员指定了节区的类型,而 SHT_SYMTAB 这种类型表明该头部对应的节区就是符号表节区。
另外在节区头部结构中还看到 sh_link 这个成员。这个成员的属性是由 sh_type 的值来决定的,当 sh_type 为 SHT_SYMTAB (静态符号表)或者 SHT_DYNSYM (动态符号表) 时,sh_link 表示的是相关联的字符串表的节区头部索引。也就是说,通过它,可以找到在节区头部表里的 strtab 节区的头部信息。
运行输出:[beyes@beyes c]$ ./readsym
do_nofail.c
do_nofail
printf
main
malloc
在上面的输出中,空白处表示该处没有相关的符号字符串。
欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) |
Powered by Discuz! X3.2 |