Linux无法在Windows运行,08 | ELF和静态链接:为什么程序无法同时在Linux和Windows下运行?...

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

C 语言代码,可编译成汇编代码,汇编器变成 CPU 可以理解机器码,CPU执行机器码。C 语言程序是如何变成可执行程序。

通过 gcc 生成的文件和 obj dump 获取到的汇编指令都有些小小的问题。我们先把前面的 add 函数示例,拆分成两个文件 add_lib.c 和link_example.c。

cd3626c58dcb

cd3626c58dcb

我们通过 gcc 来编译这两个文件,然后通过 obj dump 命令看看它们的汇编代码。

$ gcc -g -c  add_lib.c link_example.c

$ obj dump -d -M  intel -S add_lib.o

$ obj dump -d -M  intel -S link_example.o

cd3626c58dcb

cd3626c58dcb

既然代码已经“编译”成了指令,运行一下./link_example.o。文件没有执行权限Permission denied 错误。赋予权限,./link_example.o仍 cannot execute binary file: Exec format error 的错误。

看obj dump 出来代码,两个程序的地址都是从 0 开始。如果地址一样,程序如果通过 call指令调用函数的话,怎么知道应该跳转到哪一个文件里呢?

无论运行报错,还是地址重复,都因为 add_lib.o 以及link_example.o 并不是一个可执行文件(Executable Program),而是目标文件(Object File)。只有通过链接器(Linker)把多个目标文件以及调用的各种函数库链接起来,我们才能得到一个可执行文件。

gcc 的 -o 参数,生成对应可执行文件,对应执行之后,得函数的结果。

$ gcc -o  link-example add_lib.o link_example.o

$ ./link_example

c = 15

二、“C语言代码-汇编代码-机器码” 由两部分组成:

(1)编译(Compile)、汇编(Assemble)、链接(Link)生成可执行文件。

(2)通过装载器(Loader)把可执行文件装载(Load)到内存中。CPU 从内存中读取指令和数据,真正执行程序。

cd3626c58dcb

ELF格式和链接:理解链接过程

程序最终是通过装载器变成指令和数据,生成可执行代码不仅是一条条的指令。还是通过 ob jdump 指令,可执行文件的内容拿出来看:

cd3626c58dcb

可执行代码 dump 出来内容,之前的目标代码长得差不多,但长了很多。在 Linux 下,可执行文件和目标文件所使用的都是一种叫ELF(Execuatable  and Linkable File Format)的文件格式,中文名字叫可执行与可链接文件格式,存汇编指令(编译成)和别的数据。

所有 obj dump 出来的代码里,可以看到对应的函数名称,像 add、main 等等,乃至你自己定义的全局可以访问的变量名称,都存在ELF 里。名字和它们对应的地址,ELF 里存储符号表(Symbols Table)里(相当于地址簿,关联名字和地址)

add 的跳转地址,不再是下一条指令的地址了,是 add 函数入口地址,这就是 EFL 格式和链接器的功劳。

cd3626c58dcb

ELF 把信息,分成一个个 Section 存起来。文件头(File Header):文件的基本属性,是否是可执行文件,对应的 CPU、操作系统等。

1. text Section:代码段或者指令段(Code Section),保存代码和指令;

2. data Section:数据段(Data Section),初始化数据信息

3. rel.text Secion:重定位表(Relocation Table)。当前的文件里面,哪些跳转地址其实是我们不知道的。比如link_example.o调用 add 和 printf,链接发生之前,并不知道该跳转到哪里,这些信息就会存储在重定位表里;

4.symtab Section:符号表(Symbol Table)。当前文件里面定义的函数名称和对应地址的地址簿。

链接器:扫描所有输入的目标文件,收集符号表里信息,构成全局符号表。根据重定位表,把不确定跳转地址代码,根据符号表里面存储的地址修正。把目标文件合并,变成可执行代码。这也是为什么,可执行文件里面的函数调用的地址都是正确的。

cd3626c58dcb

装载器执行程序。不需要考虑地址跳转的问题,只需解析 ELF 文件,把对应的指令和数据,加载到内存里面CPU 执行。

总结延伸

为什么同样一个程序, Linux 下可执行 Windows 不能。格式不一样(可执行文件)。

(1)Linux 下ELF 文件格式, Windows可执行文件格式叫作PE(Portable Executable Format)。Linux 下的装载器只能解析ELF格式不能解析 PE。

能解析 PE 格式装载器,就可 Linux 下运行 Windows 程序。例:Wine(Linux 下)兼容 PE 格式的装载器,Windows 里也提供了 WSL(Windows  Subsystem for Linux) ,可解析加载 ELF 格式的文件。

(2)我们不仅是把代码放在文件里编译执行,可拆分成不同函数库,通过一个静态链接的机制,文件之间:有分工,有合作(通过静态链接),变成可执行程序。

ELF 文件,为了能够实现静态链接的机制,罗列程序所需要执行指令,包括链接所需要的重定位表和符号表。

课后思考

想要更深入了解程序的链接过程和 ELF 格式,我推荐你阅读《程序员的自我修养——链接、装载和库》的 1~4 章。

通过 readelf 读取出今天演示程序的符号表,看看符号表里都有哪些信息;然后通过 objdump 读取出今天演示程序的重定位表,看看里面又有哪些信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值