X86_64参数传递
1 从一个问题引入
如下不正确调用的输出是什么?
int main(int argc, char *argv[]) { printf("%d\n"); return 0; }
不给printf提供更多参数, 如何稳定的输出100 ?
int main(int argc, char *argv[]) { __asm__ volatile("movq $100, %rsi"); printf("%d\n"); return 0; }
2 寄存器规范
常用寄存器37个, 21个通用寄存器, 16个浮点寄存器.
2.1 通用寄存器
2.1.1 函数参数传递
整型参数入参6个, rdi, rsi, rdx, rcx, r8, r9. 超过6个入参, 压入堆栈.
写了一个9个参数的demo, 输出如下:
[yanyg@x1{192.168.1.116} ~/test ] $ ./a.out rdi : index=1, value=1 rsi : index=2, value=2 rdx : index=3, value=3 rcx : index=4, value=4 r8 : index=5, value=5 r9 : index=6, value=6 sta0 : index=0, value=7 sta1 : index=1, value=8 sta2 : index=2, value=9
demo代码如下, 为了使汇编能工作, 需要设置优化级别为0. gcc -Wall -O0 demo.c
#include <stdarg.h> #include <stdio.h> #define GetRegValueByName(reg) \ ({ \ long ret; \ __asm__ volatile("movq %%" # reg ", %0" : "=m"(ret)); \ ret; \ }) #define GetStackValueByOffset(offset) \ ({ \ long ret; \ __asm__ volatile("movq " #offset "(%rbp), %rax"); \ __asm__ volatile("movq %%rax, %0" : "=m"(ret)); \ ret; \ }) int get_gp_index(long reg, long v1, long v2, long v3, long v4, long v5, long v6) { if (reg == v1) { return 1; } if (reg == v2) { return 2; } if (reg == v3) { return 3; } if (reg == v4) { return 4; } if (reg == v5) { return 5; } if (reg == v6) { return 6; } return 0; } #define PRINT_REG(reg) \ printf("%4s : index=%d, value=%ld\n", \ #reg, get_gp_index(reg, v1, v2, v3, v4, v5, v6), reg); #define PRINT_STACK(stackIdx) \ printf("sta%d : index=%d, value=%ld\n", \ stackIdx, stackIdx, stack ## stackIdx); void print_gp_registers(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8, long v9) { long rsi = GetRegValueByName(rsi); long rdi = GetRegValueByName(rdi); long rdx = GetRegValueByName(rdx); long rcx = GetRegValueByName(rcx); long r8 = GetRegValueByName(r8); long r9 = GetRegValueByName(r9); // call + pushq rbp = 16 bytes long stack0 = GetStackValueByOffset(16); long stack1 = GetStackValueByOffset(24); long stack2 = GetStackValueByOffset(32); PRINT_REG(rdi); PRINT_REG(rsi); PRINT_REG(rdx); PRINT_REG(rcx); PRINT_REG(r8); PRINT_REG(r9); PRINT_STACK(0); PRINT_STACK(1); PRINT_STACK(2); } int main(int argc, char *argv[]) { print_gp_registers(1, 2, 3, 4, 5, 6, 7, 8, 9); return 0; }
优化为0时的部分关联汇编如下:
print_gp_registers: pushq %rbp movq %rsp, %rbp ... main: pushq $9 pushq $8 pushq $7 movl $6, %r9d movl $5, %r8d movl $4, %ecx movl $3, %edx movl $2, %esi movl $1, %edi call print_gp_registers ...
优化等级为2时的部分关联汇编如下, 可以看出rbp的数值有所差异. 我所在平台上, 优化等级为2 运行会core, core位置是第一次从栈取数据的位置.
print_gp_registers: pushq %r15 movq %rcx, %rax movq %rdx, %r15 pushq %r14 ... main: .LFB13: movl $6, %r9d movl $5, %r8d movl $4, %ecx pushq $9 movl $3, %edx movl $2, %esi movl $1, %edi pushq $8 pushq $7 call print_gp_registers ...
2.1.2 浮点寄存器
8个浮点寄存器: xmm0~xmm7. 当浮点参数和整型参数混杂时, 各自独立计算参数位置. 例如, 参数1为double, 参数2为long, 则xmm0保存参数1, rdi保存参数2.
2.1.3 使用者需要保存的寄存器
rbx, r12~r15.
2.1.4 特殊寄存器
rbp, rsp, rip.