假设我有一个名为 func 的函数:
PROC func:
;Bla bla
ret
ENDP func
Run Code Online (Sandbox Code Playgroud)
现在,假设我使用 register ax,bx例如,为了保存它们的初始值,我将它们推送到函数内部的堆栈中。
现在的问题是:在创建堆栈帧之前推送寄存器之间是否有很大的不同:
PROC func:
push bp
push ax
push bx
mov bp, sp
;Bla bla
ret
ENDP func
Run Code Online (Sandbox Code Playgroud)
还是之后?
PROC func:
push bp
mov bp, sp
push ax
push bx
;Bla bla
ret
ENDP func
Run Code Online (Sandbox Code Playgroud)
我应该在我的程序中使用什么?一种方法比另一种更好或更“正确”吗?因为我目前使用第一种方法。
我正在编写一些x86_64程序集来调用C函数.我的C函数接受1个参数,因此程序集将参数放入%rdi.ABI pdf(下面链接)表示其他6个参数寄存器(rsi,rdx,rcx,r8,r9)不会在函数调用中保留.但是,由于我的C函数只接受一个long参数,我是否可以保证C函数是否会破坏其他5个寄存器?我的假设是,如果参数的值发生了变化,那么参数寄存器只会被破坏:
void foo(int a, int b) {
a++; /* %rdi will be changed, but %rsi won't be changed when control returns. */
}
Run Code Online (Sandbox Code Playgroud)
我问,因为我想在我的C函数调用中保留其他5个参数寄存器的值(无需手动显式地从堆栈中推送/弹出它们).
x86_64 ABI - http://www.x86-64.org/documentation/abi-0.99.pdf
我有一个用 MASM64/ML64 组装的 X64 ASM 例程。它是一个独立的叶函数,而不是内联汇编。它用于 Visual Studio 解决方案中的 C/C++ 程序。
我在 MSDN 上找到了两篇关于保存寄存器的参考资料:
第一个是内联汇编,但它特别指出在使用__fastcall. 它似乎也缺乏对 X64 的处理,因为它引用 32 位寄存器。
第二个告诉我们“RAX、RCX、RDX、R8、R9、R10、R11 被认为是易失性的,必须在函数调用时被认为是销毁的”。不幸的是,它没有明确说明是否需要保留它们。(如果你仔细观察,你会发现它使用的是误导,而不是说明要采取的行动)。
我认为第二篇文章在这种情况下是控制性的,但我想澄清以避免混淆......是否需要为 X64 Fastcall Leaf Functions 保留 CX/ECX/RCX?
在子例程调用中,我们保存了PC的内容,以便重新启动我们的调用例程。但是,如果被调用的子例程更改了通用寄存器的值,会发生什么?如果必须访问存储在寄存器中的旧值,它不会对调用子例程造成任何问题吗?
我只是试图在x86汇编中进行非常快速的基于计算的程序,但我需要在调用程序之前推送累加器,计数器和数据寄存器.手动推动它们的速度更快:
push eax
push ecx
push edx
Run Code Online (Sandbox Code Playgroud)
或者只是使用,
pushad
Run Code Online (Sandbox Code Playgroud)
与弹出一样.谢谢
来自 Intel 对 x64 汇编的介绍https://software.intel.com/en-us/articles/introduction-to-x64-assemble,
虽然我了解如何将 RCX、RDX、R8、R9 用作函数参数,但我见过使用 4 个以上参数的函数会恢复为使用堆栈(如 32 位代码)。示例如下:
sub_18000BF10 proc near
lpDirectory = qword ptr -638h
nShowCmd = dword ptr -630h
Parameters = word ptr -628h
sub rsp, 658h
mov r9, rcx
mov r8, rdx
lea rdx, someCommand ; "echo "Hello""...
lea rcx, [rsp+658h+Parameters] ; LPWSTR
call cs:wsprintfW
xor r11d, r11d
lea r9, [rsp+658h+Parameters] ; lpParameters
mov [rsp+658h+nShowCmd], r11d ; nShowCmd
lea r8, …Run Code Online (Sandbox Code Playgroud) 在这篇关于寄存器保存的维基百科文章中,我读到调用者函数负责某些寄存器(以防止其先前的数据被更改),而被调用者则负责其他寄存器。
我的问题是为什么我们必须让事情复杂化?为什么不让所有寄存器成为调用者的责任,在调用函数之前备份现有值并在之后重新加载它们?
我没有看到执行这些步骤有任何性能提升,有人可以帮助我理解吗?
所以我现在正在学习MIPS,我在MIPS绿板上看到,总共有12个寄存器(包括s寄存器)在调用中保留.在我对此的理解中,必须堆叠所有这些寄存器并在我们想要再次访问它们时检索它们.
不过我的问题是,如果遇到这个麻烦,为什么还要使用这些寄存器呢?是否有可能使用这些寄存器而不是可能耗尽寄存器?
我们知道,根据 x86-64 约定,寄存器%rbx、%rbp和%r12–%r15被归类为被调用者保存的寄存器。While%r10和%r111是调用者保存的寄存器。但是当我在大多数情况下编译 C 代码时,例如函数P调用Q,我看到以下函数的汇编代码Q:
Q:
push %rbx
movq %rdx, %rbx
...
popq %rbx
ret
Run Code Online (Sandbox Code Playgroud)
我们知道,由于%rbx是一个被调用者保存的寄存器,我们必须将它存储在堆栈中,并在P以后为调用者恢复它。
但它不会更简洁并通过使用调用者保存的寄存器来保存堆栈操作%r10:
Q:
movq %rdx, %r10
...
ret
Run Code Online (Sandbox Code Playgroud)
所以被调用者不需要担心保存和恢复调用者的寄存器,因为调用者在调用被调用者之前已经把它推到了堆栈?
因此,当我们的 C 程序(或其他语言)的函数 (funcA) 调用同一程序中的另一个函数 (funcB) 时,funcA 被认为是非叶函数,因为它调用其他函数。因此,设置了堆栈框架和所有内容,而不是使用 redzone。
然而,比如说在 funcB 中,我们不会调用在程序本身中显式编写的任何函数,但我们确实调用了一两个库函数,例如 fscanf()、fopen() (但我认为这并不重要,只要它是库函数)。那么 funcB 是否会不是叶函数,因为它仍在调用另一个函数?x86 中如何处理库函数?
分析一些 x86 很明显没有发生明显的跳转,但我可以看到它的执行方式类似于,call __isoc99_fscanf@PLT #和call perror@PLT #。
assembly ×9
x86-64 ×4
c ×3
x86 ×3
call ×1
callstack ×1
masm ×1
mips ×1
performance ×1
preserve ×1
riscv ×1
stack-frame ×1
subroutine ×1