c++函数返回细节解析(从汇编角度)

c++函数返回细节解析(从汇编角度)

在学习c++ primer 5th的时候, 注意到书中说函数调用的返回值将会给到调用处的临时量.

在函数类型方面要考虑是内置类型(build-in type)还是struct/class

在函数行为方面要考虑两方面:

  1. 函数内如何return的.
  2. main函数(调用处)如何接收的.

int类型

main函数无接收

直接返回字面值常量

  1. int型20通过eax直接返回.
  2. 调用处直接丢弃, 没有临时量.
#include<iostream>
using namespace std;
int F()
{
    return 20;
}
int main()
{
    F();
}
汇编如下:0x1169 <_Z1Fv>                                                  endbr64                                                                                                                                                          │
│    0x116d <_Z1Fv+4>                                                push   %rbp                                                                                                                                                      │
│    0x116e <_Z1Fv+5>                                                mov    %rsp,%rbp                                                                                                                                                 │
│    0x1171 <_Z1Fv+8>                                                mov    $0x14,%eax                                                                                                                                                │
│    0x1176 <_Z1Fv+13>                                               pop    %rbp                                                                                                                                                      │
│    0x1177 <_Z1Fv+14>                                               ret                                                                                                                                                              │
│    0x1178 <main()>                                                 endbr64                                                                                                                                                          │
│    0x117c <main()+4>                                               push   %rbp                                                                                                                                                      │
│    0x117d <main()+5>                                               mov    %rsp,%rbp                                                                                                                                                 │
│    0x1180 <main()+8>                                               call   0x1169 <_Z1Fv>                                                                                                                                            │
│    0x1185 <main()+13>                                              mov    $0x0,%eax                                                                                                                                                 │
│    0x118a <main()+18>                                              pop    %rbp                                                                                                                                                      │
│    0x118b <main()+19>                                              ret

返回局部对象

  1. 在函数中构造了局部变量a存储20
  2. 将a的内容通过eax返回.
  3. 调用处直接抛弃, 没有临时量.
#include<iostream>
using namespace std;
int F()
{
    int a=20;
    return a;
}
int main()
{
    F();
}0x1169 <_Z1Fv>                                                  endbr64                                                                                                                               │
│    0x116d <_Z1Fv+4>                                                push   %rbp                                                                                                                           │
│    0x116e <_Z1Fv+5>                                                mov    %rsp,%rbp                                                                                                                      │
│    0x1171 <_Z1Fv+8>                                                movl   $0x14,-0x4(%rbp)                                                                                                               │
│    0x1178 <_Z1Fv+15>                                               mov    -0x4(%rbp),%eax                                                                                                                │
│    0x117b <_Z1Fv+18>                                               pop    %rbp                                                                                                                           │
│    0x117c <_Z1Fv+19>                                               ret                                                                                                                                   │
│    0x117d <main()>                                                 endbr64                                                                                                                               │
│    0x1181 <main()+4>                                               push   %rbp                                                                                                                           │
│    0x1182 <main()+5>                                               mov    %rsp,%rbp                                                                                                                      │
│    0x1185 <main()+8>                                               call   0x1169 <_Z1Fv>                                                                                                                 │
│    0x118a <main()+13>                                              mov    $0x0,%eax                                                                                                                      │
│    0x118f <main()+18>                                              pop    %rbp                                                                                                                           │
│    0x1190 <main()+19>                                              ret

main函数有接收

接收可以分为两种:

  1. 用返回值初始化.
  2. 用返回值赋值.

初始化

Dump of assembler code for function main():
main.cpp:
57	{
   0x0000000000001178 <+0>:	endbr64 
   0x000000000000117c <+4>:	push   %rbp
   0x000000000000117d <+5>:	mov    %rsp,%rbp
   0x0000000000001180 <+8>:	sub    $0x10,%rsp

58	    int a=F();
   0x0000000000001184 <+12>:	call   0x1169 <_Z1Fv>
   0x0000000000001189 <+17>:	mov    %eax,-0x4(%rbp)

59	}
   0x000000000000118c <+20>:	mov    $0x0,%eax
   0x0000000000001191 <+25>:	leave  
   0x0000000000001192 <+26>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
53	{
   0x0000000000001169 <+0>:	endbr64 
   0x000000000000116d <+4>:	push   %rbp
   0x000000000000116e <+5>:	mov    %rsp,%rbp

54	    return 20;
   0x0000000000001171 <+8>:	mov    $0x14,%eax

55	}
   0x0000000000001176 <+13>:	pop    %rbp
   0x0000000000001177 <+14>:	ret    
End of assembler dump.

赋值

  1. 定义a并且初始化.
  2. 定义a但并不初始化.
定义a并初始化

