天天看點

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

轉自: https://www.jianshu.com/p/863b279c941e

引言

本文是對程式員的自我修養:連結、裝載與庫中第3章的實踐總結(和結構相關的示意圖都是用Gliffy Diagrams畫的?),通過使用工具readelf、objdump對目标檔案進行解析,學習目标檔案的結構。

1. 目标檔案

1.1 目标檔案的定義

編譯器編譯源代碼後生成的檔案叫做目标檔案。在Linux下,使用gcc -c xxxx.c編譯生成.o檔案。

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

gcc -c xxxx.c編譯生成目标檔案

1.2 編譯過程回顧

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

編譯過程

目标檔案的檔案類型為ELF,在Linux下對應檔案字尾為.o的檔案,Window下對應檔案字尾為.obj的檔案。使用file指令可以檢視到.o和.obj檔案均為ELF類型。

[email protected]:~/work/elf$ file simple.o
simple.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

[email protected]:~/work/elf$ file mp4_player.obj 
mp4_player.obj: ELF 32-bit LSB relocatable, ARM, version 1 (SYSV), not stripped
           

目标檔案隻是ELF檔案的可重定位檔案(Relocatable file),ELF檔案一共有4種類型:Relocatable file、Executable file、Shared object file和Core Dump file

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

ELF檔案類型

  • 示例

在Linux下,使用指令 gcc -c xxxx.c就可以編譯生成.o檔案

[email protected]:~/work/elf$ gcc -c simple.c 
[email protected]:~/work/elf$ ls
simple.c  simple.o
           

在 simple.c中,我們隻加入了下面這一個函數fun,函數内容為空

void fun()
{

}
           

使用UltraEdit将simple.o打開,裡面的内容有機器指令代碼、資料等,我們的程式就是由這些位元組組成的。對于程式員來說,使用進階語言(C/C++,Java等)實作的代碼是最容易閱讀和了解的,但是對于計算機來說,它隻懂得機器語言,它更喜歡二進制,将0轉換為低電平,1轉換成高電平,這樣一個程式就可以跑起來了。

我們可以使用工具readelf 和objdump對目标檔案simple.o進行分析。為了加深對目标檔案的了解,在使用readelf & objdump進行前,需要先要了解ELF檔案的結構。

