0
Nothing here, because it was just an arbitrary choice by the linker
ELF and Program and Section Headers - 0x400000 on 64 bit
Program Text (.text) - Entry Point as Reported by readelf
Nothing Here either
Some unknown assembly and data - 0x600000
Initialised Data (.data) - 0x601068
Uninitialised Data (.bss) - 0x601078
Heap
|
v
Memory Mapped Region for Shared Libraries or Anything Else
^
|
User Stack
这个问题与 C 语言或 GCC 编译器无关,而与程序运行的操作系统有关。具体来说,您对进程的虚拟内存映射感兴趣- 是她确定可执行文件的堆栈、堆和段(那些
.bss,.text等等.data)位于什么地址。以下是具有 Linux 内核和 x86 系列架构的操作系统的答案。Linux内核将一个进程的整个虚拟地址空间分为两部分:用户空间内存和内核内存。具体划分不同,至少有三种选择:
0x0000000000000000结尾0x00007fffffffffff。内核内存也占用 128 TB,从地址开始到地址†0xffff800000000000结束。0xffffffffffffffff内核内存是相同的,并且由所有程序共享,所以我们将对用户空间内存感兴趣。
奇怪的是,在 Linux 中找到有关进程地址空间分区的具体信息并不容易。我设法找到了一篇相当详细的文章“Understanding the Memory Layout of Linux Executables”,经过相当长的调查,结果大约是以下进程内存分配:
因此,我们看到堆栈(eng.stack )位于地址空间的最末端并“向下”增长‡,即朝向较低地址(朝向零)。反过来,堆(eng. heap)会“向上”增长,并立即位于 section 之后
.bss。默认堆栈大小为 8 MiB。最初,为堆栈分配了 4 KiB 的第一个内存页。如果用户代码超出这 4 KiB,则会发生页面错误,从而捕获内核。然后内核检查堆栈是否超出范围 8 MiB。如果没有退出,它会为堆栈分配一个新页面(堆栈增长),如果退出,它会杀死进程。
堆栈大小可以使用程序从用户空间更改
ulimit。还值得注意的是,由于ASLR(地址空间层随机化),每次运行时特定的堆和堆栈地址总是不同的。随机化它们的函数在
linux/mm/util.c. 具体来说,这些是函数randomize_stack_top和arch_randomize_brk.一种计算进程内存结构的实用方法
在 Linux 中找出进程内存布局的另一种方法是使用文件
/proc/<PID>/maps,它实际上包含有关进程地址空间的信息。如果你写最简单的 hello world,你会看到这样的东西:在这里您可以看到文件的前五行表示可执行文件的部分
/tmp/hello。具体名称不详,但从权限集中可以猜出部分部分的含义。您还可以使用前端程序
pmap:* 在Unix SE 的一个问题下的评论中,一位名为 @phuclv 的用户声称这不是完全正确的信息。根据评论,“根据内核版本,分区率可能会有所不同。旧版本可能使用 1/3、2/2 或 3/1 分割,如 ; 所示
CONFIG_VMSPLIT_。从 2007 年开始,可以选择像 5/16 和 15/32 这样的小数分区。如果手动改一些#define-s,就可以实现任意分区。今天,受 Meltdown 漏洞影响的系统通常使用 4/4 拆分,即内核和用户模式的地址空间完全分开。”† x86_64 的虚拟内存映射的详细描述可以在内核文档中找到。
‡ 堆栈增长方向实际上取决于平台。请参阅 enSO 上的相关问题。