关于程序的编译链接与装载,有一些问题一直没有弄得很清楚。这次花了不少精力,把这些问题,好好研究了一番。在学习的过程中,熟读了<<程序员的自我修养-链接、装载与库>>这本书,收获良多。强烈推荐这本书。
此次系列文章,是针对静态编译,动态编译太多复杂,目前仍有很多问题,没有解决。
在开始之前,带有如下的一些问题:
-
程序是如何编译成可执行程序?
-
跨模块之间,如何知道其他模块的函数以及变量地址?
-
linux下,可执行程序elf,长什么样?
-
对于任意一个elf,如何查看该elf的信息?
-
反汇编中,程序的地址,以及符号,是如何得到的?
-
为什么程序,以section为单位进行组织?
-
section与segment的区别是什么?
-
ELF中的VMA与LMA的区别是什么?链接视图与执行视图区别是什么?
-
在EDA仿真中,elf,是如何装载到环境中去运行的?
-
在linux中,elf程序,是如何装载到内存中运行的?
-
链接脚本的AT,MEMORY关键字,到底有什么用?
通过查看资料,以及自己做实验,对这些问题,有了清晰的答案。希望通过本系列博文,也能让大家,对这些问题,也有清晰的答案。
一、工具介绍
首先是介绍下,之后会用到的一些工具:
-
aarch64工具链,包括如下:
-
-S参数:将源程序转换成.s
-
-c参数:将源程序转换成.o
-
aarch64-none-elf-gcc:arm-gcc编译器
-
aarch64-none-elf-as: 汇编器,将.s转换成.o
-
aarch64-none-elf-ld: 链接器,将 .o 链接成elf
-
aarch64-none-elf-objdump: 反汇编,将elf反汇编
-
readelf工具,用于查看elf文件内容
-
-a: 查看elf文件的所有信息
-
-h: 查看elf文件的头信息
-
-l: 查看program段信息
-
-S: 查看section段信息
-
-s: 查看符号表信息
-
-r: 查看可重定位表信息
-
gvim工具,-b参数,表示查看的文件是二进制文件
-
:%!xxd,该命令,在gvim中查看二进制数据
-
hexdump工具,直接查看二进制文件
借助以上的一些工具,就便于之后分析.o文件,.elf文件。
二、示例程序
首先,先介绍使用的示例程序。
两个程序:
-
a.c:
2个全局变来那个,一个有赋初值,一个没有赋初值
2个局部静态变量,一个有赋初值,一个没有赋初值
调用了b.c中的swap函数
-
b.c:
引用a.c中的一个全局变量
定义了一个全局变量
定义了swap函数,供a.c调用
在swap函数中,更改了a.c中定义的全局变量
程序如下图所示:
之后,便以编译这两个c文件,成最终的可执行程序out.elf为例,来进行详细的分析。
Makefile脚本如下:
后面的 .o和.elf文件的分析,就是基于这个示例程序进行分析的。