Linux C目标文件

版权声明:原创不易,转载请注明出处:http://blog.csdn.net/wqx521 https://blog.csdn.net/a1314521531/article/details/50809440

LinuxC目标文件


宗旨:技术的学习是有限的,分享的精神是无限的。


一、目标文件格式(ELF格式)

编译器编译源代码后生成的文件叫做目标文件。目标文件是已经编译后的可执行文件,只是还没有经过链接的过程。

PC平台流行的可执行文件格式:windows下的PE和Linux下的ELF。

动态链接库和静态链接库也是按照可执行文件存储的。

1ELF文件归于4类:

ELF文讲类型

说明

举例

可重定位文件

目标文件.o

Linux的.o, windows下的.obj

可执行文件

直接可执行的文件

/bin/bash   windows的.exe

共享目标文件

 

.so   DLL

核心转储文件

进程意外终止

core dump

Linux下的file命令查看相应的文件格式:



2、目标文件

编译后的机器指令代码、数据、符号表、调试信息、字符串等。

一般目标文件将这些文件信息按不同的属性,以“节”的形式存储。

机器指令放在代码段.text,已初始化全局变量和静态变量放在数据段里.data,未初始化全局变量和静态变量放在数据段里.bss。.bss只是为变量预留的位置而已,并没有内容,不占据空间。

  

二、剖析目标文件section.o

// section.c:
#include<stdio.h>

int init_var = 84;
int uninit_var;

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

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

    fun(init_var + uninit_var + a + b);

    return 0;
}

gcc  -c  section.c生成section.o——只编译不链接 

objdump  -h  section.o// ELF文件的各个段的基本信息打印出来。


除了最基本的代码段、数据段、BSS段,还有三个段:只读数据段(.rodata)、注释信息段(.comment)和堆栈提示段(.note.GNU-stack)。

段的属性:最容易理解的就是段的长度Size和段所在的位置File off(偏移量)。每个段第二行的“CONTENTS”表示该段在文件中存在——BSS段没有“CONTENTS “,实际上在ELF中不存在;”note.GNU-stack“有“CONTENTS”但大小为0,奇怪。

 

size命令查看ELF文件中的代码段、数据段和BSS段长度。

root@colinux:~/mystudy# size section.o

   text    data    bss     dec     hex filename

   88      8       4     100     64 section.o

 

1、代码段

         objdump的“-s”十六进制方式打印,“-d”反汇编。提取出代码段的内容:

 

“Contents of section.text”就是.text的数据以十六进制方式打印出来的内容,0x58字节,与size命令的长度符合。对照反汇编结果,.text包含两个函数,fun()和main()。.text的第一个字节就是”0x55”就是fun()函数的第一条“push   %ebp”指令,而最后一个字节0xc3正是main()函数的最后一条指令“ret”。


2、数据段和只读数据段

         .data段保存的是初始化的全局变量和静态变量,section.c中有这样两个变量init_var和static_var,都是int型,刚好8字节。所以.data的大小是8字节。

Contents of section .data:

 0000 54000000 55000000                    T...U...       

Contents of section .rodata:

 0000 20256420 0a00                         %d ..  

.data前四个字节,0x54、0x00、0x00、0x00 —— 0x54 = 84;——大端机

 

3BSS

         .bss段保存的是未化的全局变量和静态变量,section.c中有这样两个变量uninit_var和static_var2。但是通过size命令看到.bss只有4字节。通过符号表(后面说)看到,只有static_var2被放入了.bss段,uninit_var没有。与不同的语言和不同的编译器有关。

 

4、其他段        

常用段名

说明

.rodata

只读数据,如字符串常量,const只读变量

.comment

编译器版本信息,

.debug

调试信息

.dynamic

动态链接信息

.hash

符号哈希表

.line

行号表

.note

额外的编译器信息:公司名,发布版本号等

.strtab

字符串表

.symtab

符号表

.shstrtab

段名表

.plt   .got

动态链接的跳转表和全局入口表

.init  .fini

程序初始化与终结代码段

 

三、ELF文件结构

         提取重要的结构:ELFHeaderELF文件头)、.text.data.bss、其他段、段表、字符串表、符号表等。

ELF文件头——描述了整个文件的文件属性:是否可执行、是静态还是动态连接及入口地址、目标硬件、目标操作系统等信息。

段表——所有段的信息:段名、段的长度、在文件中的偏移、读写权限及段的其他属性。

 

1、文件头(readelf命令) 


ELF文件头定义:ELF魔数、文件机器字节长度、文件存储方式、版本、运行平台、ABI版本、ELF重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口地址和长度、段表的位置和长度及段的数量等。

ELF文件头结构及相关常数被定义在”/usr/include/elf.h”,32位“ELF32_Ehdr

#define EI_NIDENT(16)

typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf32_Half   e_type;                 /* Objectfile type */
  Elf32_Half   e_machine;              /*Architecture */
  Elf32_Word   e_version;              /* Object file version */
  Elf32_Addr   e_entry;                /* Entrypoint virtual address */
  Elf32_Off    e_phoff;                /* Programheader table file offset */
  Elf32_Off    e_shoff;                /* Sectionheader table file offset */
  Elf32_Word   e_flags;                /*Processor-specific flags */
  Elf32_Half   e_ehsize;               /* ELFheader size in bytes */
  Elf32_Half   e_phentsize;            /* Programheader table entry size */
  Elf32_Half   e_phnum;                /* Program header table entrycount */
  Elf32_Half   e_shentsize;            /* Sectionheader table entry size */
  Elf32_Half   e_shnum;                /* Sectionheader table entry count */
  Elf32_Half   e_shstrndx;             /* Sectionheader string table index */
} Elf32_Ehdr;

