今天翻翻老本,翻到一款上天入地的神器 —— readelf,据说用它可以拂开云雾,抽丝剥茧,去伪存真,深入其里。它就像一把精工刀,专用于对ELF格式文件进行外科手术般的解剖,今天我们来见识见识。
先来看看什么是ELF格式文件:
在Linux中,ELF是标准的可执行文件格式,其全称是Executable and Linkable Format。
换句话说:
他们都属于ELF格式文件。
这种格式主要特点是里面的数据或代码,都是按照段(section)的方式来组织的,请看上面示例代码example的内部景象:
可以看到,这个示例程序里面包含了29个section,其中不乏熟悉的面孔,比如.init、.text、.data等等,这些section都是程序本身的内容,而其他的很多section只是系统加载器在运行程序的时候需要的一些辅助信息。
-S 选项中列出来的信息,有一项是Addr,这是程序运行时对应的段的虚拟地址,可以用这个选项查看可执行文件和可重定位文件的区别,你会发现重定位文件中的全局变量、函数等符号的地址都是0,这也验证了这些符号需要链接定位的编译原理。
另外,如果想知道更加详细的细节,可以使用不同的参数选项的组合。比如查看ELF格式头信息,可以使用:
在上述输出中,重要的信息是:
使用 -s 可以查看详细的符号表信息:
raedelf -s example
由于以上命令会输出相当多的符号表信息,这里就不贴出来了。
所谓的符号,就是程序中使用到的所有的函数名和全局变量名,由于函数和全局变量默认都是全局可见的,因此他们简称全局符号或者符号。全局符号都是需要重定位的。
执行以上命令之后,将会列举出程序中直接使用和间接调用的所有符号细节。我们甚至可以查看调用的库函数的真正版本,比如程序中使用了printf()函数,但是编译系统会为了某些目的简化为puts()函数来输出。
readelf还有很多有用的选项,帮助我们更精确理解ELF格式的内在细节,这些用法可以在man手册中查到。