c - 为什么 main 函数总是加载在同一个地址,而变量大多数时候有不同的地址?

标签 c linux virtualization memory-address relocation

我今天写了这个小程序,结果令我震惊。这是程序


int main(int argc, char **argv)
{
 int a;
 printf("\n\tMain is located at: %p and the variable a is located at address: %p",main,&a);
 return 0;
}

在我的机器上,main 函数总是加载到地址“0x80483d4”,变量的地址不断变化这是怎么发生的?我在操作系统中读到,作为虚拟化方案的一部分,操作系统不断重新定位指令地址。那么,为什么每次我运行这个程序时,main 都加载到相同的地址?

在此先感谢大家。

最佳答案

在 ELF 系统(如 Linux)上,正常可执行文件(ELF 类型 ET_EXEC)加载的地址在编译时是固定的。诸如库之类的共享对象(ELF 类型 ET_DYN)被构建为与位置无关,它们的段可加载到地址空间中的任何位置(可能对某些体系结构有一些限制)。构建可执行文件使其实际上是 ET_DYN 是可能的——这些被称为“位置无关可执行文件”(PIE),但这不是一种常见的技术。

您所看到的是您的 main() 函数位于已编译可执行文件的固定地址文本段中。在通过 dlsym() 找到库函数(例如 printf())后,也尝试打印它的地址——如果您的系统支持并启用了地址空间布局随机化( ASLR),那么您应该会看到该函数的地址随着程序的运行而变化。 (如果你只是通过将引用直接放在你的代码中来打印库函数的地址,你实际得到的可能是函数的过程查找表(PLT)蹦床的地址,它是在你的可执行文件中的一个固定地址静态编译的.)

您看到的变量在每次运行中都会更改地址,因为它是在堆栈上创建的自动变量,而不是在静态分配的内存中。根据操作系统和版本的不同,即使没有 ASLR,堆栈基地址也可能会在不同的运行之间移动。如果您将变量声明移动到函数外部的全局变量,您会看到它的行为方式与 main() 函数的行为方式相同。

这是一个完整的例子——用类似 gcc -o example example.c -dl 的东西编译:

#include <stdio.h>
#include <dlfcn.h>

int a = 0;

int main(int argc, char **argv)
{
    int b = 0;
    void *handle = dlopen(NULL, RTLD_LAZY);
    printf("&main: %p; &a: %p\n", &main, &a);
    printf("&printf: %p; &b: %p\n", dlsym(handle, "printf"), &b);
    return 0;
}

关于c - 为什么 main 函数总是加载在同一个地址,而变量大多数时候有不同的地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3699845/

相关文章:

c++ - 我应该从 Windows 主机虚拟化什么 Linux 发行版来运行 Eclipse?

c - 为矩阵动态分配数组

c - printf 导致代码崩溃

c - "more"作为管道命令的目标会破坏 bash

linux - 如何从 Linux 上的系统缓存中逐出文件?

linux - 无论父文件夹名称如何,查找和排序唯一文件名

c - 查找字符串中的公共(public)字符

python - docker -py : Accessing output of python script run in ENTRYPOINT command

php - PHP 如何使用超过 100% 的 CPU?

virtualization - 如何完全自动化无人值守的 virt-install?