ELF符号解析


解析ELF符号

1.elf基本概念

ELF 是 Executable and Linking Format 的缩写,其类型定义在Elf64_Ehdr中,字段名为e_type,主要有3种类型

  • 可重定位文件(ET_REL) 即所谓的目标文件,在这些文件中,如果引用到其它目标文件或库文件中定义的符号(变量或者函数)的话,只是给出一个名字,这里还并不知道这个符号在哪里,其 具体的地址是什么。需要在连接的过程中,把对这些外部符号的引用重新定位到其 真正定义的位置上,所以称目标文件为“可重定位”或者“待重定位”的。
  • 共享目标文件(ET_DYN) 即动态连接库文件。它在以下两种情况下 被使用:第一,在连接过程中与其它动态链接库或可重定位文件一起构建新的目标文件;第二,在可执行文件被加载的过程中,被动态链接到新的进程中,成为运行代码的一部分。
  • 可执行文件(ET_EXEC) 即正常的可执行文件。

不同视角下的elf

ELF的作用有两个,一是用于构建程序,构建动态链接库或都可执行程序,主要体现在连接的过程;二是用于运行程序。在这两种情况下,我们可以从不同的视角来看待同一个目标文件。对于同一个目标文件,当它分别被用于连接和用于执行的时候,其特性是不一样的,我们所关注的内容也不一样。在Elf64_Ehdr的定义中,e_phoffe_shoff分别指定了段头表和节头表的偏移量。

1.连接视图

这个视图主要用于构建程序、构建动态链接库或者可执行文件,这时候关注的是**节(section)**,主要关心的时符号的解析。

2.运行视图

这个视图下是准备执行时使用,加载器只需要按照段头表描述的那样,将对应的段(segment)加载进内存,并按照要求设置好段的权限并最终跳转到e_entry所指定的位置执行即可。

ELF文件头定义

Elf64_Ehdr定义在elf.h文件中,e_ident到e_version都是一些元信息,文件头包含的比较重要的信息就是程序头表(段头表)和节头表的偏移和对应表项的大小。对于节头表,有一个比较重要的信息就是字符串节表在整个节表中的索引,字符串里面存储了节名称的字符串,而每个节的定义里有一个sh_name 指向这个字符串表里的一个字符串,每个字符串以NULL结尾,对于解析ELF,找到字符串表是定位指定节的关键。

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
  Elf64_Half	e_type;			/* Object file type */
  Elf64_Half	e_machine;		/* Architecture */
  Elf64_Word	e_version;		/* Object file version */
  Elf64_Addr	e_entry;		/* Entry point virtual address */
  Elf64_Off	e_phoff;		/* Program header table file offset */
  Elf64_Off	e_shoff;		/* Section header table file offset */
  Elf64_Word	e_flags;		/* Processor-specific flags */
  Elf64_Half	e_ehsize;		/* ELF header size in bytes */
  Elf64_Half	e_phentsize;		/* Program header table entry size */
  Elf64_Half	e_phnum;		/* Program header table entry count */
  Elf64_Half	e_shentsize;		/* Section header table entry size */
  Elf64_Half	e_shnum;		/* Section header table entry count */
  Elf64_Half	e_shstrndx;		/* Section header string table index */
} Elf64_Ehdr;
typedef struct
{
  Elf64_Word	sh_name;		/* Section name (string tbl index) *///节的名字在字符串表中的偏移
  Elf64_Word	sh_type;		/* Section type */
  Elf64_Xword	sh_flags;		/* Section flags */
  Elf64_Addr	sh_addr;		/* Section virtual addr at execution */
  Elf64_Off	sh_offset;		/* Section file offset */
  Elf64_Xword	sh_size;		/* Section size in bytes */
  Elf64_Word	sh_link;		/* Link to another section */
  Elf64_Word	sh_info;		/* Additional section information */
  Elf64_Xword	sh_addralign;		/* Section alignment */
  Elf64_Xword	sh_entsize;		/* Entry size if section holds table */
} Elf64_Shdr;

2.解析函数符号的地址

