我的问题是关于X86汇编语言(我还在学习)。
我假设我有一个正常的功能序言,请阅读
push ebp
mov ebp,esp
我知道我可以通过访问内存目标操作数来读取或写入寄存器,让我们说我想要第一个参数,然后我会做
mov eax,[ebp +8]
得到f.e.堆栈中的整数参数。
那么为什么我不能直接使用stackpointer?
inc esp,8
pop eax
dec esp,12
这会导致错误吗?这是否用于任何情况?
我当然知道第一个操作的大小较小,因为它只有一个操作码,即mov
而不是三个,但这不是问题的关键。
为什么这样做不好?
答案 0 :(得分:2)
您可以直接使用ESP
作为指针
然而,如果发生任何推动或弹出,则ESP变成移动目标,使您的计算更加困难。
出于这个原因,我们在EBP中放置了堆栈指针的副本,因此我们不必担心ESP更改。
但是,如果您不想做任何改变堆栈指针的事情,那么使用ESP
而不是EBP
完全没问题。
如果你改变ESP
,你当然可以相应地改变ESP的抵消。
add esp,8
mov ecx,[esp-4] //never access data outside the actual stack.
pop eax
sub esp,12
请记住,任何时候都可能发生中断 中断将假定堆栈指针下方的任何内容都可以更改。如果您手动增加堆栈指针然后访问它下面的数据,就像它仍在堆栈中一样,您可能会发现那里的数据已被中断处理程序(Oops)替换。
规则:ESP以北的任何东西都是安全的,ESP以南的任何东西都标记为死亡
这就是例程创建stack frame
的原因。通过降低堆栈指针(记住堆栈增长),内存区域受到保护,因为它现在位于堆栈内部
堆栈的语义意味着ESP上方的任何数据都是安全的,ESP下面的任何数据都是公平的。
如果你违反了这两个原则中的任何一个
A - 使用非固定ESP作为基指针,或
B - 访问ESP下方的数据
您将冒险A:破坏他人的数据或B:自己处理损坏的数据。
这是不好的做法吗?
add esp,8 //equivalent to pop anyreg, pop anyreg pop eax //pop from the (new) top of the stack. sub esp,12 //reset the stack back to where is was.
如果在sub esp,12
之前发生中断,则此位堆栈空间中存储的3个整数将被更改,从而导致应用程序中的数据损坏。
请改用以下代码。
mov eax,[esp+12]
这段代码是A:安全,B:更快,C:不破坏标志寄存器,D:更短,E:用更少的字节编码。
关于add / sub的说明
如果您正在计算地址或地址偏移量,请改用LEA
它与add / sub完成相同的工作,但它不会改变标志。
lea esp,[esp+8] == add esp,8 (but without altering the flags).
答案 1 :(得分:2)
那么为什么我不直接使用堆栈指针?
angular.element("#strtDate")[0].value = null;
用作帧指针,因此编写调试器更容易(或者更容易让调试器找出当前的堆栈帧并确定局部变量和参数的位置)。
使用EBP
作为帧指针使其无法用于其他任何事情,这对性能不利 - 可以使用的寄存器越少意味着将更多时间用于将临时值移入/移出堆栈。好的代码(和好的调试器)不使用或需要EBP
作为帧指针。好的编译器通常支持省略/禁止使用EBP
作为帧指针的选项(例如GCC中的“--fomit-frame-pointer”)。
此代码效率低下:
EBP
但是,根据代码的使用方式,它可能非常危险或非常安全。更具体地说,它取决于代码是否必须满足异步事件(信号,IRQ),这些事件假定数据可以被压入堆栈而不会破坏堆栈上的现有数据。
更好的是:
add esp,8
pop eax
sub esp,12
这样效率更高,而且总是安全的。
注意:在某些(相对极端)条件下,将ESP用作另一个通用寄存器也是完全安全的。对于一个简单的场景,请考虑以下事项:
mov eax,[esp+8]