4

我今天写了这个小程序,结果让我大吃一惊。这是程序


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 都加载到同一个地址?

提前谢谢你们。

4

2 回答 2

8

在 Linux 等 ELF 系统上,正常可执行文件(ELF 类型ET_EXEC)段加载的地址在编译时是固定的。ET_DYN诸如库之类的共享对象(ELF 类型)被构建为与位置无关,它们的段可在地址空间中的任何位置加载(可能对某些体系结构有一些限制)。可以构建可执行文件,使其实际上是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;
}
于 2010-09-13T12:40:11.877 回答
0

main(...)是操作系统每次加载和执行的运行时启动库代码。查看 CRT(C 运行时库),其中包含执行此操作的代码,具体取决于您的编译器。

要记住的另一件事是该地址-只要 C 代码有效,我就不会太担心它。这是一种侥幸模式,按顺序取决于许多因素,例如操作系统负载、使用的驱动程序、硬件、防病毒软件等……

此外,关于代码,如果添加静态变量、函数、指针,这将改变二进制代码的布局,重要是,在运行时加载的那些符号的地址将会不同。

于 2010-09-13T11:19:03.317 回答