为什么我的代码/数据/ bss段的内存如此之高?

时间:2017-10-30 11:51:26

标签: linux linker virtual-memory

我曾经认为,当ELF图像加载到RAM中时,代码/数据/ bss段,堆栈和堆的地址从最低到最高分配。所以,当我运行类似的东西时:

#include <stdio.h>

int ext;

int main(void) {
    printf("%p\n", &ext);
    return 0;
}

结果是在24位间隔内的某个地方。令我惊讶的是,当我在另一台机器上从头开始安装Linux发行版时,我得到的地址如0x55ae11d1e034(两台机器都是64位)。

我尝试过各种各样的事情,例如玩ulimit/proc/sys/kernel/shmmaxsetarch,但我无法将该死的地址发送到前4 GB。

有人可以解释这里发生了什么,为什么?

2 个答案:

答案 0 :(得分:1)

虚拟地址空间(高地址)不限于可用的RAM大小。这是virtual memory的重点。 通常,不需要控制加载地址。

如果您确实想拥有地址(任何随机地址可能不起作用 - 需要是页面大小的倍数),您可以传递链接器选项-Ttext-segment

  

-Ttext段=组织
   创建ELF可执行文件时,它将设置文本段的第一个字节的地址。

请参阅ld(1)的手册页。

例如,使用节目在我的机器上编译程序:

$ gcc -Wl,-Ttext-segment,0x10000000 test.c
0x10201014

答案 1 :(得分:1)

  

我曾经认为,当ELF图像加载到RAM中时,代码/数据/ bss段的地址,堆栈和堆的分配从最低到最高。

你错了。从最低地址分配的代码/ data / bss从未(至少不是自20世纪70年代以来):为了捕获NULL指针解除引用,零页面一直处于无访问保护状态。

此外,非PIE二进制文件无法在任意位置加载,只能在加载链接的位置加载(但默认地址过低:0x400000 x86_64 })。

最后,堆栈从未从最低地址分配。在堆栈朝向较低地址(例如所有i * 86和x86_64)增长的机器上,堆栈在最高地址处分配,以便为堆栈留下最大空间向下和/或堆积起来(朝向彼此)。

  

有人可以解释这里发生了什么,为什么?

您将获得与位置无关的可执行文件。

传统上,Linux上的x86_64可执行文件在地址0x400000处链接。在这样的可执行文件上输出file

$ file /bin/date
/bin/date: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked ...

较新的发行版将其GCC配置为生成PIE二进制文件(使某些攻击,例如已知地址攻击,稍微更难)。

当您在PIE二进制文件上运行file ./a.out时,您会看到:

$ file ./a.out
./a.out: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), dynamically linked ...

此类可执行文件在开始执行之前会重新定位,通常会在0x55..........上的x86_64地址范围内重新定位。

您可以通过将-no-pie传递给链接命令来链接非PIE二进制文件。