00000000h: 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 ; �ELF............
00000010h: 01 00 3E 00 01 00 00 00 00 00 00 00 00 00 00 00 ; ..>.............
00000020h: 00 00 00 00 00 00 00 00 08 01 00 00 00 00 00 00 ; ................
00000030h: 00 00 00 00 40 00 00 00 00 00 40 00 0B 00 08 00 ; ....@.....@.....
00000040h: 55 48 89 E5 5D C3 00 00 00 47 43 43 3A 20 28 55 ; UH夊]?..GCC: (U
00000050h: 62 75 6E 74 75 2F 4C 69 6E 61 72 6F 20 34 2E 36 ; buntu/Linaro 4.6
00000060h: 2E 33 2D 31 75 62 75 6E 74 75 35 29 20 34 2E 36 ; .3-1ubuntu5) 4.6
00000070h: 2E 33 00 00 00 00 00 00 14 00 00 00 00 00 00 00 ; .3..............
00000080h: 01 7A 52 00 01 78 10 01 1B 0C 07 08 90 01 00 00 ; .zR..x......?..
00000090h: 1C 00 00 00 1C 00 00 00 00 00 00 00 06 00 00 00 ; ................
000000a0h: 00 41 0E 10 86 02 43 0D 06 41 0C 07 08 00 00 00 ; .A..?C..A......
000000b0h: 00 2E 73 79 6D 74 61 62 00 2E 73 74 72 74 61 62 ; ..symtab..strtab
000000c0h: 00 2E 73 68 73 74 72 74 61 62 00 2E 74 65 78 74 ; ..shstrtab..text
000000d0h: 00 2E 64 61 74 61 00 2E 62 73 73 00 2E 63 6F 6D ; ..data..bss..com
000000e0h: 6D 65 6E 74 00 2E 6E 6F 74 65 2E 47 4E 55 2D 73 ; ment..note.GNU-s
000000f0h: 74 61 63 6B 00 2E 72 65 6C 61 2E 65 68 5F 66 72 ; tack..rela.eh_fr
00000100h: 61 6D 65 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ame.............
00000110h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000120h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000130h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000140h: 00 00 00 00 00 00 00 00 1B 00 00 00 01 00 00 00 ; ................
00000150h: 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000160h: 40 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 ; @...............
00000170h: 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ; ................
00000180h: 00 00 00 00 00 00 00 00 21 00 00 00 01 00 00 00 ; ........!.......
00000190h: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000001a0h: 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; H...............
000001b0h: 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ; ................
000001c0h: 00 00 00 00 00 00 00 00 27 00 00 00 08 00 00 00 ; ........'.......
000001d0h: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000001e0h: 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; H...............
000001f0h: 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ; ................
00000200h: 00 00 00 00 00 00 00 00 2C 00 00 00 01 00 00 00 ; ........,.......
00000210h: 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; 0...............
00000220h: 48 00 00 00 00 00 00 00 2B 00 00 00 00 00 00 00 ; H.......+.......
00000230h: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ; ................
00000240h: 01 00 00 00 00 00 00 00 35 00 00 00 01 00 00 00 ; ........5.......
00000250h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000260h: 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; s...............
00000270h: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ; ................
00000280h: 00 00 00 00 00 00 00 00 4A 00 00 00 01 00 00 00 ; ........J.......
00000290h: 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000002a0h: 78 00 00 00 00 00 00 00 38 00 00 00 00 00 00 00 ; x.......8.......
000002b0h: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ; ................
000002c0h: 00 00 00 00 00 00 00 00 45 00 00 00 04 00 00 00 ; ........E.......
000002d0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000002e0h: B0 04 00 00 00 00 00 00 18 00 00 00 00 00 00 00 ; ?..............
000002f0h: 09 00 00 00 06 00 00 00 08 00 00 00 00 00 00 00 ; ................
00000300h: 18 00 00 00 00 00 00 00 11 00 00 00 03 00 00 00 ; ................
00000310h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000320h: B0 00 00 00 00 00 00 00 54 00 00 00 00 00 00 00 ; ?......T.......
00000330h: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ; ................
00000340h: 00 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 ; ................
00000350h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000360h: C8 03 00 00 00 00 00 00 D8 00 00 00 00 00 00 00 ; ?......?......
00000370h: 0A 00 00 00 08 00 00 00 08 00 00 00 00 00 00 00 ; ................
00000380h: 18 00 00 00 00 00 00 00 09 00 00 00 03 00 00 00 ; ................
00000390h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000003a0h: A0 04 00 00 00 00 00 00 0E 00 00 00 00 00 00 00 ; ?..............
000003b0h: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ; ................
000003c0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000003d0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
000003e0h: 01 00 00 00 04 00 F1 FF 00 00 00 00 00 00 00 00 ; ......?........
000003f0h: 00 00 00 00 00 00 00 00 00 00 00 00 03 00 01 00 ; ................
00000400h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000410h: 00 00 00 00 03 00 02 00 00 00 00 00 00 00 00 00 ; ................
00000420h: 00 00 00 00 00 00 00 00 00 00 00 00 03 00 03 00 ; ................
00000430h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000440h: 00 00 00 00 03 00 05 00 00 00 00 00 00 00 00 00 ; ................
00000450h: 00 00 00 00 00 00 00 00 00 00 00 00 03 00 06 00 ; ................
00000460h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000470h: 00 00 00 00 03 00 04 00 00 00 00 00 00 00 00 00 ; ................
00000480h: 00 00 00 00 00 00 00 00 0A 00 00 00 12 00 01 00 ; ................
00000490h: 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 ; ................
000004a0h: 00 73 69 6D 70 6C 65 2E 63 00 66 75 6E 00 00 00 ; .simple.c.fun...
000004b0h: 20 00 00 00 00 00 00 00 02 00 00 00 02 00 00 00 ;  ...............
000004c0h: 00 00 00 00 00 00 00 00                         ; ........
           

ELF檔案結構

和class檔案類似,ELF檔案存放資料的格式也是固定的,計算機在解析目标檔案時,就是按照它每個字段的資料結構進行逐字解析的。ELF檔案結構資訊定義在/usr/include/elf.h中,整個ELF檔案的結構如下圖:

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

ELF檔案的結構

  • ELF Header

ELF Header是ELF檔案的第一部分,64 bit的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;
           

接下來我們會使用到第一個分析目标檔案的工具readelf,通過man readelf指令,我們可以查到readelf的作用就是用來顯示ELF檔案的資訊

DESCRIPTION
   readelf displays information about one or more ELF format object files. 
           

使用readelf -h simple.o來進行對Header的解析,通過man readelf指令同樣可以查詢到對-h參數的說明,

-h用來顯示ELF header的相關資訊。

OPTIONS
   -h
   --file-header
       Displays the information contained in the ELF header at the start of the file.
           

Header中主要存放的是一些基本資訊,通過Header中的資訊,我們可以确定後面其他字段的大小和起始位址,通常比較關心的部分是:ELF檔案類型、是32bit還是64bit、Header部分大小、Section部分大小和擁有Section的個數等。

結合Elf64_Ehdr來看,對應解析結果如下:

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

