为什么内存用户 space 顶部的指令地址与 linux 进程内存布局相反?

Why are instructions addresses on the top of the memory user space contrary to the linux process memory layout?

#include <stdio.h>
void func() {}

int main() {
    printf("%p", &func);

    return 0;
}

这个程序输出了0x55c4cda9464a 假设func会被存储在.text段,根据这个图,从CS:APP 我想 func 的地址应该在 .text 部分的起始地址附近,但这个地址在中间的某个地方。为什么会这样?存储在堆栈上的局部变量的地址接近 2^48 - 1,但我试图反汇编不同的 C 代码,指令总是位于该 0x55... 地址附近的某个位置。

gcc,当配置 --enable-default-pie1(默认)时,默认生成 Position Independent Executables(PIE). Which means the load address isn't same as what linker specified at compile-time (0x400000 for x86_64). This is a security mechanism so that Address Space Layout Randomization (ASLR) 2 can be enabled. That is, gcc compiles with -pie 选项。

如果你用-no-pie选项编译(gcc -no-pie file.c),那么你可以看到func的地址更接近0x400000。

在我的系统上,我得到:

$ gcc -no-pie t.c
$ ./a.out 
0x401132

你也可以用readelf查看加载地址:

$ readelf -Wl a.out | grep LOAD
 LOAD           0x000000 0x0000000000400000 0x0000000000400000 0x000478 0x000478 R   0x1000
 LOAD           0x001000 0x0000000000401000 0x0000000000401000 0x0001f5 0x0001f5 R E 0x1000
 LOAD           0x002000 0x0000000000402000 0x0000000000402000 0x000158 0x000158 R   0x1000
 LOAD           0x002e10 0x0000000000403e10 0x0000000000403e10 0x000228 0x000230 RW  0x1000

1 你可以用 gcc --verbose.

检查这个

2 您可能还会注意到您的程序打印的地址在每个 运行 中都是不同的。那是因为 ASLR。您可以通过以下方式禁用它:

$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

ASLR 默认在 Linux 上启用。