目标文件和ELF格式详解

目标文件和ELF格式详解

Linux生成的目标文件是标准的ELF文件格式,使用objdump工具和readelf工具可以查看分析elf文件的格式

gcc -c 选项只编译不连接生成目标文件

# c语言源代码
[root@localhost simple-secion-linux-elf]# cat SimpleSection.c
#include <stdio.h>

int global_init_var = 84;
int global_uninit_var;

void func1(int i) {
    printf("%d\n", i);
}

int main(int argc, char const *argv[])
{
    static int static_var = 82;
    static int static_var2;

    int a = 1;
    int b;

    func1(static_var + static_var2 + a + b);


    return 0;
}

[root@localhost simple-secion-linux-elf]# gcc -c SimpleSection.c 
[root@localhost simple-secion-linux-elf]# ll
总用量 40
-rwxr-xr-x. 1 root root  6740 2月  23 18:20 a.out
-rw-r--r--. 1 root root   286 2月  23 18:17 SimpleSection.c
-rw-r--r--. 1 root root 16968 2月  23 18:17 SimpleSection.i
-rw-r--r--. 1 root root  1904 2月  23 18:20 SimpleSection.o
-rwxr-xr-x. 1 root root  1343 2月  23 18:19 SimpleSection.s
[root@localhost simple-secion-linux-elf]# ./a.out 

初探目标文件

objdump工具可以查看ELF文件的段的信息,包含

  • 查看目标文件各个段的信息(-h -x)
  • 以十六进制的方式查看段的内容(-s)
  • 查看反汇编之后的代码(-d)

ELF文件各个段的基本信息

objdump -h 选项查看ELF文件各个段的基本信息

[root@localhost simple-secion-linux-elf]# objdump -h SimpleSection.o

SimpleSection.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         0000005d  0000000000000000  0000000000000000  00000040  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  0000000000000000  0000000000000000  000000a0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  0000000000000000  0000000000000000  000000a8  2**2
                  ALLOC
  3 .rodata       00000004  0000000000000000  0000000000000000  000000a8  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002d  0000000000000000  0000000000000000  000000ac  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  000000d9  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  0000000000000000  0000000000000000  000000e0  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

size工具可以查看ELF文件的各个段的大小,.data段和.bss段和objdump工具查看的一致,而.text段不一致,这个地方暂时还不懂为什么??

[root@localhost simple-secion-linux-elf]# size SimpleSection.o
   text    data     bss     dec     hex filename
    185       8       4     197      c5 SimpleSection.o

目标文件代码段

objdump -s 选项把ELF文件的所有信息以十六进制的方式打印出来;-d选项把代码段反汇编,可以看到代码段的地址从0-5c一共为5d个字节,和使用objdump -h查看的一致

[root@localhost simple-secion-linux-elf]# objdump -s -d SimpleSection.o

SimpleSection.o:     file format elf64-x86-64

Contents of section .text:
 0000 554889e5 4883ec10 897dfcb8 00000000  UH..H....}......
 0010 8b55fc89 d64889c7 b8000000 00e80000  .U...H..........
 0020 0000c9c3 554889e5 4883ec20 897dec48  ....UH..H.. .}.H
 0030 8975e0c7 45f80100 00008b15 00000000  .u..E...........
 0040 8b050000 00008d04 020345f8 0345fc89  ..........E..E..
 0050 c7e80000 0000b800 000000c9 c3        .............   
Contents of section .data:
 0000 54000000 52000000                    T...R...        
Contents of section .rodata:
 0000 25640a00                             %d..            
Contents of section .comment:
 0000 00474343 3a202847 4e552920 342e342e  .GCC: (GNU) 4.4.
 0010 37203230 31323033 31332028 52656420  7 20120313 (Red 
 0020 48617420 342e342e 372d3429 00        Hat 4.4.7-4).   
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 1c000000 1c000000  ................
 0020 00000000 24000000 00410e10 8602430d  ....$....A....C.
 0030 065f0c07 08000000 1c000000 3c000000  ._..........<...
 0040 00000000 39000000 00410e10 8602430d  ....9....A....C.
 0050 06740c07 08000000                    .t......        

Disassembly of section .text:

