前言:

我们都知道我们所写的程序是被编译为一条条的CPU指令去执行的,但是在linux系统下能够运行的程序在windows环境下却运行不起来,但是我们使用的CPU明明是一样的,这又是为什么呢?

一、程序的执行:编译、链接和装载

程序示例1:

// add_lib.c
int add(int a, int b)
{
return a+b;
}
// link_example.c

#include <stdio.h>
int main()
{
int a = 10;
int b = 5;
int c = add(a, b);
printf("c = %d\n", c);
}

单独编译:

$ gcc -g -c add_lib.c link_example.c
$ objdump -d -M intel -S add_lib.o
$ objdump -d -M intel -S link_example.o

编译结果:

add_lib.o:     file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <add>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 89 7d fc mov DWORD PTR [rbp-0x4],edi
7: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
a: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
d: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
10: 01 d0 add eax,edx
12: 5d pop rbp
13: c3 ret
link_example.o:     file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 10 sub rsp,0x10
8: c7 45 fc 0a 00 00 00 mov DWORD PTR [rbp-0x4],0xa
f: c7 45 f8 05 00 00 00 mov DWORD PTR [rbp-0x8],0x5
16: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8]
19: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
1c: 89 d6 mov esi,edx
1e: 89 c7 mov edi,eax
20: b8 00 00 00 00 mov eax,0x0
25: e8 00 00 00 00 call 2a <main+0x2a>
2a: 89 45 f4 mov DWORD PTR [rbp-0xc],eax
2d: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]
30: 89 c6 mov esi,eax
32: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # 39 <main+0x39>
39: b8 00 00 00 00 mov eax,0x0
3e: e8 00 00 00 00 call 43 <main+0x43>
43: b8 00 00 00 00 mov eax,0x0
48: c9 leave
49: c3 ret

分析:实际上以上编译结果是无法运行的,下面来分析一下

    1. 编译结果的两个文件左侧指令地址行都从0开始显然是重复的,这必然导致call指令无法寻址,它也不知道应该跳转去哪个文件。
    2. 实际上以上编译结果的两个文件并不是可执行文件,只是目标文件,只有通过链接器将多个目标文件以及调用库链接起来,才能成为一个可执行文件。

gcc -o 命令进行链接:

$ gcc -o link-example add_lib.o link_example.o
$ ./link_example
c = 15

链接结果:

link_example:     file format elf64-x86-64
Disassembly of section .init:
...
Disassembly of section .plt:
...
Disassembly of section .plt.got:
...
Disassembly of section .text:
... 6b0: 55 push rbp
6b1: 48 89 e5 mov rbp,rsp
6b4: 89 7d fc mov DWORD PTR [rbp-0x4],edi
6b7: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
6ba: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
6bd: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
6c0: 01 d0 add eax,edx
6c2: 5d pop rbp
6c3: c3 ret
00000000000006c4 <main>:
6c4: 55 push rbp
6c5: 48 89 e5 mov rbp,rsp
6c8: 48 83 ec 10 sub rsp,0x10
6cc: c7 45 fc 0a 00 00 00 mov DWORD PTR [rbp-0x4],0xa
6d3: c7 45 f8 05 00 00 00 mov DWORD PTR [rbp-0x8],0x5
6da: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8]
6dd: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
6e0: 89 d6 mov esi,edx
6e2: 89 c7 mov edi,eax
6e4: b8 00 00 00 00 mov eax,0x0
6e9: e8 c2 ff ff ff call 6b0 <add>
6ee: 89 45 f4 mov DWORD PTR [rbp-0xc],eax
6f1: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]
6f4: 89 c6 mov esi,eax
6f6: 48 8d 3d 97 00 00 00 lea rdi,[rip+0x97] # 794 <_IO_stdin_used+0x4>
6fd: b8 00 00 00 00 mov eax,0x0
702: e8 59 fe ff ff call 560 <printf@plt>
707: b8 00 00 00 00 mov eax,0x0
70c: c9 leave
70d: c3 ret
70e: 66 90 xchg ax,ax
...
Disassembly of section .fini:
...

分析:

  1. 可以发现通过链接生成的可执行文件和之前生成的目标文件在格式上并无区别,这中文件格式称为 ELF(Execuatable and Linkable File Format)
  2. 文件中包含函数(如main和add),此外还包含一些定义的全局可访问的变量名称,这些变量被存储在被称为符号表的位置,符号表用于关联名称和地址;
  3. 在该文件中main函数调用add函数也不再是跳转下一指令地址,而是直接跳转到add函数入口。

由c语言生成汇编码再到机器码执行的过程分两部分:

  1. 编译、汇编、链接生成可执行文件
  2. 通过装载器将可执行文件装载到内存,而CPU是从内存中读取指令并运行的。

二、ELF文件结构

  1. ELF文件分为多个部分,称为多个section,并且有一个基本的文件头用来保存文件的基本属性,包括是否为可执行文件、操作系统、CPU型号等
  2. .text Section 保存代码段和指令段
  3. .data Section 保存程序里设置好的初始化信息
  4. .real.text Section 保存程序里面我们暂时位置的跳转地址,比如链接之前的目标文件中add函数的跳转地址
  5. .symtab Section 符号表,保存当前文件里面我们未知的函数名称和地址的对应关系

三、ELF文件链接过程

  

链接时,扫描所有目标文件,收集所有符号表,将所有符号表合并成一个符号表;

根据重定向表所有不确定的地址进行修正;

