【问题标题】:Why do Linux program .text sections start at 0x0804800 and stack tops start at 0xbffffff?为什么 Linux 程序的 .text 部分从 0x0804800 开始,堆栈顶部从 0xbffffff 开始?
【发布时间】:2013-02-10 05:58:57
【问题描述】:

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

【问题讨论】:

    标签: linux assembly virtual-memory


    【解决方案1】:

    让我们先这样说吧:大多数时候,各个部分并不需要放在一个特定的位置,更重要的是布局。如今,栈顶实际上是随机化的,请参阅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(为内核提供一些接口的内核映射动态代码库)。请注意,堆的开始也是随机的。

    【讨论】:

      【解决方案2】:

      意义不大。

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

      至于程序段不使用地址 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)。

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

      【讨论】:

      • 这一切都说得通,但这比 NULL 高出 200 个数量级(0x0004096 与 0x804800)。您也没有提到堆栈未从内存字面顶部(0xfffffff)开始的原因
      • 我不知道为什么 .text 开头那么高。但为什么不能呢?我已经在最后一段中提到了堆栈位置。
      • 32 位 Linux 内核通常使用从 0xC0000000 向上的地址。通常,任何在用户模式下访问此地址范围的尝试都会导致段错误。
      • @Dipstick 通常,是的。但是请注意,问题中的所有十六进制数字都是奇怪的 28 位(7 个十六进制数字)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-20
      • 2017-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-21
      • 1970-01-01
      相关资源
      最近更新 更多