0000000000000000 <func1>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   89 7d fc                mov    %edi,-0x4(%rbp)
   b:   b8 00 00 00 00          mov    $0x0,%eax
  10:   8b 55 fc                mov    -0x4(%rbp),%edx
  13:   89 d6                   mov    %edx,%esi
  15:   48 89 c7                mov    %rax,%rdi
  18:   b8 00 00 00 00          mov    $0x0,%eax
  1d:   e8 00 00 00 00          callq  22 <func1+0x22>
  22:   c9                      leaveq 
  23:   c3                      retq   

0000000000000024 <main>:
  24:   55                      push   %rbp
  25:   48 89 e5                mov    %rsp,%rbp
  28:   48 83 ec 20             sub    $0x20,%rsp
  2c:   89 7d ec                mov    %edi,-0x14(%rbp)
  2f:   48 89 75 e0             mov    %rsi,-0x20(%rbp)
  33:   c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%rbp)
  3a:   8b 15 00 00 00 00       mov    0x0(%rip),%edx        # 40 <main+0x1c>
  40:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 46 <main+0x22>
  46:   8d 04 02                lea    (%rdx,%rax,1),%eax
  49:   03 45 f8                add    -0x8(%rbp),%eax
  4c:   03 45 fc                add    -0x4(%rbp),%eax
  4f:   89 c7                   mov    %eax,%edi
  51:   e8 00 00 00 00          callq  56 <main+0x32>
  56:   b8 00 00 00 00          mov    $0x0,%eax
  5b:   c9                      leaveq 
  5c:   c3                      retq   

目标文件数据段

数据段包含了读写数据段.data和只读数据段.rodata,objdump工具提供的-x选项可以查看段的内容

[root@localhost simple-secion-linux-elf]# objdump -s -x SimpleSection.o
...
Contents of section .data:
 0000 54000000 52000000                    T...R...        
Contents of section .rodata:
 0000 25640a00                             %d..            
...
  • .data段保存已初始化的全局变量和局部静态变量
    54000000 对应的是int global_init_var = 84;
    52000000 对应的是static int static_var = 82;;

  • .rodata段保存的是只读数据,一般是程序中的只读变量(const)和字符串常亮
    25640a00 %d.. 保存的是printf函数用到的字符串常量%d\n(25640a)

目标文件BSS段

BSS段保存的是未初始化的全局变量和局部静态变量

[root@localhost simple-secion-linux-elf]# objdump -h SimpleSection.o
...
  2 .bss          00000004  0000000000000000  0000000000000000  000000a8  2**2
                  ALLOC
...

ELF文件结构解析

ELF文件结构

主要包含:

  • ELF文件头(ELF Header)包含了ELF文件版本,目标机器型号、程序入口地址
  • ELF文件各个段(代码段,数据段、BSS段)
  • 段表(Section Header Table),ELF文件中所有段的信息,比如段名、段长度、段在文件中的偏移、段的读写权限和其他属性
  • 其他的辅助结构,比如字符串表、符号表等

readelf工具提供的以下选项可以查看elf文件的信息:

Usage: readelf <option(s)> elf-file(s)
 Display information about the contents of ELF format files
 Options are:
  -a --all               Equivalent to: -h -l -S -s -r -d -V -A -I
  -h --file-header       Display the ELF file header
  -l --program-headers   Display the program headers
     --segments          An alias for --program-headers
  -S --section-headers   Display the sections' header
     --sections          An alias for --section-headers
  -g --section-groups    Display the section groups
  -t --section-details   Display the section details
  -e --headers           Equivalent to: -h -l -S
  -s --syms              Display the symbol table
      --symbols          An alias for --syms
  -n --notes             Display the core notes (if present)
  -r --relocs            Display the relocations (if present)
  -u --unwind            Display the unwind info (if present)
  -d --dynamic           Display the dynamic section (if present)
  -V --version-info      Display the version sections (if present)
  -A --arch-specific     Display architecture specific information (if any).
  -c --archive-index     Display the symbol/file index in an archive
  -D --use-dynamic       Use the dynamic section info when displaying symbols
  -x --hex-dump=<number|name>
                         Dump the contents of section <number|name> as bytes
  -p --string-dump=<number|name>
                         Dump the contents of section <number|name> as strings
  -R --relocated-dump=<number|name>
                         Dump the contents of section <number|name> as relocated bytes
  -w[lLiaprmfFsoR] or
  --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]
                         Display the contents of DWARF2 debug sections
  -I --histogram         Display histogram of bucket list lengths
  -W --wide              Allow output width to exceed 80 characters
  @<file>                Read options from <file>
  -H --help              Display this information
  -v --version           Display the version number of readelf

