考虑下面的程序.
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
Run Code Online (Sandbox Code Playgroud)
这是在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
Run Code Online (Sandbox Code Playgroud)
但编译器如何知道"a"具有内存地址0x402000.如果编译器已对该值进行了硬编码,因为0x402000它应首先确保该地址未被其他应用程序使用吗?
如果操作系统在数据段上分配内存,则应根据应用程序和资源改变内存地址.任何人都能解释一下我定义全局变量时会发生什么吗?
正如 Falken 教授提到的,这取决于编译器/系统……但是……Linux、Windows、Mac、流行/主要工具链:
编译器获取高级源并从中进行汇编,汇编器将其转换为对象。对象解析它可以解析的相对地址,但为链接器留下线索。
链接器......链接......它接受对象,它们的二进制blob,将它们排列到它被告知的二进制地址空间中,它为诸如全局和函数之类的东西选择地址。基本上它放置 .text、.data 和 .bss。
然后是硬件中的mmu,这使生活变得更加简单,例如,您可以将地址0x8000作为入口点编译每个程序,并且同时在地址0x8000上运行许多程序。因为他们都认为他们在那个地址,因为他们在 mmu 虚拟侧的虚拟地址空间中。在物理方面,它们实际上都位于不同的地址,但通常只有操作系统需要关心这一点。
因此,如今的编译器通常按照我们在对象中的源代码中编写函数的顺序放置函数,它们有时会重新排列在我们身上的 .data 和 .bss 项。链接器通常按照他们的指示运行,谁告诉他们的?最终我们,程序员,但提供给您的工具链具有默认值(例如自动将编译后的代码组装成一个对象并自动链接),包括引导程序代码和默认链接器脚本。该目标操作系统的该编译器的默认链接器脚本是根据该操作系统的规则设置的。
以上是您通常在 gcc 和其他主要操作系统(windows、mac 和 *nix)的主要编译器中看到的内容。这并不意味着现在没有工具链可以直接编译为最终二进制文件,或者直接编译为最终二进制文件而不是对象的汇编程序。当然,历史上也不总是这样。在您进入这些极端情况之前,我假设您在深入研究工具时将拥有上述经验。