我曾经认为,当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/shmmax
,setarch
,但我无法将该死的地址发送到前4 GB。
有人可以解释这里发生了什么,为什么?
答案 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二进制文件。