按照elf的基本概念,要解析函数符号的地址,就要从连接视图来看ELF文件,即要分析ELF的节表(section。

//这里假设ELF文件的已经被mmap到内存中,且其起始地址为h
Elf64_Ehdr *h;
// 这里获取节头表的偏移并使Shdr指向具体的地址
void *Shdr = (char *)h + h->e_shoff;
// 这里获取节的字符串表的具体地址,e_shstrndx为索引,所以先把Shdr转成Elf64_Shdr*
char * sectionStringTable = (char *)h + ((Elf64_Shdr *) Shdr + h-> e_shstrndx)->sh_offset;

在ELF的规范中,名字为**.strtab**的节存储了符号表的字符串,接下来需要遍历节表找到这个节

char * symtabStringTable = NULL;
// 遍历
for (int i = 0; i < h->e_shnum; i++)
{
    Elf64_Shdr *s = (Elf64_Shdr *)(Shdr+i*h->e_shentsize);
    // .strtab节
    if(strcmp(sectionStringTable+s->sh_name,".strtab")==0){
        // 记录字符串表的地址
        symtabStringTable = (char *)h + s->sh_offset;
    }
}

在ELF的规范中,名字为**.symtab为的节(Elf64_Shdr的sh_type为SHT_SYMTAB)**存储了符号信息,接下来需要遍历节表找到这个节,遍历即可

typedef struct
{
  Elf64_Word	st_name;		/* Symbol name (string tbl index) */
  unsigned char	st_info;		/* Symbol type and binding */
  unsigned char st_other;		/* Symbol visibility */
  Elf64_Section	st_shndx;		/* Section index */
  Elf64_Addr	st_value;		/* Symbol value */
  Elf64_Xword	st_size;		/* Symbol size */
} Elf64_Sym;//符号表的表项定义

for (int i = 0; i < h->e_shnum; i++)
{
    Elf64_Shdr *s = (Elf64_Shdr *)(Shdr+i*h->e_shentsize);
//找到.symtab节
    if(s->sh_type == SHT_SYMTAB){
        printf("seciton[%d] : %s \n",i,sectionStringTable+s->sh_name);
//获取symtable的首地址
        Elf64_Sym * symtable = (Elf64_Sym*)((char *)h + s->sh_offset);
//遍历symtable表
        for (int i = 0; i < s->sh_size/s->sh_entsize; i++,symtable++)
        {
            //符号类型是函数
            if(ELF64_ST_TYPE(symtable->st_info) == STT_FUNC)
            {
                //st_value即是函数在ELF文件中的偏移量,st_name为函数名在.strtab的字符串表中的索引
                printf("fun 0x%07lx \t %s\n",symtable->st_value, symtabStringTable+symtable->st_name);
            }
        }

    }
}

完整的实现代码

#include <sys/mman.h>
#include <elf.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
void dumpSymbols(Elf64_Ehdr *h){
    void *Shdr = (char *)h + h->e_shoff;
    char * sectionStringTable = (char *)h + ((Elf64_Shdr *) Shdr + h-> e_shstrndx)->sh_offset;
    char * symtabStringTable = NULL;
    char * dynsymStringTable = NULL;
    // 遍历section表
    for (int i = 0; i < h->e_shnum; i++)
    {
        Elf64_Shdr *s = (Elf64_Shdr *)(Shdr+i*h->e_shentsize);
        if(s->sh_type == SHT_STRTAB){
            if(strcmp(sectionStringTable+s->sh_name,".strtab")==0){
                symtabStringTable = (char *)h + s->sh_offset;
            }
            if(strcmp(sectionStringTable+s->sh_name,".dynstr")==0){
                dynsymStringTable = (char *)h + s->sh_offset;
            }   
        }
    }
    for (int i = 0; i < h->e_shnum; i++)
    {
        /* code */
        Elf64_Shdr *s = (Elf64_Shdr *)(Shdr+i*h->e_shentsize);

        if(s->sh_type == SHT_SYMTAB){
            printf("seciton[%d] : %s \n",i,sectionStringTable+s->sh_name);
            Elf64_Sym * symtable = (Elf64_Sym*)((char *)h + s->sh_offset);
            for (int i = 0; i < s->sh_size/s->sh_entsize; i++,symtable++)
            {
                if(ELF64_ST_TYPE(symtable->st_info) == STT_FUNC)
                printf("fun 0x%07lx \t %s\n",symtable->st_value, symtabStringTable+symtable->st_name);
            }
            
        }
    }
    
}
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s file\n", argv[0]);
        exit(1);
    }
    int fd = open(argv[1],O_RDONLY);
    assert(fd > 0);
    // 把elf mmap进来
    
    Elf64_Ehdr *h = mmap(NULL, 40960, PROT_READ, MAP_PRIVATE, fd, 0);
    close(fd);
    dumpSymbols(h);
    return 0;
}

