我今天写了这个小程序,结果令我震惊。这是程序
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/