在函数调用前就已经分配了空间, 函数返回值只是覆盖了旧值.

Dump of assembler code for function main():
main.cpp:
57	{
   0x0000000000001178 <+0>:	endbr64 
   0x000000000000117c <+4>:	push   %rbp
   0x000000000000117d <+5>:	mov    %rsp,%rbp
   0x0000000000001180 <+8>:	sub    $0x10,%rsp

58	    int a=10;
   0x0000000000001184 <+12>:	movl   $0xa,-0x4(%rbp)

59	    a=F();
   0x000000000000118b <+19>:	call   0x1169 <_Z1Fv>
   0x0000000000001190 <+24>:	mov    %eax,-0x4(%rbp)

60	}
   0x0000000000001193 <+27>:	mov    $0x0,%eax
   0x0000000000001198 <+32>:	leave  
   0x0000000000001199 <+33>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
53	{
   0x0000000000001169 <+0>:	endbr64 
   0x000000000000116d <+4>:	push   %rbp
   0x000000000000116e <+5>:	mov    %rsp,%rbp

54	    return 20;
   0x0000000000001171 <+8>:	mov    $0x14,%eax

55	}
   0x0000000000001176 <+13>:	pop    %rbp
   0x0000000000001177 <+14>:	ret    
End of assembler dump.

定义a但不初始化

定义了a但是并不初始化, 则实际上等到函数返回才给a分配空间.

Dump of assembler code for function main():
main.cpp:
57	{
   0x0000000000001178 <+0>:	endbr64 
   0x000000000000117c <+4>:	push   %rbp
   0x000000000000117d <+5>:	mov    %rsp,%rbp
   0x0000000000001180 <+8>:	sub    $0x10,%rsp

58	    int a;
59	    a=F();
   0x0000000000001184 <+12>:	call   0x1169 <_Z1Fv>
   0x0000000000001189 <+17>:	mov    %eax,-0x4(%rbp)

60	}
   0x000000000000118c <+20>:	mov    $0x0,%eax
   0x0000000000001191 <+25>:	leave  
   0x0000000000001192 <+26>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
53	{
   0x0000000000001169 <+0>:	endbr64 
   0x000000000000116d <+4>:	push   %rbp
   0x000000000000116e <+5>:	mov    %rsp,%rbp

54	    return 20;
   0x0000000000001171 <+8>:	mov    $0x14,%eax

55	}
   0x0000000000001176 <+13>:	pop    %rbp
   0x0000000000001177 <+14>:	ret    
End of assembler dump.

struct类型

struct和class的区别仅在于默认public和默认private, 因此本文仅使用struct来例证.

c++ primer中提到, 返回一个非引用类型的对象, 使用的是拷贝构造函数.

struct test{
    int a;
    int b;
    int c;
    test(int x,int y, int z):a(x),b(y),c(z){printf("constructor\n");}
    test(const test &rhs):a(rhs.a),b(rhs.b),c(rhs.c){printf("copy constructor\n");}
};

main函数无接收

下面的例子中, 广泛出现如下代码, 其目的, 我猜测是放入一个八字节的标记(乱码之类的), 如果程序正常结束, 则此八字节的内容不应该发生改变

Dump of assembler code for function main():
main.cpp:
   0x00000000000011ba <+0>:	endbr64 
   0x00000000000011be <+4>:	push   %rbp
   0x00000000000011bf <+5>:	mov    %rsp,%rbp
   0x00000000000011c2 <+8>:	sub    $0x20,%rsp
   0x00000000000011c6 <+12>:	mov    %fs:0x28,%rax
   0x00000000000011cf <+21>:	mov    %rax,-0x8(%rbp)
   0x00000000000011d3 <+25>:	xor    %eax,%eax
	.............
   0x00000000000011f3 <+57>:	je     0x11fa <main()+64>
   0x00000000000011f5 <+59>:	call   0x1080 <__stack_chk_fail@plt>
   0x00000000000011fa <+64>:	leave  
   0x00000000000011fb <+65>:	ret    

直接返回{}列表

参考c++11标准, 按照以下顺序找构造函数.

  1. 如果有参数为std::initializer_list的构造函数, 则匹配此构造函数.
  2. 如果有非explicit的构造函数能匹配{}内的参数, 作为copy-list-initialization.(但实际用的是一个构造函数)
  3. 如果只有explicit的构造函数匹配, 编译报错.

下面展示的是情况2.

  1. 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
  2. main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
  3. 此处出现了书中说的临时量.(返回值并没有被使用, 但仍然被分配了内存空间)