ELF 文件头

使用readelf工具的-h选项查看ELF文件头部信息

[root@localhost simple-secion-linux-elf]#  readelf -h SimpleSection.o
ELF Header:
  Magic(ELF魔数):   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class(文件机器字节长度):                             ELF64
  Data(数据存储方式):                              2's complement, little endian
  Version(版本):                           1 (current)
  OS/ABI(运行平台):                            UNIX - System V
  ABI Version(ABI版本):                       0
  Type(ELF重定位类型):                              REL (Relocatable file)
  Machine(硬件平台):                           Advanced Micro Devices X86-64
  Version(硬件平台版本):                           0x1
  Entry point address(入口地址):               0x0
  Start of program headers(程序头入口):          0 (bytes into file)
  Start of section headers(段表入口):          416 (bytes into file)
  Flags:                             0x0
  Size of this header(ELF头自身的大小):               64 (bytes)
  Size of program headers(程序头长度):           0 (bytes)
  Number of program headers:         0
  Size of section headers(段表的长度):           64 (bytes)
  Number of section headers(段的数量):         13
  Section header string table index(段表字符串表在段中的位置): 10

ELF 文件头结构对应的结构体,位于/usr/include/elf.h里,包含了32位和64位的版本,下面是64位版本的头信息结构体

/* The ELF file header.  This appears at the start of every ELF file.  */

#define EI_NIDENT (16)

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;

类型描述以及长度信息如下所示:

类型描述以及长度信息

对应关系如下表所示:
ELF文件头结构成员含义

ELF魔数
标识ELF文件的平台属性,比如ELF字长、字节序、ELF文件版本,如下图所示。

ELF魔数

ELF 文件类型

ELF 文件类型

ELF机器类型

ELF机器类型

ELF 段表

ELF文件中有很多段,段表(Section Header Table)就是保存这些段的基本信息的结构,包括了段名、段长度、段在文件中的偏移位置、读写权限和其他段属性。
objdump工具可以查看ELF文件基本的段结构

[root@localhost simple-secion-linux-elf]# objdump -h SimpleSection.o

SimpleSection.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         0000005d  0000000000000000  0000000000000000  00000040  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  0000000000000000  0000000000000000  000000a0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  0000000000000000  0000000000000000  000000a8  2**2
                  ALLOC
  3 .rodata       00000004  0000000000000000  0000000000000000  000000a8  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002d  0000000000000000  0000000000000000  000000ac  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  000000d9  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  0000000000000000  0000000000000000  000000e0  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

使用readelf可以看到ELF文件全部的段结构

[root@localhost simple-secion-linux-elf]# readelf -S SimpleSection.o
There are 13 section headers, starting at offset 0x1a0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       000000000000005d  0000000000000000  AX       0     0     4
  [ 2] .rela.text        RELA             0000000000000000  000006c8
       0000000000000078  0000000000000018          11     1     8
  [ 3] .data             PROGBITS         0000000000000000  000000a0
       0000000000000008  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  000000a8
       0000000000000004  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  000000a8
       0000000000000004  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  000000ac
       000000000000002d  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000d9
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000e0
       0000000000000058  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  00000740
       0000000000000030  0000000000000018          11     8     8
  [10] .shstrtab         STRTAB           0000000000000000  00000138
       0000000000000061  0000000000000000           0     0     1
  [11] .symtab           SYMTAB           0000000000000000  000004e0
       0000000000000180  0000000000000018          12    11     8
  [12] .strtab           STRTAB           0000000000000000  00000660
       0000000000000066  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

可以看到一共有13个段,每个段对应一个Section Header数据结构,下面是64位的Section Header数据结构。
Elf64_Shdr的定义位于“/usr/include/elf.h”文件

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;

段表成员含义描述:

段表成员含义描述
Section Table及所有段的长度

段类型(sh_type)

段类型(sh_type)

段类型(sh_type)

段的标志位(sh_flag)

段的标志位(sh_flag)

** 段的链接信息(sh_link、sh_info)**

段的链接信息(sh_link、sh_info)

``

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容