结构与readelf输出的ELF文件头信息相比:只有e_ident对应了readelf输出中的“Class  Data  Version OS/ABI  ABI Version”5个参数,剩下的参数一一对应。

ELF魔数:Magic:  7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00  

16字节对应了Elf32_Ehdr的e_ident这个16字节成员。这16字节被ELF标准规定来标识ELF文件的平台属性:如字长、字节序、版本等。16字节含义:

前4字节是所有ELF文件都必须相同的标识码:0x7F、0X45、0X4C、0X46(这四个字节就是ELF文件的魔数)。几乎所有的可执行文件的开始的几个字节都是魔数,如a.out最开始两个字节是0x01、0x07;PE最开始两个字节是0x4d、0x5a。这个魔数用来确认文件类型。第5个字节用来标识ELF文件类的,0x01表示是32位的,0x02表示64位的。第6字节是字节序,规定ELF文件是大端的还是小端的。第7字节规定ELF文件的主版本号,一般是1.后面的九个字节ELF标准没有定义,一般写0。

e_type文件类型:ET_REL—— 1 ——可重定位文件,一般是.o文件;ET_EXEC—— 2 —— 可执行文件; ET_DYN —— 2 ——共享目标文件,一般是.so文件。

e_machine机器类型:ELF文件的平台属性,EM_386 —— 3 —— x86

 

2、段表

ELF的段结构就是由段表决定的,编译器、连接器和装载器都是靠段表来定位和访问各个段的属性的。段表在ELF文件中的位置由ELF文件头Elf32_Ehdr结构中的” e_shoff” 成员决定。section.o中,段表位于偏移0x104(260字节)处。

前面用”objdump -h”查看ELF文件中的段,此命令只是把ELF文件中的关键段显示出来了,省略了其他辅助性的段:符号表、字符串表、重定位表等。

readelf -S命令


段表是以“Elf32_Shdr”结构体为元素的数组,数组元素的个数等于段的个数,每个“Elf32_Shdr”结构体对应一个段。section.o:11个元素的数组。/usr/include/elf.h:

typedef struct
{
  Elf32_Word    sh_name;                /* Section name (string tblindex) */
  Elf32_Word    sh_type;                /* Section type */
  Elf32_Word    sh_flags;               /* Section flags */
  Elf32_Addr     sh_addr;                /* Section virtual addr atexecution */
  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 sectioninformation */
  Elf32_Word    sh_addralign;           /* Section alignment */
  Elf32_Word    sh_entsize;             /* Entry size if section holdstable */
} Elf32_Shdr;

总结section.o段表的位置

 

起始地址

大小

ELF Header  e_shoff = 0x104

0

0x34

.text

0x34

0x52

.data

0x88

0x08

.rodata

0x90

0x06

.comment

0x96

0x1d

.shstrtab

0xB3

0x51

Section Table

0x104

0x1b8

.symtab

0x2bc

0xf0

.rel.text

0x3fc

0x28

长度为0x424 = 1060,这个长度正好是section.o文件的大小。

段的类型(sh_type):段的名字只有在编译和链接的过程中有意义。SHT_NULL – 0 – 无效段, SHT_PROGBITS– 1 – 程序段, SHT_SYMTAB – 2 – 表示该段的内容为符号表, SHT_STRTAB – 3 – 字符串表, SHT_RELA –4 – 重定位表, SHT_HASH – 5 – 符号表的哈希表,SHT_DYNAMIC – 6 – 动态链接信息, SHT_NOTE – 7 – 提示性信息, SHT_NOBITS– 8 –该段在文件中没内容, SHT_REL – 9 –该段包含了重定位信息,SHT_SHLIB – 10 – 保留,SHT_DNYSYM – 11 – 动态链接的符号表。

段的标志位(sh_flag):表示该段在进程虚拟地址空间中的属性,可写可执行等。SHF_WRITE – 1 – 该段在进程空间中可写; SHF_ALLOC– 2 – 在进程空间中要分配空间; SHF_EXECINSTR– 4 –该段在进程空间中可以被执行,一般指代码段。

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

sh_type

sh_link

sh_info

SHT_DYNAMIC

字符串表在段表的下标

0

SHT_HASH

符号表在段表中的下标

0

SHT_REL

相应符号表在段表中的下标

该重定位表所作用的段在段表中的下标

SHT_RELA

SHT_SYMTAB

操作系统相关

操作系统相关

SHTDYNSYM

other

SHN_UNDEF

0

 

3、重定位表

         section.o中有一个“rel.text”的段,类型是“SHT_REL”——重定位表。

代码段和数据段中那些对绝对地址的引用的位置——相应的重定位表。section.o中的“rel.text”就是对“.text”段的重定位表——printf函数的调用;而“.data”段没有对绝对地址的引用,只包含了几个常量,故没有“.rel.data”。

 

4、字符串表——段名,变量名等

 

四、链接的接口——符号

         可以使用很多工具查看ELF文件的符号表,readelfobjdumpnm等;

1ELF符号表结构

         ELF符号表是文件中的一个段“.symtab”

/* Symbol table entry.  */
typedef struct
{
  Elf32_Word    st_name;                /* Symbol name (string tblindex) */
  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;


展开阅读全文

没有更多推荐了,返回首页