使用readelf和objdump解析目标文件 ***

引言

本文是对程序员的自我修养:链接、装载与库中第3章的实践总结(和结构相关的示意图都是用Gliffy Diagrams画的??),通过使用工具readelf、objdump对目标文件进行解析,学习目标文件的结构。

1. 目标文件

1.1 目标文件的定义

编译器编译源代码后生成的文件叫做目标文件。在Linux下,使用gcc -c xxxx.c编译生成.o文件。

gcc -c xxxx.c编译生成目标文件

1.2 编译过程回顾

编译过程

目标文件的文件类型为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 fileCore Dump file

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 ; [email protected]@.....
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文件的结构如下图:

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 -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

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中

程序与目标文件对应关系

  • 代码段

执行命令objdump -s -d SimpleSection.o对代码段(.text)的解析结果如下:

.text

  • 数据段和只读数据段

执行命令objdump -s -d SimpleSection.o对数据段和只读数据段解析结果如下:

.data & .rodata

  • BSS段

执行命令objdump -x -s -d SimpleSection.o打印出目标文件的符号表,通过符号表我们可以知道各个变量的存放位置,只有未初始化的局部静态变量static_var2被放到了.bss段,而global_uninit_var被放入了comment段

.bss

另外,被初始化为0的静态变量也会被放入.bss段,因为未初始变量的值也是0,经过优化后被放入.bss段,这样可以节省磁盘空间,因为.bss不占磁盘空间

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

static int x1 = 0;
static int x2 = 12;

初始值为0的静态变量会被放进.bss

参考

原文地址:https://www.cnblogs.com/jiangzhaowei/p/9095820.html

时间: 2024-10-04 11:56:07

使用readelf和objdump解析目标文件 ***的相关文章

程序员的自我修养:(1)目标文件

程序员的自我修养:(1)目标文件 1.目标文件 1.1 编译与链接 在使用像Visual Studio或Qt Creator等IDE时,通常有一个叫做"构建"的按钮.当编辑完成要运行和测试时点一下它,程序就能跑起来了,所以我们很少关心编译和链接.其实,编译和链接合并在一起就称为 构建(Build).简单的一次按键,实际背后却是异常复杂的过程: 预编译(Preprocessing) 编译(Compilation) 扫描:算法类似有限状态机(FSM),将字符转换成Token. 语法分析:分

在Unix系统上,从源文件、目标文件、可执行文件的编译过程

是由“编译器驱动”(compiler driver)完成的: unix> gcc -o hello hello.c 在这里,gcc的编译器驱动程序读取源文件hello.c, [cpp] view plaincopy #include <stdio.h> int main() { printf("hello, world/n"); return 0; } 并把它翻译成一个可执行目标文件hello,这个过程是分为四个阶段完成的.如下图所示,执行这四个阶段的程序(预处理器.

目标文件ELF

目标文件ELF(Executable Linkable Format) 可执行文件:主流上有两种,PE 和 ELF .目标文件就是.o文件,它和可执行文件有相似几乎相同的文件结构,所以我们可以将他们两个看成是一种类型的文件,其实库文件就是.o文件,这足以说明它和可执行文件的关系. ELF文件类型                                   说明                                                                

浅谈linux 中的目标文件 即.o后缀名的文件

实际上 目标文件从结构上讲,已经非常接近可执行文件,只是没有经过链接的过程,所以其中有些符号或者地址还没有被调整. 实际上上在linux下 都是可以称之为ELF文件. 看一下这个图,a.out 就是我们的hello.c的执行文件.hello.o 就是目标文件 所以实际上他们2 几乎是一样的. 一般目标文件有很多属性,比如符号表啊,字符串之类的,然后目标文件 把这些属性 按照 segment的形式存储,也叫段.就是大家经常遇到的段错误 里的那个段. 一般来说程序代码被编译以后 主要分为程序指令和程

可重定位目标文件

[0]GCC将源代码转化成可执行代码的流程 (1)C预处理器cpp扩展源代码,插入所有用#include命令指定的文件,并扩展声明的宏: (2)编译器ccl产生两个源代码的汇编代码:*.s: (3)汇编器as将汇编代码转化为二进制目标代码 :*.o ; (目标代码是机器代码的一种形式,它包含所有指令的二进制表示,但没有填入地址的全局值) (4)链接器ld将目标代码与实现库函数的代码合并,最终产生可执行代码文件: [1]第一步,源码演说 [2]生成目标文件p107.o 及对其反汇编 2.1) gc

微软BI 之SSIS 系列 - 使用 Script Component Destination 和 ADO.NET 解析不规则文件并插入数据

开篇介绍 这一篇文章是 微软BI 之SSIS 系列 - 带有 Header 和 Trailer 的不规则的平面文件输出处理技巧 的续篇,在上篇文章中介绍到了对于这种不规则文件输出的处理方式.比如下图中的这种不规则文件,第一行,第二行 Header 部分,第三行的内容 Content 部分,最后一行的 Trailer 部分. 在前几个课程 微软BI SSIS 2012 ETL 控件与案例精讲 第43,44,45,46 课中,我分别讲解了如何使用 .Script Component Source 解

解析Excel文件并把数据存入数据库

前段时间做一个小项目,为了同时存储多条数据,其中有一个功能是解析Excel并把其中的数据存入对应数据库中.花了两天时间,不过一天多是因为用了"upload"关键字作为URL从而导致总报同一个错,最后在同学的帮助下顺利解决,下面我把自己用"POI"解析的方法总结出来供大家参考(我用的是SpingMVC和hibernate框架). 1.web.xml中的配置文件 web.xml中的配置文件就按照这种方式写,只需要把"application.xml"换

目标文件里的秘密

目标文件内包含指令代码.数据和链接环节所需要的一些信息,如符号表.调试信息.字符串表等.一般目标文件将这些信息按不同的属性分类,以"段"的形式存储.本文将在Linux环境下,用bjdump.readelf工具查看目标文件存储的具体形式,可得目标文件中由C语言编译后执行语句放在.text段,已经初始化的全局变量和和局部静态变量放在.data段,未初始化的全局变量和局部静态变量放在.bss段,ELF文件中各个段的基本属性放在.symtab段,段表中很重要的还有重定位表以及ELF文件头. 详

Java之Pull方式生成xml文件和解析xml文件

Pull XML解析器早已经被google集成到android sdk当中,它是google官方推荐的解析器. 如果我们要在Java桌面.J2ME等当中使用Pull方式生成xml文件和解析xml文件,需要用到kxml2: KXML解析器是基于普通XML PULL解析器的一个小巧的解析器,官网是http://kxml.org/ 普通XML PULL解析器的官网是http://xmlpull.org/ 实验开始: 在Eclipse中新建一个java项目,其中新建一个libs文件夹,拷贝从网上下载的k