Dump of assembler code for function main():
main.cpp:
64	{
   0x00000000000011ba <+0>:	endbr64 
   0x00000000000011be <+4>:	push   %rbp
   0x00000000000011bf <+5>:	mov    %rsp,%rbp
   0x00000000000011c2 <+8>:	sub    $0x20,%rsp
   0x00000000000011c6 <+12>:	mov    %fs:0x28,%rax
   0x00000000000011cf <+21>:	mov    %rax,-0x8(%rbp)
   0x00000000000011d3 <+25>:	xor    %eax,%eax

65	    F();
   0x00000000000011d5 <+27>:	lea    -0x14(%rbp),%rax
   0x00000000000011d9 <+31>:	mov    %rax,%rdi
   0x00000000000011dc <+34>:	call   0x1189 <_Z1Fv>

66	}
   0x00000000000011e1 <+39>:	mov    $0x0,%eax
   0x00000000000011e6 <+44>:	mov    -0x8(%rbp),%rdx
   0x00000000000011ea <+48>:	sub    %fs:0x28,%rdx
   0x00000000000011f3 <+57>:	je     0x11fa <main()+64>
   0x00000000000011f5 <+59>:	call   0x1080 <__stack_chk_fail@plt>
   0x00000000000011fa <+64>:	leave  
   0x00000000000011fb <+65>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
60	{
   0x0000000000001189 <+0>:	endbr64 
   0x000000000000118d <+4>:	push   %rbp
   0x000000000000118e <+5>:	mov    %rsp,%rbp
   0x0000000000001191 <+8>:	sub    $0x10,%rsp
   0x0000000000001195 <+12>:	mov    %rdi,-0x8(%rbp)

61	    return {1,2,3};
   0x0000000000001199 <+16>:	mov    -0x8(%rbp),%rax
   0x000000000000119d <+20>:	mov    $0x3,%ecx
   0x00000000000011a2 <+25>:	mov    $0x2,%edx
   0x00000000000011a7 <+30>:	mov    $0x1,%esi
   0x00000000000011ac <+35>:	mov    %rax,%rdi
   0x00000000000011af <+38>:	call   0x126c <_ZN4testC2Eiii>

62	}
   0x00000000000011b4 <+43>:	mov    -0x8(%rbp),%rax
   0x00000000000011b8 <+47>:	leave  
   0x00000000000011b9 <+48>:	ret    
End of assembler dump.
Dump of assembler code for function _ZN4testC2Eiii:
main.cpp:
56	    test(int x,int y, int z):a(x),b(y),c(z){}
   0x000000000000126c <+0>:	endbr64 
   0x0000000000001270 <+4>:	push   %rbp
   0x0000000000001271 <+5>:	mov    %rsp,%rbp
   0x0000000000001274 <+8>:	mov    %rdi,-0x8(%rbp)
   0x0000000000001278 <+12>:	mov    %esi,-0xc(%rbp)
   0x000000000000127b <+15>:	mov    %edx,-0x10(%rbp)
   0x000000000000127e <+18>:	mov    %ecx,-0x14(%rbp)
   0x0000000000001281 <+21>:	mov    -0x8(%rbp),%rax
   0x0000000000001285 <+25>:	mov    -0xc(%rbp),%edx
   0x0000000000001288 <+28>:	mov    %edx,(%rax)
   0x000000000000128a <+30>:	mov    -0x8(%rbp),%rax
   0x000000000000128e <+34>:	mov    -0x10(%rbp),%edx
   0x0000000000001291 <+37>:	mov    %edx,0x4(%rax)
   0x0000000000001294 <+40>:	mov    -0x8(%rbp),%rax
   0x0000000000001298 <+44>:	mov    -0x14(%rbp),%edx
   0x000000000000129b <+47>:	mov    %edx,0x8(%rax)
   0x000000000000129e <+50>:	nop
   0x000000000000129f <+51>:	pop    %rbp
   0x00000000000012a0 <+52>:	ret    
End of assembler dump.
gdb_commands.txt:5: Error in sourced command file:
There is no field named test

返回局部对象

  1. 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
  2. main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
  3. 此处出现了书中说的临时量.(返回值并没有被使用, 但仍然被分配了内存空间)
