主函数总是在同一地址加载,而variables在大多数情况下有不同的地址?

我今天写了这个小程序,结果被我吹走了。 这是程序

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

在此先感谢家伙。

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

你所看到的是你的main()函数在你编译的可执行文件的固定地址文本段中。 在通过dlsym()定位之后,试着打印一个库函数的地址,例如printf() – 如果你的系统确实支持并启用了地址空间布局随机化(ASLR),那么你应该看到这个函数的地址从运行你的程序运行。 (如果你直接在你的代码中直接输入引用,就可以打印出库函数的地址,实际上你可能得到的是函数的过程查询表(PLT)trampoline的地址,它是静态编译在你的可执行文件中的一个固定地址。)

您看到的变量是从运行到运行的地址,因为它是在堆栈上创建的自动变量,而不是静态分配的内存。 根据操作系统和版本的不同,即使没有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; } 

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

另一件需要牢记的地方 – 只要C代码有效,我不会过多担心。 这是一个侥幸模式,依赖于许多因素的顺序,如操作系统负载,使用的驱动程序,硬件,防病毒软件等…

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