本文主要描述了X64下的汇编层面的方法调用。具体来说就是一个C语言的方法被另外一个方法调用,是如果在汇编语言X64的规范中实现的。
1.假设有如下C语言文件 "test.c"
点击查看代码
int sumNine(int one, int two, int three, int four, int five,
int six, int seven, int eight, int nine)
{
int x;
x = one + two + three + four + five + six + seven +
eight + nine;
return x;
}
int main(void)
{
int total;
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int f = 6;
int g = 7;
int h = 8;
int i = 9;
total = sumNine(a, b, c, d, e, f, g, h, i);
return 0;
}
2.通过GCC 命令 "gcc -o test.s -fverbose-asm -S -O0 -g test.c" 查看生成的汇编语言
点击查看代码
main:
endbr64
pushq %rbp #
movq %rsp, %rbp #,
subq $48, %rsp #,
# test.c:14: int a = 1;
movl $1, -40(%rbp) #, a
# test.c:15: int b = 2;
movl $2, -36(%rbp) #, b
# test.c:16: int c = 3;
movl $3, -32(%rbp) #, c
# test.c:17: int d = 4;
movl $4, -28(%rbp) #, d
# test.c:18: int e = 5;
movl $5, -24(%rbp) #, e
# test.c:19: int f = 6;
movl $6, -20(%rbp) #, f
# test.c:20: int g = 7;
movl $7, -16(%rbp) #, g
# test.c:21: int h = 8;
movl $8, -12(%rbp) #, h
# test.c:22: int i = 9;
movl $9, -8(%rbp) #, i
# test.c:23: total = sumNine(a, b, c, d, e, f, g, h, i);
movl -20(%rbp), %r9d # f, tmp84
movl -24(%rbp), %r8d # e, tmp85
movl -28(%rbp), %ecx # d, tmp86
movl -32(%rbp), %edx # c, tmp87
movl -36(%rbp), %esi # b, tmp88
movl -40(%rbp), %eax # a, tmp89
pushq %rdi # tmp90
movl -12(%rbp), %edi # h, tmp91
pushq %rdi # tmp91
movl -16(%rbp), %edi # g, tmp92
pushq %rdi # tmp92
movl %eax, %edi # tmp89,
call sumNine #
addq $24, %rsp #,
movl %eax, -4(%rbp) # tmp93, total
# test.c:24: return 0;
movl $0, %eax #, _13
# test.c:25: }
leave
ret
sumNine:
pushq %rbp
movq %rsp, %rbp
subq $48, %rsp
movl %edi, -20(%rbp) # save one
movl %esi, -24(%rbp) # save two
movl %edx, -28(%rbp) # save three
movl %ecx, -32(%rbp) # save four
movl %r8d, -36(%rbp) # save five
movl %r9d, -40(%rbp) # save six
movl -24(%rbp), %eax # load two
movl -20(%rbp), %edx # load one, subtotal
addl %eax, %edx # add two to subtotal
movl -28(%rbp), %eax # load three
addl %eax, %edx # add to subtotal
movl -32(%rbp), %eax # load four
addl %eax, %edx # add to subtotal
movl -36(%rbp), %eax # load five
addl %eax, %edx # add to subtotal
movl -40(%rbp), %eax # load six
addl %eax, %edx # add to subtotal
movl 16(%rbp), %eax # load seven
addl %eax, %edx # add to subtotal
movl 24(%rbp), %eax # load eight
addl %eax, %edx # add to subtotal
movl 32(%rbp), %eax # load nine
addl %edx, %eax # add to subtotal
movl %eax, -4(%rbp) # x <- total
movl $.LC0, %edi
call puts
movl -4(%rbp), %eax
leave
ret
Main函数中allocate了48个字节作为local variable的空间。
在调用sumNine的时候,前面6个需要传输的参数通过设置指定的Register来传输数据[2],多余的3个参数通过插入当前stack的形式来传输数据。(但是这边很奇怪的是48 + 3 * 8 = 72字节,stack pointer (rsp) 并没有16字节对齐,有知道的小伙伴告诉我一下为啥没有16字节对齐也能work)。在被调用的函数中前面6个传输通过register中获取,后面3个参数通过stack来获取。在调用sumNine的时候的Stack如下图所示。所有的参数都是通过8字节传输,并且在最后在stack中插入返回地址。
当调用到sumNine里面的时候, stack的样子设置如下。可以通过(rbp+16)拿到传入到函数中的参数,注意stack中包含了Return Address 和 Caller's rbp,所以需要加16才能拿到数据。
Summary
方法间的调用的时候,是通过Register和Stack传输数据的。并且需要返回的地址也会在调用函数的时候被插入。在调用其他函数之前 rbp 和 rsp 之间的数据存放着local variable 和 需要调用其他函数的参数。
Reference
[1] https://bob.cs.sonoma.edu/IntroCompOrg-x64/bookch11.html#x31-13800011
[2]
[3]