Dump of assembler code for function main():
main.cpp:
65	{
   0x00000000000011bb <+0>:	endbr64 
   0x00000000000011bf <+4>:	push   %rbp
   0x00000000000011c0 <+5>:	mov    %rsp,%rbp
   0x00000000000011c3 <+8>:	sub    $0x20,%rsp
   0x00000000000011c7 <+12>:	mov    %fs:0x28,%rax
   0x00000000000011d0 <+21>:	mov    %rax,-0x8(%rbp)
   0x00000000000011d4 <+25>:	xor    %eax,%eax

66	    F();
   0x00000000000011d6 <+27>:	lea    -0x14(%rbp),%rax
   0x00000000000011da <+31>:	mov    %rax,%rdi
   0x00000000000011dd <+34>:	call   0x1189 <_Z1Fv>

67	}
   0x00000000000011e2 <+39>:	mov    $0x0,%eax
   0x00000000000011e7 <+44>:	mov    -0x8(%rbp),%rdx
   0x00000000000011eb <+48>:	sub    %fs:0x28,%rdx
   0x00000000000011f4 <+57>:	je     0x11fb <main()+64>
   0x00000000000011f6 <+59>:	call   0x1080 <__stack_chk_fail@plt>
   0x00000000000011fb <+64>:	leave  
   0x00000000000011fc <+65>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
60	{
   0x0000000000001189 <+0>:	endbr64 
   0x000000000000118d <+4>:	push   %rbp
   0x000000000000118e <+5>:	mov    %rsp,%rbp
   0x0000000000001191 <+8>:	sub    $0x10,%rsp
   0x0000000000001195 <+12>:	mov    %rdi,-0x8(%rbp)

61	    test a(1,2,3);
   0x0000000000001199 <+16>:	mov    -0x8(%rbp),%rax
   0x000000000000119d <+20>:	mov    $0x3,%ecx
   0x00000000000011a2 <+25>:	mov    $0x2,%edx
   0x00000000000011a7 <+30>:	mov    $0x1,%esi
   0x00000000000011ac <+35>:	mov    %rax,%rdi
   0x00000000000011af <+38>:	call   0x126c <_ZN4testC2Eiii>

62	    return a;
   0x00000000000011b4 <+43>:	nop

63	}
   0x00000000000011b5 <+44>:	mov    -0x8(%rbp),%rax
   0x00000000000011b9 <+48>:	leave  
   0x00000000000011ba <+49>:	ret    
End of assembler dump.
Dump of assembler code for function _ZN4testC2Eiii:
main.cpp:
56	    test(int x,int y, int z):a(x),b(y),c(z){}
   0x000000000000126c <+0>:	endbr64 
   0x0000000000001270 <+4>:	push   %rbp
   0x0000000000001271 <+5>:	mov    %rsp,%rbp
   0x0000000000001274 <+8>:	mov    %rdi,-0x8(%rbp)
   0x0000000000001278 <+12>:	mov    %esi,-0xc(%rbp)
   0x000000000000127b <+15>:	mov    %edx,-0x10(%rbp)
   0x000000000000127e <+18>:	mov    %ecx,-0x14(%rbp)
   0x0000000000001281 <+21>:	mov    -0x8(%rbp),%rax
   0x0000000000001285 <+25>:	mov    -0xc(%rbp),%edx
   0x0000000000001288 <+28>:	mov    %edx,(%rax)
   0x000000000000128a <+30>:	mov    -0x8(%rbp),%rax
   0x000000000000128e <+34>:	mov    -0x10(%rbp),%edx
   0x0000000000001291 <+37>:	mov    %edx,0x4(%rax)
   0x0000000000001294 <+40>:	mov    -0x8(%rbp),%rax
   0x0000000000001298 <+44>:	mov    -0x14(%rbp),%edx
   0x000000000000129b <+47>:	mov    %edx,0x8(%rax)
   0x000000000000129e <+50>:	nop
   0x000000000000129f <+51>:	pop    %rbp
   0x00000000000012a0 <+52>:	ret    
End of assembler dump.
gdb_commands.txt:5: Error in sourced command file:
There is no field named test

返回临时对象

  1. 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
  2. main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
  3. 此处出现了书中说的临时量.(返回值并没有被使用, 但仍然被分配了内存空间)
Dump of assembler code for function main():
main.cpp:
64	{
   0x00000000000011ba <+0>:	endbr64 
   0x00000000000011be <+4>:	push   %rbp
   0x00000000000011bf <+5>:	mov    %rsp,%rbp
   0x00000000000011c2 <+8>:	sub    $0x20,%rsp
   0x00000000000011c6 <+12>:	mov    %fs:0x28,%rax
   0x00000000000011cf <+21>:	mov    %rax,-0x8(%rbp)
   0x00000000000011d3 <+25>:	xor    %eax,%eax

65	    F();
   0x00000000000011d5 <+27>:	lea    -0x14(%rbp),%rax
   0x00000000000011d9 <+31>:	mov    %rax,%rdi
   0x00000000000011dc <+34>:	call   0x1189 <_Z1Fv>

66	}
   0x00000000000011e1 <+39>:	mov    $0x0,%eax
   0x00000000000011e6 <+44>:	mov    -0x8(%rbp),%rdx
   0x00000000000011ea <+48>:	sub    %fs:0x28,%rdx
   0x00000000000011f3 <+57>:	je     0x11fa <main()+64>
   0x00000000000011f5 <+59>:	call   0x1080 <__stack_chk_fail@plt>
   0x00000000000011fa <+64>:	leave  
   0x00000000000011fb <+65>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
