UEFI - 调用约定(X64)
2022-09-05
约定预览
要素 | 实现 |
---|---|
实参进栈顺序 | 从右到左 |
实参管理责任 | 主调函数负责被调函数的实参的栈帧管理 |
命名约定 | [_]function-name |
为啥主调函数负责被调函数的栈帧管理?为了支持可变参数。
栈帧图示
主调函数 (caller)
- 保存现场,可变寄存器的压栈操作。
- 参数准备,初始化被调函数的参数,压栈的顺序的话请参考实参传递顺序。
- 调用指令,一是压栈返回的地址,二是分支进被调函数。
- 调用返回,被调函数的值位于
EAX
。 - 参数清理,移除参数准备对栈的影响。
- 恢复现存,可变寄存器的出栈操作。
被调函数 (callee)
- 保存现场,不变寄存器的压栈操作。
- 分配变量,本地变量既可以存储在可变寄存器中,也可以存储在调用栈中。
- 函数结束,将函数的值存储到
EAX
。 - 释放变量,释放本地变量所占的空间。
- 恢复现存,不变寄存器的出栈操作。
- 返回指令,一是出栈返回的地址,二是跳转到返回的地址。
相关表格
实参传递顺序:(反向:即与标注顺序相反)
类型 | 1st | 2nd | 3rd | 4th | 5th | + |
---|---|---|---|---|---|---|
整型 | RCX | RDX | R8 | R9 | 栈 | 栈 |
浮点 | XMM0 | XMM1 | XMM2 | XMM3 | 栈 | 栈 |
寄存器的属性:
属性 | - | - | - | - | - | - |
---|---|---|---|---|---|---|
可变 | RAX | RCX | - | - | R8 - R11 | XMM0-XMM5 |
不变 | RBP | RBX | RDI | RSI | R12 - R15 | XMM6-XMM15 |
程序示例
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = CccDemo
FILE_GUID = e6c76f7b-9449-1748-b05c-385bd8bb5441
# MODULE_TYPE = UEFI_APPLICATION
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = Main
[Sources]
Main.c
X64/Caller.nasm
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
# UefiApplicationEntryPoint
UefiDriverEntryPoint
BaseLib
DebugLib
[Depex]
TRUE
[BuildOptions.X64]
GCC:*_*_*_CC_FLAGS = -O0
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
extern
UINT64
EFIAPI
NasmCaller (
OUT UINT64 *Arg1,
IN UINT64 Arg2,
IN UINT64 Arg3
);
UINT64
EFIAPI
NasmCallee (
OUT UINT64 *Arg1,
IN UINT8 Arg2,
IN UINT32 Arg3
)
{
DEBUG((DEBUG_ERROR, "%a(): &Arg1 = %08p, &Arg2 = %08p, &Arg3 = %08p\n", __FUNCTION__, &Arg1, &Arg2, &Arg3));
DEBUG((DEBUG_ERROR, "%a(): *Arg1 = %08x, Arg2 = %08x, Arg3 = %08x\n", __FUNCTION__, *Arg1, Arg2, Arg3));
return *Arg1;
}
EFI_STATUS
EFIAPI
Main (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINT64 Atom = 0;
UINT64 *Arg1 = &Atom;
UINT64 Arg2 = 0;
UINT64 Arg3 = 1;
// CpuBreakpoint();
NasmCaller(Arg1, Arg2, Arg3);
NasmCaller(Arg1, Arg2, Arg3);
return EFI_SUCCESS;
}
DEFAULT REL ; Rip Relative Addressing
SECTION .text ; Code Section
extern ASM_PFX(NasmCallee)
;------------------------------------------------------------------------------
; UINT64
; EFIAPI
; NasmCaller (
; OUT UINT64 *Arg1,
; IN UINT64 Arg2,
; IN UINT64 Arg3
; );
; {
; UINT64 Bak = *Arg1;
; UINT64 Ret;
;
; if (*Arg1 == Arg2) {
; *Arg1 = Arg3;
; }
;
; Ret = NasmCallee(Arg1, Arg2, Arg3);
;
; return Bak & Ret;
; }
;------------------------------------------------------------------------------
global ASM_PFX(NasmCaller) ; Base.h: #define ASM_PFX(name)
ASM_PFX(NasmCaller):
; 保护现场
push rbp
mov rbp, rsp
; 分配变量
sub rsp, 8
; 变量的初始化
mov rax, [rcx]
mov [rbp-8], rax
; 本地逻辑
cmp [rcx], rdx
jne .0
mov [rcx], r8
.0:
; 函数调用.参数准备
sub rsp, 16
push r8
push rdx
push rcx
; 函数调用.调用指令
call NasmCallee
; 函数调用.调用返回
and rax, [rbp-8]
; 函数调用.参数清理
add rsp, 40
; 释放变量
add rsp, 8
; 恢复现场
pop rbp
ret
分享: