MSVC x64函数调用参数传递顺序不符预期求助
x64汇编参数传递顺序疑惑解答
我正在学习x64汇编,使用MSVC工具集编写了基础示例程序。根据微软文档,整数和指针类型的前4个参数会通过RCX、RDX、R8、R9寄存器传递,剩余参数则使用栈传递。我理解这一规则,且从下方代码片段和调试反汇编中也能看出这一点,但无法理解为何参数传递没有按预期顺序进行:我原本预期反汇编会以从右到左的顺序(e、d、c、b、a、cp、ip)传递参数,但实际看到的是e、b、a等顺序,恳请有人帮忙解释,谢谢!
代码片段
__declspec(noinline) int testArgsAsm(int* x, char* cp, int a, int b, int c, int d, int e) { std::cout << *x << *cp; int sum = a + b + c + d + e; if (sum == 0) std::cout << "0"; else std::cout << "Non 0"; return sum; } int main(int argc, const char** args) { int a, b, c, d, e; int* ip = new int; *ip = 0x101; char* cp = new char; *cp = ('g'); std::cin >> a >> b >> c >> d >> e; testArgsAsm(ip, cp, a,b,c,d,e); return 0; }
反汇编及寄存器快照
Assembly testArgsAsm(ip, cp, a,b,c,d,e); 00007FF694DB1359 mov eax,dword ptr [e] 00007FF694DB135D mov r9d,dword ptr [b] 00007FF694DB1362 mov r8d,dword ptr [a] 00007FF694DB1367 mov dword ptr [rsp+30h],eax 00007FF694DB136B mov eax,dword ptr [d] 00007FF694DB136F mov dword ptr [rsp+28h],eax 00007FF694DB1373 mov eax,dword ptr [c] 00007FF694DB1377 mov rdx,rbx 00007FF694DB137A mov rcx,rdi 00007FF694DB137D mov dword ptr [rsp+20h],eax 00007FF694DB1381 call testArgsAsm (07FF694DB1270h) <= Instruction pointer registers at this instant RAX = 0000000000000003 RBX = 0000025633A16030 RCX = 0000025633A13EC0 RDX = 0000025633A16030 RSI = 0000000000000000 RDI = 0000025633A13EC0 R8 = 0000000000000001 R9 = 0000000000000002 R10 = 000000000000000F R11 = 000000F5016F0001 R12 = 0000000000000000 R13 = 0000000000000000 R14 = 0000000000000000 R15 = 0000000000000000 RIP = 00007FF694DB1381 RSP = 000000F5016FFB00 RBP = 0000000000000000 EFL = 00000202
问题解答
哈哈,刚学x64汇编遇到这种“参数顺序混乱”的疑惑太正常了!其实你是把编译器准备参数的操作顺序和调用约定要求的参数最终位置搞混了,咱们一步步拆解:
首先明确testArgsAsm的参数和MSVC x64调用约定的对应关系:
- 第1个参数
int* x→ 对应RCX寄存器 - 第2个参数
char* cp→ 对应RDX寄存器 - 第3个参数
int a→ 对应R8寄存器 - 第4个参数
int b→ 对应R9寄存器 - 第5个参数
int c→ 栈上rsp+20h位置 - 第6个参数
int d→ 栈上rsp+28h位置 - 第7个参数
int e→ 栈上rsp+30h位置
调用约定只要求最终调用函数时,寄存器和栈里的参数位置完全匹配上述规则,根本不限制编译器准备这些参数的操作顺序!编译器会根据寄存器使用情况、内存访问效率来灵活调整准备顺序,怎么高效怎么来。
看你给出的反汇编就能验证这一点:
- 先把
e读到eax,存到栈上rsp+30h(对应第7个参数的位置) - 把
b放到R9d(对应第4个参数的寄存器) - 把
a放到R8d(对应第3个参数的寄存器) - 把
d存到栈上rsp+28h(对应第6个参数的位置) - 把
c存到栈上rsp+20h(对应第5个参数的位置) - 最后把
ip(第1个参数)放到RCX,cp(第2个参数)放到RDX
再看你提供的寄存器快照,完全符合约定:
- RCX是
ip的值,RDX是cp的值,R8是a,R9是b - 栈上的
rsp+20h、28h、30h分别对应c、d、e
你之前误以为要“从右到左依次传递参数”,其实那是老的x86 cdecl调用约定里的入栈规则,但x64下MSVC的调用约定已经完全不同了:前4个参数用寄存器,栈上的参数是按参数定义的从左到右顺序排列(第5个参数在栈的最低地址,依次往上),而编译器准备参数的顺序完全不需要死板遵循右到左,只要最终位置正确就行。
内容的提问来源于stack exchange,提问作者Abhinav
相关产品推荐
相关产品推荐

