Assembly X.86 X64汇编程序中的正确堆栈操作

Assembly X.86 X64汇编程序中的正确堆栈操作,assembly,stack,x86-64,Assembly,Stack,X86 64,因此,在阅读了x64体系结构快速入门指南之后,我编写了一些汇编程序 汇编器函数是从C调用的。反过来,汇编器调用C函数 我不确定堆栈机制是如何工作的,因为我似乎多次破坏堆栈 下面的代码演示: PUBLIC Lbra_R_A ; Op 16 - Long Branch Always Lbra_R_A PROC sub rsp, 28h push rbx ; must preserve rbx ; Calc destination branch address by Addi

因此,在阅读了x64体系结构快速入门指南之后,我编写了一些汇编程序

汇编器函数是从C调用的。反过来,汇编器调用C函数

我不确定堆栈机制是如何工作的,因为我似乎多次破坏堆栈

下面的代码演示:

PUBLIC Lbra_R_A ; Op 16 - Long Branch Always
Lbra_R_A PROC
    sub rsp, 28h
    push rbx ; must preserve rbx
    ; Calc destination branch address by Adding the two Bytes at [PC+1] (high byte) and [PC+2] (low byte) with PC+2 reg
    ; Get first byte high byte
    movzx rcx, word ptr [pc_s]
    mov rbx, rcx ; save pc_s into temp
    inc bx ; inc temp pc_s
    call MemRead8_s ; returns byte in ax (al)
    push ax ; save high byte
    ; Get second byte low byte @ pc_s
    mov rcx, rbx
    inc bx ; inc temp pc_s
    call MemRead8_s ; returns byte in ax (al) - this call destroys saved high byte???
    ; combine low and high bytes to make 16 bit 2 complements offset
    pop dx ; get saved high byte - wrong value
    mov ah, dl ; move the high byte to high position ; ax now contains 16 bit offset
    add bx, ax ; bx now contains pc_s(+2) + offset
    mov word ptr [pc_s], bx
    pop rbx ; must restore rbx - wrong value???
    add rsp, 28h
    ret
Lbra_R_A ENDP
我用子rsp设置堆栈,28小时,但我不知道为什么,我不知道我可以在28小时字节区域做什么!!!是给我的还是留着的。但是如果没有这个,我的代码甚至不能运行

接下来我保留rbx寄存器,因为它被认为是非易失性的。然而,当我最后恢复rbx时,它与我保存的不一样

Mid代码在调用名为MemRead8_s(由我提供)的C函数之前,我保存/推送ax寄存器。然而,当我调用该函数时,堆栈上存储的ax值被重写,所以当我尝试在几条指令之后恢复它时,它是错误的!!!调用之前和之后的rsp值似乎相同,那么调用此函数对堆栈做了什么


有谁能解释一下什么是正确的堆栈设置协议,并解释为什么我的堆栈存储会损坏?

您的函数拥有低于RSP初始值(函数项上)和高于RSP当前值的堆栈空间

在Windows上,您的函数在返回地址(阴影空间)上方还拥有32个字节。在调用另一个函数之前,请确保保留此空间,除非它是不使用阴影空间的私人助手函数

在Linux和其他非Windows系统上,x86-64 System V ABI表示您的函数比当前RSP(红色区域)低128字节。显然,
push
或函数调用将占用该空间,因此它在叶函数中最有用,以避免RSP的sub/add。但它不会被信号处理程序攻击


So
sub-rsp,28h
分配0x28字节的堆栈空间(并将堆栈对齐16个字节,因为在调用方中的
call
推送返回地址之前,堆栈对齐了16个字节)。(如果用16位(2字节)推送中断对齐,这是错误的).
ret
基本上是
pop-rip
,这就是为什么在运行
ret
之前必须将堆栈恢复到其原始值的原因


不要使用16位push/pop;保存/恢复完整的64位寄存器。
MemRead8_s
允许假定RSP在为其推送返回地址的调用之前已对齐16字节,幸运的是它恰好不依赖堆栈对齐。)

您的函数拥有低于RSP初始值的堆栈空间(在函数项上)并且高于RSP的当前值。因此
sub-rsp,28h
分配0x28字节的堆栈空间(并将堆栈按16字节对齐,通过16位推送将其中断。不要使用16位推送/pop;保存/恢复完整的64位寄存器。
MemRead8_
允许假定rsp在推送返回地址的
调用之前对齐了16字节。)16位还是字节?我还以为推斧是16位推斧呢!!!为什么我的push rbx出错了?是的,
push ax
push 2个字节,打破了创建28h
子rsp的16字节堆栈对齐。因此,如果我想保留rbx reg和ax,我可以/应该将它们存储在我保留的28h字节区域中吗?这个区域对我来说安全吗?是的,那是个好计划。或者在功能开始时(在
子rsp之前,20小时
)按rbx,最后按
弹出
,也会很有效。您不需要保存/恢复ax
ax
;您的函数可以删除该寄存器。查找您正在使用的调用约定,找出哪些reg是保留调用的,哪些是关闭调用的。实际上,您不需要使用16位寄存器。在32位或64位代码中使用32位eax。有关文档的链接,请参阅。