Open chenpengcong opened 6 years ago
我们知道一般情况下函数的返回值是通过eax或rax寄存器来返回的,但是当我们返回值超过eax和rax所能存储的大小情况呢?
以下面代码为例,函数返回大小为32字节的结构体对象
本机环境:Linux cike-pc 4.14.0-deepin2-amd64 #1 SMP PREEMPT Deepin 4.14.12-2 (2018-01-06) x86_64 GNU/Linux
struct Test { char buf[32]; }; struct Test foo() { struct Test t2; return t2; } int main() { struct Test t1 = foo(); return 0; }
查看该文件编译后的汇编代码
$ objdump -d test.o test.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <foo>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 89 7d d8 mov %rdi,-0x28(%rbp) 8: 48 8b 45 d8 mov -0x28(%rbp),%rax c: 48 8b 55 e0 mov -0x20(%rbp),%rdx 10: 48 89 10 mov %rdx,(%rax) 13: 48 8b 55 e8 mov -0x18(%rbp),%rdx 17: 48 89 50 08 mov %rdx,0x8(%rax) 1b: 48 8b 55 f0 mov -0x10(%rbp),%rdx 1f: 48 89 50 10 mov %rdx,0x10(%rax) 23: 48 8b 55 f8 mov -0x8(%rbp),%rdx 27: 48 89 50 18 mov %rdx,0x18(%rax) 2b: 48 8b 45 d8 mov -0x28(%rbp),%rax 2f: 5d pop %rbp 30: c3 retq 0000000000000031 <main>: 31: 55 push %rbp 32: 48 89 e5 mov %rsp,%rbp 35: 48 83 ec 20 sub $0x20,%rsp 39: 48 8d 45 e0 lea -0x20(%rbp),%rax 3d: 48 89 c7 mov %rax,%rdi 40: b8 00 00 00 00 mov $0x0,%eax 45: e8 00 00 00 00 callq 4a <main+0x19> 4a: b8 00 00 00 00 mov $0x0,%eax 4f: c9 leaveq 50: c3 retq
汇编代码的意义如下
首先看main函数,函数main在栈上开辟0x20大小的空间,并将栈顶的地址赋值给rdi寄存器(作为传递给foo的第一个参数)。
35: 48 83 ec 20 sub $0x20,%rsp 39: 48 8d 45 e0 lea -0x20(%rbp),%rax 3d: 48 89 c7 mov %rax,%rdi
接下来看foo函数,函数foo在栈上开辟0x28大小的空间,并将栈顶(8个字节)的值赋值为%rdi(即main函数传递进来的地址)。然后将该栈上剩下的0x20大小的内存数据对应复制到main函数开辟的0x20大小的栈空间。
4: 48 89 7d d8 mov %rdi,-0x28(%rbp) 8: 48 8b 45 d8 mov -0x28(%rbp),%rax c: 48 8b 55 e0 mov -0x20(%rbp),%rdx 10: 48 89 10 mov %rdx,(%rax) 13: 48 8b 55 e8 mov -0x18(%rbp),%rdx 17: 48 89 50 08 mov %rdx,0x8(%rax) 1b: 48 8b 55 f0 mov -0x10(%rbp),%rdx 1f: 48 89 50 10 mov %rdx,0x10(%rax) 23: 48 8b 55 f8 mov -0x8(%rbp),%rdx 27: 48 89 50 18 mov %rdx,0x18(%rax)
可以这样理解上述的执行过程:
由于t2的内容已经拷贝到t1了,所以t1就相当于foo函数返回的t2
从上面分析来看,其实foo函数的声明struct Test foo()更像是void foo(struct Test *ret)
struct Test foo()
void foo(struct Test *ret)
我们知道一般情况下函数的返回值是通过eax或rax寄存器来返回的,但是当我们返回值超过eax和rax所能存储的大小情况呢?
以下面代码为例,函数返回大小为32字节的结构体对象
查看该文件编译后的汇编代码
汇编代码的意义如下
首先看main函数,函数main在栈上开辟0x20大小的空间,并将栈顶的地址赋值给rdi寄存器(作为传递给foo的第一个参数)。
接下来看foo函数,函数foo在栈上开辟0x28大小的空间,并将栈顶(8个字节)的值赋值为%rdi(即main函数传递进来的地址)。然后将该栈上剩下的0x20大小的内存数据对应复制到main函数开辟的0x20大小的栈空间。
可以这样理解上述的执行过程:
由于t2的内容已经拷贝到t1了,所以t1就相当于foo函数返回的t2
从上面分析来看,其实foo函数的声明
struct Test foo()
更像是void foo(struct Test *ret)