曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 8373|回复: 1
打印 上一主题 下一主题

ELF 编程

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2011-7-28 23:59:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
这里会涉及到 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 里遍历了所有节区的名称。

为了形象的表示上述过程,我画了个图:

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
沙发
 楼主| 发表于 2011-8-1 15:36:22 | 只看该作者

通过 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_shndxst_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[i].sh_type == SHT_SYMTAB) {
                        symtab_start = (void *)hdr + sechdrs[i].sh_offset;   /* 符号表节区起始地址 */
                        symtab_stop = (void *)hdr + sechdrs[i].sh_offset + sechdrs[i].sh_size;   /* 符号表节区终止地址 */
                        strtab = (void *)hdr + sechdrs[sechdrs[i].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
在上面的输出中,空白处表示该处没有相关的符号字符串。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-2 23:05 , Processed in 0.084235 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表