运行结果,和readelf的对应的结果一致

# f19 @ F19999 in ~/jyy/elf-load [16:14:03] 
$ gcc myreadelf.c -o read

# f19 @ F19999 in ~/jyy/elf-load [16:14:05] 
$ ./read read 
seciton[28] : .symtab 
fun 0x0001170    deregister_tm_clones
fun 0x00011a0    register_tm_clones
fun 0x00011e0    __do_global_dtors_aux
fun 0x0001220    frame_dummy
fun 0x0001000    _init
fun 0x0001580    __libc_csu_fini
fun 0x0001229    dumpSymbols
fun 0x0001588    _fini
fun 0x0000000    mmap@@GLIBC_2.2.5
fun 0x0000000    printf@@GLIBC_2.2.5
fun 0x0000000    __assert_fail@@GLIBC_2.2.5
fun 0x0000000    close@@GLIBC_2.2.5
fun 0x0000000    __libc_start_main@@GLIBC_2.2.5
fun 0x0000000    strcmp@@GLIBC_2.2.5
fun 0x0000000    fprintf@@GLIBC_2.2.5
fun 0x0001510    __libc_csu_init
fun 0x0001140    _start
fun 0x000143b    main
fun 0x0000000    open@@GLIBC_2.2.5
fun 0x0000000    exit@@GLIBC_2.2.5
fun 0x0000000    __cxa_finalize@@GLIBC_2.2.5

# f19 @ F19999 in ~/jyy/elf-load [16:14:15] 
$ 
# f19 @ F19999 in ~/jyy/elf-load [16:15:16] C:1
$ readelf -s read  

