Linux内核分析(七):可执行程序的装载

何天杨+ 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

一、可执行文件(程序)

1.1什么是可执行文件

  可执行文件包括被执行的函数目标代码和一些数据,在程序中很多的函数实际上都是我们可以使用的服务例程,他们就是我们通常意义上说的库文件(比如说C库)。实际上,一个库文件他的代码在程序执行时候有两种方式我们可以去利用它。一种方式就是在编译这个程序的时候就把他添加到我们的执行文件中,这样我们在实际使用时就可以直接找到他了,即静态编译。但是这种方式有一个不好的地方就是当我们的程序很庞大,我们需要用到很多的库函数的时候,如果采用运行之前就直接静态的全部加载过来这会导致我们执行的程序占用过高的存储空间,这有的时候是我们不想看到的。所以另一种方式就是当我们实际运行到比如说printf的时候,我们再到库里面去找到它加载它。(共享库)
  在windows环境下,双击一个.exe的文件就可以执行一个程序,这个以.exe结尾的文件就是一个可执行文件。在andriod系统下,一个.apk的文件就是一个可执行文件。而可执行文件在linux环境下并没有什么特殊的后缀标记,只是在生成该文件时,它的属性设置了可执行(就是‘x’),那么他就是属于可执行文件。

1.2可执行程序是怎么来的

C代码—>预处理—>汇编代码—>目标代码—>可执行文件
1.预处理阶段
gcc -E -o XX.cpp XX.c -m32
XX.cpp是预处理文件

2.编译器生成汇编代码阶段
gcc -x cpp-output -S -o hello.s hello.cpp -m32
XX.s是汇编代码

3.汇编器生成目标代码阶段
gcc -x assembler -c hello.s -o hello.o -m32
XX.o是目标代码

4.链接器生成可执行文件阶段
gcc -o hello.static hello.c -m32 -static

5.查看hello和hello.static的区别:
ls -l

6.补充:
hello和hello.o都是ELF文件
.static文件会将所有用到C库文件都放到这一个可执行程序中(所以占用空间比较多)

1.3执行文件的格式

1.3.1常见的目标文件格式:
A.out -> COFF -> PE(windows)/ELF(linux)

2.目标文件也叫ABI:应用程序二进制接口

3.ABI是二进制兼容:目标文件已经适应某种CPU体系结构上的二进制指令
  linux系统中,可执行文件的格式为elf(Executable and Linking Format)格式。

ELF文件有三种类型:
(1)可重定位文件
  也就是通常称的目标文件,后缀为.o。链接器将它作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件。
(2)共享文件
  这些就是所谓的动态库文件,也即 .so 文件。如果拿前面的静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉宝贵的物理内存。如果将静态库换成动态库,那么这些问题都不会出现。
(3)可执行文件
  文件保存着一个用来执行的程序;包含二进制代码和数据,其形式可以被直接拷贝到存储器并执行

1.3.2elf 文件的格式
Linux内核分析(七):可执行程序的装载_第1张图片
其中
(1) Linking View: 组成不同的可重定位文件,以参与可执行文件或者可被共享的对象文件的链接构建;
(2) Execution View: 组成可执行文件或者可被共享的对象文件,以在运行时内存中进程映像的构建。
我们从Execution View进行分析:
(1) ELF头部结构Elf32_Ehdr

 #define EI_NIDENT       16

  typedef struct {
      unsigned char       e_ident[EI_NIDENT]; //魔数和相关信息
      Elf32_Half          e_type;   /* 目标文件类型 */
      Elf32_Half          e_machine;    /* 硬件体系 */
      Elf32_Word          e_version;     /* 目标文件版本 */
      Elf32_Addr          e_entry;    /* 程序进入点 */
      Elf32_Off           e_phoff;    /* 程序头部偏移量 */
      Elf32_Off           e_shoff;    /* 节头部偏移量 */
      Elf32_Word          e_flags;     /* 处理器特定标志 */
      Elf32_Half          e_ehsize;   /* ELF头部长度 */
      Elf32_Half          e_phentsize;   //程序头部中一个条目的长度
      Elf32_Half          e_phnum;   /*程序头部条目个数*/
      Elf32_Half          e_shentsize;  //节头部中一个条目的长度
      Elf32_Half          e_shnum;      /* 节头部条目个数 */
      Elf32_Half          e_shstrndx;  /* 节头部字符表索引 */
  } Elf32_Ehdr;

e_ident[0]-e_ident[3]包含了文件的魔数 依次是 0x7f, 'E', 'L', 'F'
e_ident[4] 表示硬件的位数 1表示32位, 2表示64位
e_ident[5] 表示数据编码方式

你可能感兴趣的:(孟Linux内核分析,linux,kernel)