assembly - x64 fastcall 调用者堆栈管理

标签 assembly x86-64 disassembly calling-convention masm64

MSDN 说

Integer valued arguments in the leftmost four positions are passed in left-to-right order in RCX, RDX, R8, and R9, respectively. Space is allocated on the call stack as a shadow store for callees to save those registers. Remaining arguments get pushed on the stack in right-to-left order.

所以,我尝试调用 CreateFileW 函数,这是我的代码:

sub rsp, 20h             ; Allocate 32 bytes because 4 registers 8 byte each
mov rcx, offset filename ; lpFileName
mov rdx, GENERIC_READ or GENERIC_WRITE ; dwDesiredAccess
mov r8, FILE_SHARE_DELETE              ; dwShareMode
xor r9, r9                             ; LpSecurityAttributes
          ;__And right-to-left order remaining arguments__
push 0 ; hTemplateFile
push FILE_ATTRIBUTE_NORMAL             ;dwFlagsAndAttributes
push CREATE_ALWAYS                     ; dwCreationDisposition
call CreateFileW

它可以组装,但不起作用,win64dbg 会导致下一个错误:

00000057 (ERROR_INVALID_PARAMETER)

参数 100% 正确,因为它与 Invoke 宏配合使用,只是生成的代码不同。

mov rcx,src.403000    ;name          
mov edx,C0000000      ;GENERIC_READ or GENERIC_WRITE                  
mov r8d,4             ;FILE_SHARE_DELETE                  
xor r9d,r9d           ;0                  
mov qword ptr ss:[rbp-20],2; ;CREATE_ALWAYS           
mov qword ptr ss:[rbp-18],80 ;FILE_ATTRIBUTE_NORMAL          
and qword ptr ss:[rbp-10],0  ;0           
call qword ptr ds:[<&CreateFileW>]   

所以我的问题是为什么它使用 RBP 寄存器而不是 push 并且不为“shadow-store”分配 32 个字节?


注释

由于 Microsoft 的 64 位 MASM 不再具有 invoke 指令,我正在使用俄语 MASM64 SDK具有 invoke 宏的项目。该项目大致基于 MASM32 SDK .

最佳答案

如果您想push args,您必须在sub rsp,20h之前进行。 (这效果不好,因为通常您只需要一个 sub rsp,20h 来完成整个函数,而不是每次调用一个)。并且您必须正确计数才能在最后一次推送后使 RSP%16 == 0。通常,您不想更改 RSP,除了 Windows x64 中的函数序言/结尾之外,alloca 类型的事物除外。

堆栈参数位于影子空间上方,因此,如果函数将其寄存器参数转储到影子空间中的“主空间”,它将有一个连续的参数数组。 (像 printf 这样的可变参数函数实际上会这样做;普通函数不会,除非它是调试版本。)

使用调试器在调用之前查看堆栈内存的内容(高于RSP),以您的方式与将mov存储到堆栈的正常方式进行比较影子空间上方的 arg 空间。

请注意,sub rsp,20h 不够大,无法为影子空间堆栈参数保留空间,因此您显示的“调用宏”代码必须保留该函数开头有更多空间。

Why it use RBP register instead of push and does not allocate 32 bytes for "shadow-store"?

它使用 RBP,因为如果您已经使用指令将 RBP 设置为帧指针,那么这是访问堆栈空间的正常方式。

这会更清晰、更容易地看到相对于 RSP 的寻址模式发生了什么,例如 [rsp+20h] 访问影子空间上方的第一个插槽,您想要的位置存储第一个堆栈参数。

如果您分配了可变数量的空间,因此从 RBP 到阴影空间上方的距离未知,则这是必要的,但您无论如何都可以这样做,只是为了清晰和易于获得偏移正确。但是,如果编译器或聪明的宏可以计算正确的偏移量,并且您已经使用指令将 RBP 设置为帧指针,那么使用它会稍微更有效,因为它在机器代码中节省了一个字节( [rsp+constant] 需要 SIB 字节进行编码。)

常规 MASM 在 64 位模式下没有调用。我不知道你在用什么。也许您需要手动保留堆栈空间,或者它可能在函数顶部为您执行此操作,但您将其遗漏了。 Michael Petch 说 MASM64 附带了一个 invoke 宏,它确实向您的代码添加了一个 sub rsp

关于assembly - x64 fastcall 调用者堆栈管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74247426/

相关文章:

c - 如何在 x86-64 上使用 ptrace?

debugging - 反编译 1990 DOS 应用程序

c - 打印内存时出现段错误

assembly - 计算寄存器中 1 个数的最快方法,ARM 程序集

performance - 我如何从预取内在函数中获得可衡量的好处?

assembly - Hello World 引导加载程序不工作

c - 为什么编译器坚持在这里使用被调用者保存的寄存器?

c++ - 如何修改已编译 DLL 中的函数

c - 从 gcc 中的内联汇编引用全局变量

assembly - 使用 NASM + LD 编写/链接平面二进制文件