将代码段进行合并,生成一个可执行文件;

总结:

讲了这么多,为什么linux环境下的可执行文件和windows环境下的不同呢?这是因为两种环境下运行的可执行文件的文件格式是不一样的,而想要在linux环境运行windows环境的可执行文件,需要通过一种能够兼容ELF格式文件的装载器。

扩展:这里可以联系java的类装载机制,class被加载到方法区才能运行,其实jvm内的方法区就是一个大的.text Section,全局的。

(九)ELF和动态链接的更多相关文章

  1. 实例分析ELF文件动态链接

    参考文献: <ELF V1.2> <程序员的自我修养---链接.装载与库>第6章 可执行文件的装载与进程 第7章 动态链接 <Linux GOT与PLT> 开发平台 ...

  2. ELF 文件 动态链接 - 地址无关代码(GOT)

    Linux 系统中,ELF动态链接文件被称为 动态共享对象(DSO,Dynamic Shared Object),简称共享对象 文件拓展名为".so" 动态链接下 一个程序可以被分 ...

  3. ELF动态链接

    为什么要使用动态链接? 在现代的linux系统中,假设一个普通的程序会使用到c语言静态库至少1MB以上,那么,如果我们的机器运行100个这样的程序,就用浪费近100MB的内存:如果磁盘有2000个这样 ...

  4. 程序员的自我修养九Windows下的动态链接

    9.1 DLL简介 DLL即动态链接库的缩写,它相对于Linux下的共享对象. Windows下的DLL文件和EXE文件实际上是一个概念,它们都是有PE格式的二进制文件. 微软希望通过DLL机制加强软 ...

  5. ELF 动态链接 - so 的 .dynamic 段

    动态链接文件中最重要的段就是 .dynamic段 这个段里保存了动态链接器需要的最基本的信息 比如:1.  依赖于哪些共享对象, d_tag = DT_NEED,  d_ptr 表示共享对象文件名 2 ...

  6. ELF文件加载与动态链接(一)

    关于ELF文件的详细介绍,推荐阅读: ELF文件格式分析 —— 滕启明.ELF文件由ELF头部.程序头部表.节区头部表以及节区4部分组成. 通过objdump工具和readelf工具,可以观察ELF文 ...

  7. ELF文件加载与动态链接(二)

    GOT应该保存的是puts函数的绝对虚地址,这里为什么保存的却是puts@plt的第二条指令呢? 原来“解释器”将动态库载入内存后,并没有直接将函数地址更新到GOT表中,而是在函数第一次被调用时,才会 ...

  8. ELF 动态链接 - so 的 重定位表

    动态链接下,无论时可执行文件还是共享对象,一旦对其他共享对象有依赖,也就是所有导入的符号时,那么代码或数据中就会有对于导入符号的引用.而在编译时期这些导入符号的确切地址时未知的.只有在运行期才能确定真 ...

  9. ELF 动态链接 so的动态符号表(.dynsym)

    静态链接中有一个专门的段叫符号表 -- ".symtab"(Symbol Table), 里面保存了所有关于该目标文件的符号的定义和引用. 动态链接中同样有一个段叫 动态符号表 - ...

随机推荐

  1. python开发基础(二)运算符以及数据类型之dict(字典)

    # encoding: utf-8 # module builtins # from (built-in) # by generator 1.147 """ Built- ...

  2. 知识管理——得到CEO脱不花女士的一次分享

    知识管理--得到CEO脱不花女士的一次分享 近日,公司举办了一场"CKO首席知识官"研讨会,邀请到了得到APP的CEO脱不花女士做了一场精彩的分享,让我深受启发. 分享内容围绕3个 ...

  3. input 与 button 的问题 (空隙/不等高/对不齐)及 解决办法

    1. input 与 button 为什么有空隙? - 要明白为什么,需要了解一下几点基础知识(耐心看完,你会发现竟如此简单)     1. input 与 button 都属于行级块元素,都具有文本 ...

  4. day89:luffy:使用Celery完成我的订单超时取消&Polyv视频加密播放

    目录 1.我的订单超时取消 2.PoliV视频播放 1.我的订单超时取消 使用Celery完成超时取消功能 mycelery/order/tasks.py from mycelery.main imp ...

  5. 14Flask重要知识

    一,李辉<Flask Web开发实战> 1,内网穿透 内网穿透工具可以快速让flask项目运行: 1,https://localtunnel.github.io/www/ 2,https: ...

  6. vi&vim 基本使用方法

    vi/&vim 基本使用方法 本文介绍了vi (vim)的基本使用方法,但对于普通用户来说基本上够了!i/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim 是vi的升级版本,它不仅 ...

  7. iptables-centos|mysql navicat登陆不上

    iptables-centos: vi /etc/sysconfig/iptables service iptables restart ====================== mysql na ...

  8. nginx安装 linux

    1.安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel 2.创建一个文件夹 cd /usr/local m ...

  9. 使用KepServerEx进行数据模拟

    KepServerEx是一款在工业控制中比较常见的数据采集服务软件之一,提供了多种类型的驱动,具有比较广泛的适用性.很多厂商和个人都会选择用它来做OPCServer.在项目的实施或测试过程中,我们有时 ...

  10. 利用日志文件getshell

    一.包含日志文件漏洞利用概述           当我们没有上传点,并且也没有url_allow_include功能时,我们就可以考虑包含服务器的日志文件.        利用思路也比较简单,当我们访 ...