60	{
   0x0000000000001189 <+0>:	endbr64 
   0x000000000000118d <+4>:	push   %rbp
   0x000000000000118e <+5>:	mov    %rsp,%rbp
   0x0000000000001191 <+8>:	sub    $0x10,%rsp
   0x0000000000001195 <+12>:	mov    %rdi,-0x8(%rbp)

61	    return test(1,2,3);
   0x0000000000001199 <+16>:	mov    -0x8(%rbp),%rax
   0x000000000000119d <+20>:	mov    $0x3,%ecx
   0x00000000000011a2 <+25>:	mov    $0x2,%edx
   0x00000000000011a7 <+30>:	mov    $0x1,%esi
   0x00000000000011ac <+35>:	mov    %rax,%rdi
   0x00000000000011af <+38>:	call   0x126c <_ZN4testC2Eiii>

62	}
   0x00000000000011b4 <+43>:	mov    -0x8(%rbp),%rax
   0x00000000000011b8 <+47>:	leave  
   0x00000000000011b9 <+48>:	ret    
End of assembler dump.
Dump of assembler code for function _ZN4testC2Eiii:
main.cpp:
56	    test(int x,int y, int z):a(x),b(y),c(z){}
   0x000000000000126c <+0>:	endbr64 
   0x0000000000001270 <+4>:	push   %rbp
   0x0000000000001271 <+5>:	mov    %rsp,%rbp
   0x0000000000001274 <+8>:	mov    %rdi,-0x8(%rbp)
   0x0000000000001278 <+12>:	mov    %esi,-0xc(%rbp)
   0x000000000000127b <+15>:	mov    %edx,-0x10(%rbp)
   0x000000000000127e <+18>:	mov    %ecx,-0x14(%rbp)
   0x0000000000001281 <+21>:	mov    -0x8(%rbp),%rax
   0x0000000000001285 <+25>:	mov    -0xc(%rbp),%edx
   0x0000000000001288 <+28>:	mov    %edx,(%rax)
   0x000000000000128a <+30>:	mov    -0x8(%rbp),%rax
   0x000000000000128e <+34>:	mov    -0x10(%rbp),%edx
   0x0000000000001291 <+37>:	mov    %edx,0x4(%rax)
   0x0000000000001294 <+40>:	mov    -0x8(%rbp),%rax
   0x0000000000001298 <+44>:	mov    -0x14(%rbp),%edx
   0x000000000000129b <+47>:	mov    %edx,0x8(%rax)
   0x000000000000129e <+50>:	nop
   0x000000000000129f <+51>:	pop    %rbp
   0x00000000000012a0 <+52>:	ret    
End of assembler dump.
gdb_commands.txt:5: Error in sourced command file:
There is no field named test

main函数有接收

初始化

返回临时变量的情况与直接返回{}列表差不多, 不在单独赘述.

直接返回{}列表
  1. 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
  2. main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
  3. 此处与上面不同, 分配给a的空间(并没有使用)直接传给F, 没有临时量.
Dump of assembler code for function main():
main.cpp:
69	{
   0x00000000000011ba <+0>:	endbr64 
   0x00000000000011be <+4>:	push   %rbp
   0x00000000000011bf <+5>:	mov    %rsp,%rbp
   0x00000000000011c2 <+8>:	sub    $0x20,%rsp
   0x00000000000011c6 <+12>:	mov    %fs:0x28,%rax
   0x00000000000011cf <+21>:	mov    %rax,-0x8(%rbp)
   0x00000000000011d3 <+25>:	xor    %eax,%eax

70	    test a=F();
   0x00000000000011d5 <+27>:	lea    -0x14(%rbp),%rax
   0x00000000000011d9 <+31>:	mov    %rax,%rdi
   0x00000000000011dc <+34>:	call   0x1189 <_Z1Fv>

71	}
   0x00000000000011e1 <+39>:	mov    $0x0,%eax
   0x00000000000011e6 <+44>:	mov    -0x8(%rbp),%rdx
   0x00000000000011ea <+48>:	sub    %fs:0x28,%rdx
   0x00000000000011f3 <+57>:	je     0x11fa <main()+64>
   0x00000000000011f5 <+59>:	call   0x1080 <__stack_chk_fail@plt>
   0x00000000000011fa <+64>:	leave  
   0x00000000000011fb <+65>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
