为什么主函数总是加载在相同的地址,而变量大多数时候有不同的地址?

2023-12-30

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


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;
}

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

提前谢谢你们。


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

你所看到的事实是你的main()函数位于已编译的可执行文件的固定地址文本段中。还尝试打印库函数的地址,例如printf()通过找到它之后dlsym()-- 如果您的系统确实支持并启用了地址空间布局随机化 (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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么主函数总是加载在相同的地址,而变量大多数时候有不同的地址? 的相关文章

随机推荐