x86_64架构下函数调用过程分析
2018-01-13 17:16
621 查看
//被分析的C程序 int test1(int a1,int b1) { int c1; c1 = a1+b1; return c1; } int test2(int a2,char b2) { int c2; c2 = test1(30,40); return c2; } int main(int argc,char **argv) { int main_c; int x=2,y=1,z=3; main_c = test2(10,20); return main_c; } //通过gcc得到可执行文件test.out /* 通过命令:objdump -d test.out > func.txt 得到反汇编文件; 结合图1 “典型的存储空间安排” 去看,可以知道以下各指令依次存储在正文段中。 */ test.out: file format elf64-x86-64 Disassembly of section .init: 0000000000400390 <_init>: 400390: 48 83 ec 08 sub $0x8,%rsp 400394: 48 8b 05 5d 0c 20 00 mov 0x200c5d(%rip),%rax # 600ff8 <_DYNAMIC+0x1d0> 40039b: 48 85 c0 test %rax,%rax 40039e: 74 05 je 4003a5 <_init+0x15> 4003a0: e8 2b 00 00 00 callq 4003d0 <__libc_start_main@plt+0x10> 4003a5: 48 83 c4 08 add $0x8,%rsp 4003a9: c3 retq Disassembly of section .plt: 00000000004003b0 <__libc_start_main@plt-0x10>: 4003b0: ff 35 52 0c 20 00 pushq 0x200c52(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8> 4000 4003b6: ff 25 54 0c 20 00 jmpq *0x200c54(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10> 4003bc: 0f 1f 40 00 nopl 0x0(%rax) 00000000004003c0 <__libc_start_main@plt>: 4003c0: ff 25 52 0c 20 00 jmpq *0x200c52(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> 4003c6: 68 00 00 00 00 pushq $0x0 4003cb: e9 e0 ff ff ff jmpq 4003b0 <_init+0x20> Disassembly of section .plt.got: 00000000004003d0 <.plt.got>: 4003d0: ff 25 22 0c 20 00 jmpq *0x200c22(%rip) # 600ff8 <_DYNAMIC+0x1d0> 4003d6: 66 90 xchg %ax,%ax Disassembly of section .text: 00000000004003e0 <_start>: 4003e0: 31 ed xor %ebp,%ebp 4003e2: 49 89 d1 mov %rdx,%r9 4003e5: 5e pop %rsi 4003e6: 48 89 e2 mov %rsp,%rdx 4003e9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 4003ed: 50 push %rax 4003ee: 54 push %rsp 4003ef: 49 c7 c0 d0 05 40 00 mov $0x4005d0,%r8 4003f6: 48 c7 c1 60 05 40 00 mov $0x400560,%rcx 4003fd: 48 c7 c7 17 05 40 00 mov $0x400517,%rdi 400404: e8 b7 ff ff ff callq 4003c0 <__libc_start_main@plt> 400409: f4 hlt 40040a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 0000000000400410 <deregister_tm_clones>: 400410: b8 37 10 60 00 mov $0x601037,%eax 400415: 55 push %rbp 400416: 48 2d 30 10 60 00 sub $0x601030,%rax 40041c: 48 83 f8 0e cmp $0xe,%rax 400420: 48 89 e5 mov %rsp,%rbp 400423: 76 1b jbe 400440 <deregister_tm_clones+0x30> 400425: b8 00 00 00 00 mov $0x0,%eax 40042a: 48 85 c0 test %rax,%rax 40042d: 74 11 je 400440 <deregister_tm_clones+0x30> 40042f: 5d pop %rbp 400430: bf 30 10 60 00 mov $0x601030,%edi 400435: ff e0 jmpq *%rax 400437: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 40043e: 00 00 400440: 5d pop %rbp 400441: c3 retq 400442: 0f 1f 40 00 nopl 0x0(%rax) 400446: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 40044d: 00 00 00 0000000000400450 <register_tm_clones>: 400450: be 30 10 60 00 mov $0x601030,%esi 400455: 55 push %rbp 400456: 48 81 ee 30 10 60 00 sub $0x601030,%rsi 40045d: 48 c1 fe 03 sar $0x3,%rsi 400461: 48 89 e5 mov %rsp,%rbp 400464: 48 89 f0 mov %rsi,%rax 400467: 48 c1 e8 3f shr $0x3f,%rax 40046b: 48 01 c6 add %rax,%rsi 40046e: 48 d1 fe sar %rsi 400471: 74 15 je 400488 <register_tm_clones+0x38> 400473: b8 00 00 00 00 mov $0x0,%eax 400478: 48 85 c0 test %rax,%rax 40047b: 74 0b je 400488 <register_tm_clones+0x38> 40047d: 5d pop %rbp 40047e: bf 30 10 60 00 mov $0x601030,%edi 400483: ff e0 jmpq *%rax 400485: 0f 1f 00 nopl (%rax) 400488: 5d pop %rbp 400489: c3 retq 40048a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 0000000000400490 <__do_global_dtors_aux>: 400490: 80 3d 99 0b 20 00 00 cmpb $0x0,0x200b99(%rip) # 601030 <__TMC_END__> 400497: 75 11 jne 4004aa <__do_global_dtors_aux+0x1a> 400499: 55 push %rbp 40049a: 48 89 e5 mov %rsp,%rbp 40049d: e8 6e ff ff ff callq 400410 <deregister_tm_clones> 4004a2: 5d pop %rbp 4004a 13c62 3: c6 05 86 0b 20 00 01 movb $0x1,0x200b86(%rip) # 601030 <__TMC_END__> 4004aa: f3 c3 repz retq 4004ac: 0f 1f 40 00 nopl 0x0(%rax) 00000000004004b0 <frame_dummy>: 4004b0: bf 20 0e 60 00 mov $0x600e20,%edi 4004b5: 48 83 3f 00 cmpq $0x0,(%rdi) 4004b9: 75 05 jne 4004c0 <frame_dummy+0x10> 4004bb: eb 93 jmp 400450 <register_tm_clones> 4004bd: 0f 1f 00 nopl (%rax) 4004c0: b8 00 00 00 00 mov $0x0,%eax 4004c5: 48 85 c0 test %rax,%rax 4004c8: 74 f1 je 4004bb <frame_dummy+0xb> 4004ca: 55 push %rbp 4004cb: 48 89 e5 mov %rsp,%rbp 4004ce: ff d0 callq *%rax 4004d0: 5d pop %rbp 4004d1: e9 7a ff ff ff jmpq 400450 <register_tm_clones> 00000000004004d6 <test1>: 4004d6: 55 push %rbp 4004d7: 48 89 e5 mov %rsp,%rbp 4004da: 89 7d ec mov %edi,-0x14(%rbp) 4004dd: 89 75 e8 mov %esi,-0x18(%rbp) 4004e0: 8b 55 ec mov -0x14(%rbp),%edx 4004e3: 8b 45 e8 mov -0x18(%rbp),%eax 4004e6: 01 d0 add %edx,%eax 4004e8: 89 45 fc mov %eax,-0x4(%rbp) 4004eb: 8b 45 fc mov -0x4(%rbp),%eax 4004ee: 5d pop %rbp 4004ef: c3 retq 00000000004004f0 <test2>: 4004f0: 55 push %rbp 4004f1: 48 89 e5 mov %rsp,%rbp 4004f4: 48 83 ec 18 sub $0x18,%rsp 4004f8: 89 7d ec mov %edi,-0x14(%rbp) 4004fb: 89 f0 mov %esi,%eax 4004fd: 88 45 e8 mov %al,-0x18(%rbp) 400500: be 28 00 00 00 mov $0x28,%esi 400505: bf 1e 00 00 00 mov $0x1e,%edi 40050a: e8 c7 ff ff ff callq 4004d6 <test1> 40050f: 89 45 fc mov %eax,-0x4(%rbp) 400512: 8b 45 fc mov -0x4(%rbp),%eax 400515: c9 leaveq 400516: c3 retq 0000000000400517 <main>: 400517: 55 push %rbp 400518: 48 89 e5 mov %rsp,%rbp 40051b: 48 83 ec 20 sub $0x20,%rsp 40051f: 89 7d ec mov %edi,-0x14(%rbp) 400522: 48 89 75 e0 mov %rsi,-0x20(%rbp) 400526: c7 45 f0 02 00 00 00 movl $0x2,-0x10(%rbp) 40052d: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%rbp) 400534: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp) 40053b: be 14 00 00 00 mov $0x14,%esi 400540: bf 0a 00 00 00 mov $0xa,%edi 400545: e8 a6 ff ff ff callq 4004f0 <test2> 40054a: 89 45 fc mov %eax,-0x4(%rbp) 40054d: 8b 45 fc mov -0x4(%rbp),%eax 400550: c9 leaveq 400551: c3 retq 400552: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 400559: 00 00 00 40055c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000400560 <__libc_csu_init>: 400560: 41 57 push %r15 400562: 41 56 push %r14 400564: 41 89 ff mov %edi,%r15d 400567: 41 55 push %r13 400569: 41 54 push %r12 40056b: 4c 8d 25 9e 08 20 00 lea 0x20089e(%rip),%r12 # 600e10 <__frame_dummy_init_array_entry> 400572: 55 push %rbp 400573: 48 8d 2d 9e 08 20 00 lea 0x20089e(%rip),%rbp # 600e18 <__init_array_end> 40057a: 53 push %rbx 40057b: 49 89 f6 mov %rsi,%r14 40057e: 49 89 d5 mov %rdx,%r13 400581: 4c 29 e5 sub %r12,%rbp 400584: 48 83 ec 08 sub $0x8,%rsp 400588: 48 c1 fd 03 sar $0x3,%rbp 40058c: e8 ff fd ff ff callq 400390 <_init> 400591: 48 85 ed test %rbp,%rbp 400594: 74 20 je 4005b6 <__libc_csu_init+0x56> 400596: 31 db xor %ebx,%ebx 400598: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1) 40059f: 00 4005a0: 4c 89 ea mov %r13,%rdx 4005a3: 4c 89 f6 mov %r14,%rsi 4005a6: 44 89 ff mov %r15d,%edi 4005a9: 41 ff 14 dc callq *(%r12,%rbx,8) 4005ad: 48 83 c3 01 add $0x1,%rbx 4005b1: 48 39 eb cmp %rbp,%rbx 4005b4: 75 ea jne 4005a0 <__libc_csu_init+0x40> 4005b6: 48 83 c4 08 add $0x8,%rsp 4005ba: 5b pop %rbx 4005bb: 5d pop %rbp 4005bc: 41 5c pop %r12 4005be: 41 5d pop %r13 4005c0: 41 5e pop %r14 4005c2: 41 5f pop %r15 4005c4: c3 retq 4005c5: 90 nop 4005c6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 4005cd: 00 00 00 00000000004005d0 <__libc_csu_fini>: 4005d0: f3 c3 repz retq Disassembly of section .fini: 00000000004005d4 <_fini>: 4005d4: 48 83 ec 08 sub $0x8,%rsp 4005d8: 48 83 c4 08 add $0x8,%rsp 4005dc: c3 retq /* 在对汇编进行分析之前,我们首先简单介绍一下基本的汇编命令 */ 特殊用途的寄存器: ax:存放函数返回值 bp:存放栈底 sp:存放栈顶 ip:存放下一条执行指令 di,si:依次对应函数的第一个参数、第二个参数..... push %rbp 等价于://先把sp-2,再把rbp值mov进内存 sub 0x02,%rsp mov %rbp,(%rsp) pop %rbp 等价于: mov (%rsp),%rbp add 0x02,%rsp call指令:首先将返回地址压入栈顶,然后跳转,相当于push和jump ret指令则是将栈顶的返回地址弹出到eip,然后按对应指令继续运行 leave 等价于: movl %ebp %esp pop %ebp /* 下面我们摘取最熟悉的main函数进行分析 */ 00000000004004d6 <test1>: /* 进入test1后,同样进行当前栈底入栈保存,但可以发现存在不同点:rsp不下偏,进行新的栈帧空间开辟。 这是因为test1函数不会继续调用子函数,所以它使用的局部变量不需要继续使用。 */ 4004d6: 55 push %rbp 4004d7: 48 89 e5 mov %rsp,%rbp //同样的,将保存在寄存器中入参值写入内存进行保存 4004da: 89 7d ec mov %edi,-0x14(%rbp) 4004dd: 89 75 e8 mov %esi,-0x18(%rbp) //进行运算 4004e0: 8b 55 ec mov -0x14(%rbp),%edx 4004e3: 8b 45 e8 mov -0x18(%rbp),%eax 4004e6: 01 d0 add %edx,%eax 4004e8: 89 45 fc mov %eax,-0x4(%rbp) //ax寄存器被用来存储函数返回值,所以把返回结果写入eax寄存器 4004eb: 8b 45 fc mov -0x4(%rbp),%eax /*由于此时rsp没有下偏开辟新的栈空间,因此rsp指向内存地址的值为调用函数的栈底, 因此通过pop将栈底寄存器值恢复成被调用函数栈底地址 */ 4004ee: 5d pop %rbp //将栈顶的返回地址弹出到eip,然后按对应指令继续运行 4004ef: c3 retq 00000000004004f0 <test2>: /*当函数被调用时,主要工作包括: 1.保存调用函数的栈帧情况,主要是栈底地址,因此需要将当前栈底地址push入栈。 2.保存调用函数栈底后,被调用函数即以当前栈顶为栈底,随后栈底下偏,相当于开辟一段栈空间给被调用函数使用。 */ 4004f0: 55 push %rbp //同样的,进入后首先将栈底地址压栈 4004f1: 48 89 e5 mov %rsp,%rbp 4004f4: 48 83 ec 18 sub $0x18,%rsp //同样的,将保存在寄存器中入参值写入内存进行保存 4004f8: 89 7d ec mov %edi,-0x14(%rbp) 4004fb: 89 f0 mov %esi,%eax 4004fd: 88 45 e8 mov %al,-0x18(%rbp) /* 如同main函数,test2内部也将调用一个子函数,为此将做相同的准备工作,包括: 1.将需要传递的参数从右向左依次存入对应的寄存器进行保存和传递; 2.使用callq指令,调用test1; 3.callq指令首先把返回地址push压栈,使得当函数返回时,可从该地址继续执行,随后jump至对应的内存地址(处于正文段)进行执行。 */ 400500: be 28 00 00 00 mov $0x28,%esi 400505: bf 1e 00 00 00 mov $0x1e,%edi 40050a: e8 c7 ff ff ff callq 4004d6 <test1> //从test1函数返回后,将返回值写入ax寄存器保存、传递 40050f: 89 45 fc mov %eax,-0x4(%rbp) 400512: 8b 45 fc mov -0x4(%rbp),%eax /*由于此时rsp下偏开辟新的栈空间,因此不能向test1中直接pop,需要使用leaveq命令; leaveq命令首先将rbp值复制给rsp,再pop,同样的都是将栈底寄存器值恢复成被调用函数栈底地址 */ 400515: c9 leaveq //将栈顶的返回地址弹出到eip,然后按对应指令继续运行 400516: c3 retq 0000000000400517 <main>: 400517: 55 push %rbp //首先把当前栈底压入栈中,即将当前栈底指向的内存地址保存下来 //下面两句通过使改变rsp、rbp寄存器值来使栈顶和栈底指向不同的内存地址,相当于开辟了一段栈空间供调用函数使用,具体开辟多大,在编译时就已经计算完毕。 400518: 48 89 e5 mov %rsp,%rbp //把rsp所存储的栈顶地址赋值给rbp寄存器 40051b: 48 83 ec 20 sub $0x20,%rsp //rsp存储的内存地址向下偏移0x20 //下面两句将main函数的两个入参分别写入内存,进行保存 40051f: 89 7d ec mov %edi,-0x14(%rbp) //把edi寄存器值放入rbp下偏0x14的内存地址中 400522: 48 89 75 e0 mov %rsi,-0x20(%rbp) //把rsi寄存器值放入rbp下偏0x20的内存地址中 //将函数内部定义的局部变量依次写入内存进行保存 400526: c7 45 f0 02 00 00 00 movl $0x2,-0x10(%rbp) 40052d: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%rbp) 400534: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp) /* 可以看到main函数即将调用子函数test2,为此将做一些准备工作,包括: 1.将需要传递的参数从右向左依次存入对应的寄存器进行保存和传递; 2.使用callq指令,调用test2; 3.callq指令首先把返回地址push压栈,使得当函数返回时,可从该地址继续执行,随后jump至对应的内存地址(处于正文段)进行执行。 */ 40053b: be 14 00 00 00 mov $0x14,%esi 400540: bf 0a 00 00 00 mov $0xa,%edi 400545: e8 a6 ff ff ff callq 4004f0 <test2> //从test2函数返回后,将存储在ax中的被调用函数返回值写入内存保存 40054a: 89 45 fc mov %eax,-0x4(%rbp) //main函数返回值写入ax寄存器 40054d: 8b 45 fc mov -0x4(%rbp),%eax //下面与test2中同理 400550: c9 leaveq 400551: c3 retq 400552: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 400559: 00 00 00 40055c: 0f 1f 40 00 nopl 0x0(%rax)
相关文章推荐
- X86架构上函数调用过程的堆栈
- ARM架构下函数调用过程分析
- X86架构上函数调用过程的堆栈
- 【应聘笔记系列】堆栈、栈帧与函数调用过程分析
- 第26讲 js函数调用过程内存分析 js函数细节
- X86_64架构下的LINUX缓冲区溢出栈分析
- 分析linux下的进程地址空间,以及c语言的函数调用过程
- Windows系统调用架构分析—也谈KiFastCallEntry函数地址的获取
- 分析fork函数对应的系统调用处理过程
- 堆栈、栈帧与函数调用过程分析
- 函数调用过程再分析
- 函数调用过程内存堆栈变化分析
- Windows系统调用架构分析—也谈KiFastCallEntry函数地址的获取
- 从汇编的角度分析函数调用过程(2)
- 堆栈、栈帧与函数调用过程分析
- 汇编代码分析----函数的调用堆栈过程(进程内核栈的切换过程)
- Windows系统调用架构分析—也谈KiFastCallEntry函数地址的获取
- c函数调用过程原理及函数栈帧分析
- c语言内部(汇编代码分析)函数调用过程探究