关于程序入口地址

profile

Posted by bbkgl on December 12, 2019

孤帆远影碧空尽

唯见长江天际流

关于编译器

这里先贴出我使用的开发机信息:

20200112220256.png

疑问来了

使用readelf -h <filename>,随意读取一个可执行文件:

20200112220343.png

怪事发生了,居然提示是共享库文件。。。

于是我们随便用gcc去编译一段代码,生成可执行文件

20200112220443.png

还是共享库文件。。。

于是我去试试从其他地方拷贝过来的可执行文件:

20200112220832.png

居然是EXEC的可执行文件 。。。!!!

突然就想到了是不是因为gcc/g++编译器默认编译生成成DYN文件。

于是马上安装了clang试试,于是出现了如下场景。

20200112220910.png

是的,使用clang编译后生成的文件是EXEC的可执行文件!!!

但是我在家里用自己电脑,gcc生成的就是exec的可执行文件呀!!!

现在出现了几个疑点。

  • 是gcc/g++编译生成文件就是DYN文件吗?但是我在家的时候确实是gcc,而且生成的就是EXEC文件

  • 为什么标记着DYN文件,是共享库文件,为什么又能够执行呢?而且系统命令都是用的DYN文件格式

  • DYN文件首地址为什么是莫名的55c6664f3000???x64的程序首地址不是400000吗???

  • 为什么elf文档(无论x64还是x86)中明明说了,elf中记录的全局变量地址就是绝对地址,为什么在DYN文件中是相对地址?而且是相对于程序加载首地址

后面两个疑点,是基于我要获取全局变量地址这样一个需求来的,如果有人看到这个文档有疑问的,可以自己尝试一下,这里说的应该是没有错的。

解除疑问

陆陆续续折腾好几天以后,我想到了自己笔记本上的gcc/g++版本好像是5.几的,然后这里是6.3,就开始思考是不是gcc版本的问题呢?

首先在google上搜索了关于DYN相关的信息,发现了 ELF file type - ET_EXEC and ET_DYN duplicate 这么一个帖子,然后其中表示答案在32-bit absolute addresses no longer allowed in x86-64 Linux?中。

该帖子提了一个问题,问题内容是:

64位linux默认使用小地址来存储静态变量和全局变量,通常在2GB以下的空间里,这样确保可以使用绝对地址,然而我在汇编中创建32位绝对地址时,会报错。然后就是考虑是不是gcc版本低的问题,以及gcc什么时候做了这种改变。。

下面的回答是这么说的:

你的发行版使用–enable-default-pie配置了gcc,因此默认情况下它将制作与位置无关的可执行文件(允许可执行文件和库的ASLR)。如今,大多数发行版都在这样做。您实际上是在创建一个共享库:PIE可执行文件有点像使用带有入口点的共享库。ELF共享库不允许使用32位绝对重定位。这将阻止它们被加载到低2GB(用于符号扩展的32位地址)之外的空间。当然可以使用64位绝对地址,但是通常只希望将其用于跳转表或其他静态数据,而不是作为指令的一部分来使用。

回答后面内容还很长,总结一下主要内容就是gcc高版本中,生成的都是共享库文件,为了能够把这些共享库文件加载到高地址中,不再使用绝对地址,而使用相对地址(相对程序首地址的相对地址)。

好吧,问题算是解决了,但还是很尴尬,因为在能搜索到的大部分文档中,依然是这么写的:

20200112221008.png

在可执行文件或者共享库文件中,st_value记录的是一个虚拟地址,节偏移量会让位于该地址。。。

所以最终结论就是,使用clang或者gcc/g++的低版本好了,或者去做某些判断以调整。

吐槽

这个问题其实不影响使用gcc,但是对于开发profile工具来说,还是挺棘手的,毕竟需要获取到很多变量的地址。