c++函数返回细节解析(从汇编角度)
在学习c++ primer 5th的时候, 注意到书中说函数调用的返回值将会给到调用处的临时量.
在函数类型方面要考虑是内置类型(build-in type)还是struct/class
在函数行为方面要考虑两方面:
- 函数内如何return的.
- main函数(调用处)如何接收的.
int类型
main函数无接收
直接返回字面值常量
- int型20通过eax直接返回.
- 调用处直接丢弃, 没有临时量.
#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
返回局部对象
- 在函数中构造了局部变量a存储20
- 将a的内容通过eax返回.
- 调用处直接抛弃, 没有临时量.
#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函数有接收
接收可以分为两种:
- 用返回值初始化.
- 用返回值赋值.
初始化
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.
赋值
- 定义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=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标准, 按照以下顺序找构造函数.
- 如果有参数为std::initializer_list的构造函数, 则匹配此构造函数.
- 如果有非explicit的构造函数能匹配{}内的参数, 作为copy-list-initialization.(但实际用的是一个构造函数)
- 如果只有explicit的构造函数匹配, 编译报错.
下面展示的是情况2.
- 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
- main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
- 此处出现了书中说的临时量.(返回值并没有被使用, 但仍然被分配了内存空间)
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
返回局部对象
- 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
- main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
- 此处出现了书中说的临时量.(返回值并没有被使用, 但仍然被分配了内存空间)
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
返回临时对象
- 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
- main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
- 此处出现了书中说的临时量.(返回值并没有被使用, 但仍然被分配了内存空间)
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函数有接收
初始化
返回临时变量的情况与直接返回{}列表差不多, 不在单独赘述.
直接返回{}列表
- 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
- main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
- 此处与上面不同, 分配给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
返回局部对象
- 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
- main函数在调用前开辟了一个地址(rbp-0x14), 先传给F, 又被F传给构造函数, 该地址即为this指针.
- 此处与上面不同, 分配给a的空间(并没有使用)直接传给F, 没有临时量.
- 值得注意的是, 编译器在初始化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
赋值
仅使用返回局部对象做例子
- 在这种情况下, 拷贝构造函数甚至没有被编译(但不能是private).
- main函数为a分配了内存(rbp-0x20), 然后又分配了临时量(rbp-0x14), 用途类似上面.
- 值得注意的是, 编译器在初始化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"
其中, 输出汇编有几种方式:
disassemble /s main
按照源代码行分割输出disassemble /r main
整体输出, 不包含源代码layout asm
暂时不知道如何重定向