C与汇编的函数传参
理论
汇编的分类
按指令集架构
- CISC(复杂指令集计算机),如x86(Intel/AMD)、VAX、Z80、Motorola 68k 等。
- RISC(精简指令集计算机),如ARM、MIPS、RISC-V、PowerPC、SPARC 等。
按处理器位数
- 32位汇编,如 x86(IA-32)、ARMv7、MIPS32
- 64位汇编,如 x86-64(AMD64)、ARMv8(AArch64)、MIPS64、RISC-V 64
按汇编风格
Intel 语法,用于 x86/x86-64,指令操作数顺序为
dest, src
AT&T 语法,用于 GNU 汇编(GAS),指令操作数顺序为
src, dest
参数调用规则
在 x86-64 架构下,C 语言和汇编的参数调用规则遵循特定的调用约定(calling convention),通常是 System V AMD64 ABI(在 Linux、macOS 等系统上)或 Microsoft x64 调用约定(在 Windows 上)。
这些调用约定规定了:
- 前 6 个参数(System V)或前 4 个参数(Microsoft)通过寄存器传递:
- System V:RDI, RSI, RDX, RCX, R8, R9
- Microsoft:RCX, RDX, R8, R9
- 超出寄存器范围的参数通过栈传递,从右到左的顺序压栈(这是逆序)。
实例
通过一个函数例子来分析。
汇编代码如下
1 | loc_218DD: |
对应的C语言的函数如下
1 | parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF+2 - fRequestBuffer, |
本文不关心具体的函数实现,主要关注函数传参的对应情况
分析
- 汇编代码中的寄存器没有%,说明是intel语法,源操作数和目的操作数的顺序是 dest, src
- 出现了
RDI
和RSI
,所以判断采用了System V AMD64 ABI 而不是Microsoft x64- 前者是前 6 个参数使用 RDI, RSI, RDX, RCX, R8, R9传参,剩余的用栈从右往左传参
- 后者是前 4 个参数使用 RCX, RDX, R8, R9传参,剩余的从右到左的顺序压栈
参数\寄存器\栈
函数parseRTSPRequestString的前六个参数按照顺序使用 RDI, RSI, RDX, RCX, R8, R9传参
参数 | 寄存器 | 对应指令 |
---|---|---|
fRequestBuffer | RDI | mov rdi, rbp |
fLastCRLF+2 - fRequestBuffer | RSI | mov rax, [r15+9C88h] lea rsi, [rax+2] sub rsi, rbp |
cmdName | RDX | mov rdx, r12 |
sizeof cmdName | RCX | mov ecx, 0C8h |
urlPreSuffix | R8 | lea r8, [rsp+6E8h+var_5E8] |
sizeof urlPreSuffix | R9 | mov r9d, 0C8h |
函数parseRTSPRequestString的剩余参数按照逆序用栈传参
参数 | 对应指令 | |
---|---|---|
urlSuffix | lea r13, [rsp+720h+var_518] push r13 |
|
sizeof urlSuffix | push 0C8h | |
cseq | lea r14, [rsp+710h+var_448] push r14 |
|
sizeof cseq | push 0C8h | |
sessionIdStr | lea r11, [rsp+700h+var_378] push r11 |
|
sizeof sessionIdStr | push 0C8h | |
contentLength | lea rax, [rsp+6F0h+var_6C4] push rax |
为了便于阅读,将注释后的汇编代码补充如下
1 | loc_218DD: |