什么决定全局变量的内存地址。编译器还是操作系统?

时间:2014-10-19 12:49:26

标签: c assembly x86-16

考虑以下计划。

int a = 0x45;
int main()
{
   int i = a;
   return 0;
}

;; asm code
call   0x401780 <__main>
mov    0x402000,%eax   // why does it allocate 0x402000 only for global 'a'?
mov    %eax,0xc(%esp)
mov    $0x0,%eax
leave

这是在Windows / xp上的CodeBlocks中生成的等效汇编代码。 我知道0x402000是数据段地址。但是内存位置是否由编译器硬编码?

我认为它不是硬编码的,因为其他应用程序也可能/不会使用该内存位置。

众所周知,操作系统为局部变量分配Stack帧并返回堆栈帧的基础addess。使用%esp and %ebp寄存器和偏移量访问本地变量。

操作系统是否对全局变量执行相同操作?如果为什么价值被硬编码呢?

dw a 0x40; this directive allocates memory on data segment
mov %ax,a; copies value of a to accumulator

但编译器如何知道“a”具有内存地址0x402000。如果编译器已将值硬编码为0x402000,那么它应首先确保该地址未被其他应用程序使用吗?

如果操作系统在数据段上分配内存,则应根据应用程序和资源改变内存地址。任何人都能解释一下我定义全局变量时会发生什么吗?

2 个答案:

答案 0 :(得分:4)

正如Falken教授所说,这取决于编译器/系统......但...... Linux,Windows,Mac,流行/主要工具链:

编译器获取高级源并从中进行汇编,汇编器将其转换为对象。该对象解析了它可以存在的相对地址,但为链接器留下了线索。

链接器...链接......它接受对象,它们的二进制blob,将它们排列到它被告知的二进制地址空间中,它选择全局和函数之类的地址。基本上它放置.text,.data和.bss。

然后硬件中有mmu,这使生活变得简单得多,你可以将每个程序编译为地址0x8000作为入口点,并且有许多程序同时在地址0x8000运行。因为他们都认为他们在那个地址,因为在他们的虚拟边上的虚拟地址空间。在物理方面,它们实际上都生活在不同的地址,但通常只有操作系统需要关心它。

所以现在的编译器通常按照我们在对象的源代码中编写它们的顺序放置函数,它们有时会重新排列在我们身上的.data和.bss项。接头通常按照它们的说法操作,谁告诉他们?最终我们,程序员,但提供给你的工具链有默认值(比如自动将已编译的代码组装成一个对象并自动链接),包括引导代码和默认的链接描述文件。该目标操作系统的编译器的默认链接描述文件是根据该操作系统的规则设置的。

以上是您通常使用gcc和其他主要编译器看到的主要操作系统windows,mac和* nix。这并不意味着现在没有工具链可以做一些不同的编译直接到最终的二进制文件,或直接进入最终二进制而不是对象的汇编程序。当然,从历史上看,它也不总是这样。在你进入这些极端情况之前,我假设你在挖掘工具时会有上述经验。

答案 1 :(得分:3)

这取决于操作系统和编译器。

例如在Amiga上,如果我没记错的话,绝对地址存储在磁盘上的可执行文件中。但是当操作系统加载二进制文件时,它会动态地重写地址以适应它为程序分配的内存区域。

在你的情况下,我认为地址可以是DOS的64k限制中的绝对地址&#34;小&#34;记忆模型程序。 64k是8086架构中的一个段,DOS将为每个&#34; small&#34;分配一个完整的段。它加载的内存模型程序。 &#34; .COM&#34;文件按原样加载到64k DOS段中。

我可能没有完全正确的术语和细节,但我的主要观点是,它取决于所讨论的操作系统和编译器。