为什么Linux程序.text部分从0x0804800开始,堆栈顶部从0xbffffff开始?

时间:2013-02-10 05:58:57

标签: linux assembly virtual-memory

根据Assembly Primer For Hackers (Part 2) Virtual Memory Organization,Linux计划.text部分从0x0804800开始,堆栈顶部从0xbffffff开始。这些数字有什么意义?为什么不在.text(或0x00000000x0000020处开始0x0000040以过去NULL之后的下一个32位或64位?为什么不在0xfffffff开始堆栈顶部?

2 个答案:

答案 0 :(得分:5)

让我们首先说一下:大部分时间,各个部分不需要放在特定的位置,更重要的是布局。如今,堆栈顶部实际上是随机的,请参阅here

0x08048000是ld在Linux / x86上启动第一个PT_LOAD段的默认地址。在Linux / amd64上,默认值为0x400000,您可以使用自定义链接描述文件更改默认值。您还可以将.text部分以-Wl,-Ttext,0xNNNNNNNN标志开头的位置更改为gcc。要理解为什么.text没有映射到地址0,请记住,为方便起见,NULL指针通常映射到((void *)0)。因此,将零页面映射为不可访问以捕获NULL指针的使用是有用的。 .text开始之前的内存实际上被很多东西使用了;以cat /proc/self/maps为例:

$ cat /proc/self/maps 
001c0000-00317000 r-xp 00000000 08:01 245836     /lib/libc-2.12.1.so
00317000-00318000 ---p 00157000 08:01 245836     /lib/libc-2.12.1.so
00318000-0031a000 r--p 00157000 08:01 245836     /lib/libc-2.12.1.so
0031a000-0031b000 rw-p 00159000 08:01 245836     /lib/libc-2.12.1.so
0031b000-0031e000 rw-p 00000000 00:00 0 
00376000-00377000 r-xp 00000000 00:00 0          [vdso]
00852000-0086e000 r-xp 00000000 08:01 245783     /lib/ld-2.12.1.so
0086e000-0086f000 r--p 0001b000 08:01 245783     /lib/ld-2.12.1.so
0086f000-00870000 rw-p 0001c000 08:01 245783     /lib/ld-2.12.1.so
08048000-08051000 r-xp 00000000 08:01 2244617    /bin/cat
08051000-08052000 r--p 00008000 08:01 2244617    /bin/cat
08052000-08053000 rw-p 00009000 08:01 2244617    /bin/cat
09ab5000-09ad6000 rw-p 00000000 00:00 0          [heap]
b7502000-b7702000 r--p 00000000 08:01 4456455    /usr/lib/locale/locale-archive
b7702000-b7703000 rw-p 00000000 00:00 0 
b771b000-b771c000 r--p 002a1000 08:01 4456455    /usr/lib/locale/locale-archive
b771c000-b771e000 rw-p 00000000 00:00 0 
bfbd9000-bfbfa000 rw-p 00000000 00:00 0          [stack]

我们在这里看到的是C库,动态加载器ld.so和内核VDSO(内核映射动态代码库,它为内核提供了一些接口)。请注意,堆的开头也是随机的。

答案 1 :(得分:3)

没有多大意义。

堆栈通常会向下扩展(到较低的地址),因此将它放在高地址并且有一定空间扩展到较低地址是合理的(但不是强制性的)。

至于不使用地址0作为程序部分,这里有一些逻辑。首先,很多软件使用0代表NULL,这是C和C ++中合法的无效指针,不应该取消引用。很多软件都有错误,它实际上是在没有正确的指针验证的情况下尝试在地址0处读取或写入内存。如果使程序无法访问地址0周围的内存区域,则可以发现其中一些错误(程序将在调试程序中崩溃或停止)。此外,由于NULL是合法的无效指针,因此该地址不应该有数据或代码(如果存在,则无法区分指向NULL的指针)。

在x86平台上,通常通过虚拟到物理地址转换使地址0周围的内存不可访问。页面表的设置方式使得虚拟地址0的条目不被一页物理内存备份,页面大小通常为4 KB,而不仅仅是少数字节。这就是为什么如果你取出地址0,你也会取出地址1到4095。在地址0处取出超过4 KB的地址空间也是合理的。其原因是指向C和C ++中的结构。你可以有一个指向结构的NULL指针,当你取消引用它时,尝试的内存访问发生在指针(0)中包含的地址加上你试图访问的结构成员和开头之间的距离结构(第一个成员为0,其余成员大于0)。

为程序选择特定的地址范围可能还有其他一些考虑因素,但我无法代表所有这些地址。操作系统可能希望在程序本身内保留一些与程序相关的东西(数据结构),那么为什么不在地址空间的可访问部分的一端附近使用固定位置呢?