60	{
   0x0000000000001189 <+0>:	endbr64 
   0x000000000000118d <+4>:	push   %rbp
   0x000000000000118e <+5>:	mov    %rsp,%rbp
   0x0000000000001191 <+8>:	sub    $0x10,%rsp
   0x0000000000001195 <+12>:	mov    %rdi,-0x8(%rbp)

61	    // test a(1,2,3);
62	    // return a;
63	
64	    // return test(1,2,3);
65	
66	    return {1,2,3};
   0x0000000000001199 <+16>:	mov    -0x8(%rbp),%rax
   0x000000000000119d <+20>:	mov    $0x3,%ecx
   0x00000000000011a2 <+25>:	mov    $0x2,%edx
   0x00000000000011a7 <+30>:	mov    $0x1,%esi
   0x00000000000011ac <+35>:	mov    %rax,%rdi
   0x00000000000011af <+38>:	call   0x126c <_ZN4testC2Eiii>

67	}
   0x00000000000011b4 <+43>:	mov    -0x8(%rbp),%rax
   0x00000000000011b8 <+47>:	leave  
   0x00000000000011b9 <+48>:	ret    
End of assembler dump.
Dump of assembler code for function _ZN4testC2Eiii:
main.cpp:
56	    test(int x,int y, int z):a(x),b(y),c(z){}
   0x000000000000126c <+0>:	endbr64 
   0x0000000000001270 <+4>:	push   %rbp
   0x0000000000001271 <+5>:	mov    %rsp,%rbp
   0x0000000000001274 <+8>:	mov    %rdi,-0x8(%rbp)
   0x0000000000001278 <+12>:	mov    %esi,-0xc(%rbp)
   0x000000000000127b <+15>:	mov    %edx,-0x10(%rbp)
   0x000000000000127e <+18>:	mov    %ecx,-0x14(%rbp)
   0x0000000000001281 <+21>:	mov    -0x8(%rbp),%rax
   0x0000000000001285 <+25>:	mov    -0xc(%rbp),%edx
   0x0000000000001288 <+28>:	mov    %edx,(%rax)
   0x000000000000128a <+30>:	mov    -0x8(%rbp),%rax
   0x000000000000128e <+34>:	mov    -0x10(%rbp),%edx
   0x0000000000001291 <+37>:	mov    %edx,0x4(%rax)
   0x0000000000001294 <+40>:	mov    -0x8(%rbp),%rax
   0x0000000000001298 <+44>:	mov    -0x14(%rbp),%edx
   0x000000000000129b <+47>:	mov    %edx,0x8(%rax)
   0x000000000000129e <+50>:	nop
   0x000000000000129f <+51>:	pop    %rbp
   0x00000000000012a0 <+52>:	ret    
End of assembler dump.
gdb_commands.txt:5: Error in sourced command file:
There is no field named test

返回局部对象
  1. 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
  2. main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
  3. 此处与上面不同, 分配给a的空间(并没有使用)直接传给F, 没有临时量.
  4. 值得注意的是, 编译器在初始化F中的a的时候, 用的其实是main中的a的地址, 因此仅一次构造函数.
Dump of assembler code for function main():
main.cpp:
69	{
   0x00000000000011bb <+0>:	endbr64 
   0x00000000000011bf <+4>:	push   %rbp
   0x00000000000011c0 <+5>:	mov    %rsp,%rbp
   0x00000000000011c3 <+8>:	sub    $0x20,%rsp
   0x00000000000011c7 <+12>:	mov    %fs:0x28,%rax
   0x00000000000011d0 <+21>:	mov    %rax,-0x8(%rbp)
   0x00000000000011d4 <+25>:	xor    %eax,%eax

70	    test a=F();
   0x00000000000011d6 <+27>:	lea    -0x14(%rbp),%rax
   0x00000000000011da <+31>:	mov    %rax,%rdi
   0x00000000000011dd <+34>:	call   0x1189 <_Z1Fv>

71	}
   0x00000000000011e2 <+39>:	mov    $0x0,%eax
   0x00000000000011e7 <+44>:	mov    -0x8(%rbp),%rdx
   0x00000000000011eb <+48>:	sub    %fs:0x28,%rdx
   0x00000000000011f4 <+57>:	je     0x11fb <main()+64>
   0x00000000000011f6 <+59>:	call   0x1080 <__stack_chk_fail@plt>
   0x00000000000011fb <+64>:	leave  
   0x00000000000011fc <+65>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
