其他分享
首页 > 其他分享> > 如何将main函数始终加载到同一地址,而变量在大多数时间内具有不同的地址?

如何将main函数始终加载到同一地址,而变量在大多数时间内具有不同的地址?

作者:互联网

我今天写了这个小程序,我对结果感到震惊.这是程序


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”并且变量的地址保持不变这是怎么发生的?我在操作系统中读到,作为虚拟化方案的一部分,操作系统不断重新定位指令地址.那么为什么每次我运行这个程序时主要加载到同一个地址呢?

先谢谢你们.

解决方法:

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

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

您看到的变量从run-to-run更改地址,因为它是在堆栈上创建的自动变量,而不是在静态分配的内存中.根据操作系统和版本,即使没有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-3,linux,virtualization,memory-address,relocation
来源: https://codeday.me/bug/20190526/1158081.html