readelf -h simple.o

  • Section

完成了對Header的解析,再接着分析Section部分,Section對應結構體如下:

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部分主要存放的是機器指令代碼和資料,執行指令readelf -S -W simple.o對Section部分的解析,解析結果和Elf64_Shdr也是一一對應的。

[email protected]:~/work/elf$ readelf -S -W simple.o
There are 11 section headers, starting at offset 0x108:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        0000000000000000 000040 000006 00  AX  0   0  4
  [ 2] .data             PROGBITS        0000000000000000 000048 000000 00  WA  0   0  4
  [ 3] .bss              NOBITS          0000000000000000 000048 000000 00  WA  0   0  4
  [ 4] .comment          PROGBITS        0000000000000000 000048 00002b 01  MS  0   0  1
  [ 5] .note.GNU-stack   PROGBITS        0000000000000000 000073 000000 00      0   0  1
  [ 6] .eh_frame         PROGBITS        0000000000000000 000078 000038 00   A  0   0  8
  [ 7] .rela.eh_frame    RELA            0000000000000000 0004b0 000018 18      9   6  8
  [ 8] .shstrtab         STRTAB          0000000000000000 0000b0 000054 00      0   0  1
  [ 9] .symtab           SYMTAB          0000000000000000 0003c8 0000d8 18     10   8  8
  [10] .strtab           STRTAB          0000000000000000 0004a0 00000e 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
           

對于這部分内容,通常我們比較的Section是.text(存放代碼)、.data(存放全局靜态變量和局部靜态變量)和.bss(存未初始化的全局變量和局部靜态變量) ,在後面會對這幾個段分别分進行解析。

根據readelf -S -W simple.o的輸出結果,我們可以算出整個simple.o的組成部分和起始位址,使用ls -l 指令檢視simple.o的大小,和simple.o結束位址0x0000048c是吻合的。

[email protected]:~/work/elf$ ls -l simple.o
-rw-rw-r-- 1 ckt ckt 1224 Apr 12 18:42 simple.o
           
使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

simple.o組成

解析目标檔案

分析完ELF檔案結構,接着來解析一個目标檔案。首先,準備好源碼SimpleSection.c,執行指令gcc -c SimpleSection.c生成目标檔案SimpleSection.o。

int printf(const char* format, ...);

int global_init_var = 84;
int global_uninit_var;

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

int main(void)
{
    static int static_var = 85;
    static int static_var2;

    int a = 1;
    int b;
    func1(static_var + static_var2 + a + b);
    return 0;
}
           

在這部分,我們會使用另外一個指令objdump,使用man objdump檢視該指令,objdump是用來顯示目标檔案相關資訊的。

DESCRIPTION
   objdump displays information about one or more object files. 
           
  • 檢視目标檔案的Section

執行指令objdump -h SimpleSection.o對Section部分進行解析,我們可以得到每個段的大小

[email protected]:~/work/elf$ objdump -h SimpleSection.o

SimpleSection.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00000052  0000000000000000  0000000000000000  00000040  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  0000000000000000  0000000000000000  00000094  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  0000000000000000  0000000000000000  0000009c  2**2
                  ALLOC
  3 .rodata       00000004  0000000000000000  0000000000000000  0000009c  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002b  0000000000000000  0000000000000000  000000a0  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  000000cb  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  0000000000000000  0000000000000000  000000d0  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
           

我們的代碼是存放到.text中,已初始化全局變量和局部靜态變量存放在.data中,未初始化全局變量和局部靜态變量存放在.bss中

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

程式與目标檔案對應關系

  • 代碼段

執行指令objdump -s -d SimpleSection.o對代碼段(.text)的解析結果如下:

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

.text

  • 資料段和隻讀資料段

執行指令objdump -s -d SimpleSection.o對資料段和隻讀資料段解析結果如下:

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

.data & .rodata

  • BSS段

執行指令objdump -x -s -d SimpleSection.o列印出目标檔案的符号表,通過符号表我們可以知道各個變量的存放位置,隻有未初始化的局部靜态變量static_var2被放到了.bss段,而global_uninit_var被放入了comment段

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

.bss

另外,被初始化為0的靜态變量也會被放入.bss段,因為未初始變量的值也是0,經過優化後被放入.bss段,這樣可以節省磁盤空間,因為.bss不占磁盤空間

例如,下面的代碼中x1會被放入.bss段,而x2被放入.data段

static int x1 = 0;
static int x2 = 12;
           
使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

初始值為0的靜态變量會被放進.bss

ELF目标檔案的總體結構

使用readelf和objdump解析目标檔案轉自: https://www.jianshu.com/p/863b279c941e引言1. 目标檔案  ELF檔案結構  解析目标檔案

繼續閱讀