c-程序集如何访问/存储堆栈上的变量



在汇编中,您可以将数据存储在寄存器或堆栈中。在任何给定的时刻都只能访问堆栈的顶部(对吗?)。考虑以下C代码:

main(){
int x=2;
func();
}
func( int x ){
int i;
char a;
}

在调用func()时,以下内容被推到堆栈上(考虑32位系统):

variable x (4 bytes, pushed by main)
<RETURN ADDRESS> (4 bytes pushed by main?)
<BASE POINTER> (4 bytes pushed by func())
variable i (4 bytes, pushed by func())
variable a (1 byte, pushed by func())

我有以下问题:

  1. 在C代码中,您可以从函数内部的任何位置访问局部变量,但在汇编中,您只能访问堆栈的顶部。C代码被翻译成汇编(在机器代码中,但汇编是它的可读形式)。那么,汇编程序如何支持读取不在堆栈顶部的变量呢?

  2. 在我的例子中,我是否遗漏了一些也会被推到堆栈中的内容?

  3. 在汇编中,如果在堆栈上推送char或int,它如何确定需要推送4字节还是1字节?因为它使用相同的操作(推)对吗?

提前感谢Gr.Maricruzz

函数开头的堆栈指针被放入寄存器中,然后通过该基地址加上变量的偏移量来访问变量/自变量。

如果您想查看代码,而不是创建对象文件,请让编译器停止创建汇编文件。然后你可以确切地看到它是如何工作的。(当然,这需要你有一个有效的C程序,不像你现在的问题中那样。)

编译器正在生成程序集,每个指令集可能不同,但最终堆栈只是一个寄存器,其中包含一个内存地址。编译器正在创建,知道它正在创建的函数的整个范围,知道在堆栈上查找本地数据项的每个数据项的距离,因此它将基于该指令集创建适当的代码来访问这些本地项。

有些指令集需要复制堆栈指针和/或将堆栈指针作为操作数进行运算,但将其他寄存器作为运算的结果,然后根据该运算(例如堆栈指针+8个字)访问该内存地址。有些指令集有一种寻址模式,在这种模式下,你可以在加载或存储中对堆栈指针应用偏移量——数学是作为指令执行的一部分完成的,你不必使用中间结果和寄存器。

在任何给定时刻都只能访问堆栈的顶部(对吗?)

否,通常ISA也有访问堆栈上其他元素的指令。也就是说,访问堆栈上的元素不限于类似pushpop的操作;通常情况下,您可以在堆栈位置和寄存器之间来回mov

Assembly可以通过地址访问任何内存(就像C一样)。

简单的、未优化的程序会在方法执行之前将所有局部变量放在堆栈上,所以变量地址是执行帧的地址加上一些移位。

然后程序可以简单地使用poppush方法在堆栈的顶部存储额外的变量(即某个表达式的子结果)。

摘要:

  1. 有一个寄存器(x86中的ESP)指向堆栈顶部
  2. 调用push是将变量移动到堆栈顶部并增加此寄存器
  3. 调用pop是从堆栈顶部移动变量并减少此寄存器
  4. 调用mov是在内存和寄存器之间移动变量,对堆栈寄存器(ESP)不起任何作用

最新更新