60	{
   0x0000000000001189 <+0>:	endbr64 
   0x000000000000118d <+4>:	push   %rbp
   0x000000000000118e <+5>:	mov    %rsp,%rbp
   0x0000000000001191 <+8>:	sub    $0x10,%rsp
   0x0000000000001195 <+12>:	mov    %rdi,-0x8(%rbp)

61	    test a(1,2,3);
   0x0000000000001199 <+16>:	mov    -0x8(%rbp),%rax
   0x000000000000119d <+20>:	mov    $0x3,%ecx
   0x00000000000011a2 <+25>:	mov    $0x2,%edx
   0x00000000000011a7 <+30>:	mov    $0x1,%esi
   0x00000000000011ac <+35>:	mov    %rax,%rdi
   0x00000000000011af <+38>:	call   0x126c <_ZN4testC2Eiii>

62	    return a;
   0x00000000000011b4 <+43>:	nop

63	
64	    // return test(1,2,3);
65	
66	    // return {1,2,3};
67	}
   0x00000000000011b5 <+44>:	mov    -0x8(%rbp),%rax
   0x00000000000011b9 <+48>:	leave  
   0x00000000000011ba <+49>:	ret    
End of assembler dump.
Dump of assembler code for function _ZN4testC2Eiii:
main.cpp:
56	    test(int x,int y, int z):a(x),b(y),c(z){}
   0x000000000000126c <+0>:	endbr64 
   0x0000000000001270 <+4>:	push   %rbp
   0x0000000000001271 <+5>:	mov    %rsp,%rbp
   0x0000000000001274 <+8>:	mov    %rdi,-0x8(%rbp)
   0x0000000000001278 <+12>:	mov    %esi,-0xc(%rbp)
   0x000000000000127b <+15>:	mov    %edx,-0x10(%rbp)
   0x000000000000127e <+18>:	mov    %ecx,-0x14(%rbp)
   0x0000000000001281 <+21>:	mov    -0x8(%rbp),%rax
   0x0000000000001285 <+25>:	mov    -0xc(%rbp),%edx
   0x0000000000001288 <+28>:	mov    %edx,(%rax)
   0x000000000000128a <+30>:	mov    -0x8(%rbp),%rax
   0x000000000000128e <+34>:	mov    -0x10(%rbp),%edx
   0x0000000000001291 <+37>:	mov    %edx,0x4(%rax)
   0x0000000000001294 <+40>:	mov    -0x8(%rbp),%rax
   0x0000000000001298 <+44>:	mov    -0x14(%rbp),%edx
   0x000000000000129b <+47>:	mov    %edx,0x8(%rax)
   0x000000000000129e <+50>:	nop
   0x000000000000129f <+51>:	pop    %rbp
   0x00000000000012a0 <+52>:	ret    
End of assembler dump.
gdb_commands.txt:5: Error in sourced command file:
There is no field named test

赋值

仅使用返回局部对象做例子

  1. 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
  2. main函数为a分配了内存(rbp-0x20), 然后又分配了临时量(rbp-0x14), 用途类似上面.
  3. 值得注意的是, 编译器在初始化F中的a的时候, 用的其实是main中临时量的地址.
Dump of assembler code for function main():
main.cpp:
69	{
   0x00000000000011db <+0>:	endbr64 
   0x00000000000011df <+4>:	push   %rbp
   0x00000000000011e0 <+5>:	mov    %rsp,%rbp
   0x00000000000011e3 <+8>:	sub    $0x20,%rsp
   0x00000000000011e7 <+12>:	mov    %fs:0x28,%rax
   0x00000000000011f0 <+21>:	mov    %rax,-0x8(%rbp)
   0x00000000000011f4 <+25>:	xor    %eax,%eax

70	    test a(7,8,9);
   0x00000000000011f6 <+27>:	lea    -0x20(%rbp),%rax
   0x00000000000011fa <+31>:	mov    $0x9,%ecx
   0x00000000000011ff <+36>:	mov    $0x8,%edx
   0x0000000000001204 <+41>:	mov    $0x7,%esi
   0x0000000000001209 <+46>:	mov    %rax,%rdi
   0x000000000000120c <+49>:	call   0x12b6 <_ZN4testC2Eiii>

71	    a=F();
   0x0000000000001211 <+54>:	lea    -0x14(%rbp),%rax
   0x0000000000001215 <+58>:	mov    %rax,%rdi
   0x0000000000001218 <+61>:	call   0x11a9 <_Z1Fv>
   0x000000000000121d <+66>:	mov    -0x14(%rbp),%rax
   0x0000000000001221 <+70>:	mov    %rax,-0x20(%rbp)
   0x0000000000001225 <+74>:	mov    -0xc(%rbp),%eax
   0x0000000000001228 <+77>:	mov    %eax,-0x18(%rbp)

72	}
   0x000000000000122b <+80>:	mov    $0x0,%eax
   0x0000000000001230 <+85>:	mov    -0x8(%rbp),%rdx
   0x0000000000001234 <+89>:	sub    %fs:0x28,%rdx
   0x000000000000123d <+98>:	je     0x1244 <main()+105>
   0x000000000000123f <+100>:	call   0x1090 <__stack_chk_fail@plt>
   0x0000000000001244 <+105>:	leave  
   0x0000000000001245 <+106>:	ret    