Symbol table '.dynsym' contains 15 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mmap@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __assert_fail@GLIBC_2.2.5 (2)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND close@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcmp@GLIBC_2.2.5 (2)
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fprintf@GLIBC_2.2.5 (2)
     9: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND open@GLIBC_2.2.5 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2.2.5 (2)
    12: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    13: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)
    14: 0000000000004020     8 OBJECT  GLOBAL DEFAULT   26 stderr@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 75 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000318     0 SECTION LOCAL  DEFAULT    1 
     2: 0000000000000338     0 SECTION LOCAL  DEFAULT    2 
     3: 0000000000000358     0 SECTION LOCAL  DEFAULT    3 
     4: 000000000000037c     0 SECTION LOCAL  DEFAULT    4 
     5: 00000000000003a0     0 SECTION LOCAL  DEFAULT    5 
     6: 00000000000003c8     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000530     0 SECTION LOCAL  DEFAULT    7 
     8: 00000000000005e6     0 SECTION LOCAL  DEFAULT    8 
     9: 0000000000000608     0 SECTION LOCAL  DEFAULT    9 
    10: 0000000000000628     0 SECTION LOCAL  DEFAULT   10 
    11: 0000000000000700     0 SECTION LOCAL  DEFAULT   11 
    12: 0000000000001000     0 SECTION LOCAL  DEFAULT   12 
    13: 0000000000001020     0 SECTION LOCAL  DEFAULT   13 
    14: 00000000000010b0     0 SECTION LOCAL  DEFAULT   14 
    15: 00000000000010c0     0 SECTION LOCAL  DEFAULT   15 
    16: 0000000000001140     0 SECTION LOCAL  DEFAULT   16 
    17: 0000000000001588     0 SECTION LOCAL  DEFAULT   17 
    18: 0000000000002000     0 SECTION LOCAL  DEFAULT   18 
    19: 0000000000002064     0 SECTION LOCAL  DEFAULT   19 
    20: 00000000000020b0     0 SECTION LOCAL  DEFAULT   20 
    21: 0000000000003d80     0 SECTION LOCAL  DEFAULT   21 
    22: 0000000000003d88     0 SECTION LOCAL  DEFAULT   22 
    23: 0000000000003d90     0 SECTION LOCAL  DEFAULT   23 
    24: 0000000000003f80     0 SECTION LOCAL  DEFAULT   24 
    25: 0000000000004000     0 SECTION LOCAL  DEFAULT   25 
    26: 0000000000004020     0 SECTION LOCAL  DEFAULT   26 
    27: 0000000000000000     0 SECTION LOCAL  DEFAULT   27 
    28: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    29: 0000000000001170     0 FUNC    LOCAL  DEFAULT   16 deregister_tm_clones
    30: 00000000000011a0     0 FUNC    LOCAL  DEFAULT   16 register_tm_clones
    31: 00000000000011e0     0 FUNC    LOCAL  DEFAULT   16 __do_global_dtors_aux
    32: 0000000000004028     1 OBJECT  LOCAL  DEFAULT   26 completed.8061
    33: 0000000000003d88     0 OBJECT  LOCAL  DEFAULT   22 __do_global_dtors_aux_fin
    34: 0000000000001220     0 FUNC    LOCAL  DEFAULT   16 frame_dummy
    35: 0000000000003d80     0 OBJECT  LOCAL  DEFAULT   21 __frame_dummy_init_array_
    36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS myreadelf.c
    37: 000000000000205c     5 OBJECT  LOCAL  DEFAULT   18 __PRETTY_FUNCTION__.4084
    38: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    39: 00000000000021d4     0 OBJECT  LOCAL  DEFAULT   20 __FRAME_END__
    40: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
    41: 0000000000003d88     0 NOTYPE  LOCAL  DEFAULT   21 __init_array_end
    42: 0000000000003d90     0 OBJECT  LOCAL  DEFAULT   23 _DYNAMIC
    43: 0000000000003d80     0 NOTYPE  LOCAL  DEFAULT   21 __init_array_start
    44: 0000000000002064     0 NOTYPE  LOCAL  DEFAULT   19 __GNU_EH_FRAME_HDR
    45: 0000000000003f80     0 OBJECT  LOCAL  DEFAULT   24 _GLOBAL_OFFSET_TABLE_
    46: 0000000000001000     0 FUNC    LOCAL  DEFAULT   12 _init
    47: 0000000000001580     5 FUNC    GLOBAL DEFAULT   16 __libc_csu_fini
    48: 0000000000001229   530 FUNC    GLOBAL DEFAULT   16 dumpSymbols
    49: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    50: 0000000000004000     0 NOTYPE  WEAK   DEFAULT   25 data_start
    51: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   25 _edata
    52: 0000000000001588     0 FUNC    GLOBAL HIDDEN    17 _fini
    53: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mmap@@GLIBC_2.2.5
    54: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    55: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __assert_fail@@GLIBC_2.2.
    56: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND close@@GLIBC_2.2.5
    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    58: 0000000000004000     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
    59: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcmp@@GLIBC_2.2.5
    60: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fprintf@@GLIBC_2.2.5
    61: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    62: 0000000000004008     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle
    63: 0000000000002000     4 OBJECT  GLOBAL DEFAULT   18 _IO_stdin_used
    64: 0000000000001510   101 FUNC    GLOBAL DEFAULT   16 __libc_csu_init
    65: 0000000000004030     0 NOTYPE  GLOBAL DEFAULT   26 _end
    66: 0000000000001140    47 FUNC    GLOBAL DEFAULT   16 _start
    67: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
    68: 000000000000143b   208 FUNC    GLOBAL DEFAULT   16 main
    69: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND open@@GLIBC_2.2.5
    70: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@@GLIBC_2.2.5
    71: 0000000000004010     0 OBJECT  GLOBAL HIDDEN    25 __TMC_END__
    72: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    73: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.2
    74: 0000000000004020     8 OBJECT  GLOBAL DEFAULT   26 stderr@@GLIBC_2.2.5

文章作者: f19
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 f19 !
  目录