End of assembler dump.
Dump of assembler code for function _Z1Fv:
main.cpp:
60	{
   0x00000000000011a9 <+0>:	endbr64 
   0x00000000000011ad <+4>:	push   %rbp
   0x00000000000011ae <+5>:	mov    %rsp,%rbp
   0x00000000000011b1 <+8>:	sub    $0x10,%rsp
   0x00000000000011b5 <+12>:	mov    %rdi,-0x8(%rbp)

61	    test a(1,2,3);
   0x00000000000011b9 <+16>:	mov    -0x8(%rbp),%rax
   0x00000000000011bd <+20>:	mov    $0x3,%ecx
   0x00000000000011c2 <+25>:	mov    $0x2,%edx
   0x00000000000011c7 <+30>:	mov    $0x1,%esi
   0x00000000000011cc <+35>:	mov    %rax,%rdi
   0x00000000000011cf <+38>:	call   0x12b6 <_ZN4testC2Eiii>

62	    return a;
   0x00000000000011d4 <+43>:	nop

63	
64	    // return test(1,2,3);
65	
66	    // return {1,2,3};
67	}
   0x00000000000011d5 <+44>:	mov    -0x8(%rbp),%rax
   0x00000000000011d9 <+48>:	leave  
   0x00000000000011da <+49>:	ret    
End of assembler dump.
Dump of assembler code for function _ZN4testC2Eiii:
main.cpp:
56	    test(int x,int y, int z):a(x),b(y),c(z){printf("constructor\n");}
   0x00000000000012b6 <+0>:	endbr64 
   0x00000000000012ba <+4>:	push   %rbp
   0x00000000000012bb <+5>:	mov    %rsp,%rbp
   0x00000000000012be <+8>:	sub    $0x20,%rsp
   0x00000000000012c2 <+12>:	mov    %rdi,-0x8(%rbp)
   0x00000000000012c6 <+16>:	mov    %esi,-0xc(%rbp)
   0x00000000000012c9 <+19>:	mov    %edx,-0x10(%rbp)
   0x00000000000012cc <+22>:	mov    %ecx,-0x14(%rbp)
   0x00000000000012cf <+25>:	mov    -0x8(%rbp),%rax
   0x00000000000012d3 <+29>:	mov    -0xc(%rbp),%edx
   0x00000000000012d6 <+32>:	mov    %edx,(%rax)
   0x00000000000012d8 <+34>:	mov    -0x8(%rbp),%rax
   0x00000000000012dc <+38>:	mov    -0x10(%rbp),%edx
   0x00000000000012df <+41>:	mov    %edx,0x4(%rax)
   0x00000000000012e2 <+44>:	mov    -0x8(%rbp),%rax
   0x00000000000012e6 <+48>:	mov    -0x14(%rbp),%edx
   0x00000000000012e9 <+51>:	mov    %edx,0x8(%rax)
   0x00000000000012ec <+54>:	lea    0xd11(%rip),%rax        # 0x2004
   0x00000000000012f3 <+61>:	mov    %rax,%rdi
   0x00000000000012f6 <+64>:	call   0x10b0 <puts@plt>
   0x00000000000012fb <+69>:	nop
   0x00000000000012fc <+70>:	leave  
   0x00000000000012fd <+71>:	ret    
End of assembler dump.
gdb_commands.txt:5: Error in sourced command file:
There is no field named test

附记: gdb汇编使用

通过shell实现汇编输出重定向

#!/bin/bash
g++ -g main.cpp -o main
rm gdb.txt

# 指定要调试的程序
program="main"

# 指定输出文件名
output_file="1.txt"

# 创建 GDB 命令文件
gdb_commands="gdb_commands.txt"
echo "set logging enabled on" > $gdb_commands
echo "disassemble /s main" >> $gdb_commands
echo "disassemble /s F" >> $gdb_commands
echo "disassemble /s test::test(int,int,int)" >> $gdb_commands
echo "disassemble /s test::test(const test &)" >> $gdb_commands
echo "set logging enabled off" >> $gdb_commands
echo "quit" >> $gdb_commands

# 运行 GDB 并执行命令文件
gdb --batch -x $gdb_commands $program

# 删除临时的 GDB 命令文件
rm $gdb_commands

echo "汇编代码已保存到文件: $output_file"

其中, 输出汇编有几种方式:

  1. disassemble /s main 按照源代码行分割输出
  2. disassemble /r main 整体输出, 不包含源代码
  3. layout asm 暂时不知道如何重定向
  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值