我机器的版本
FreeBSD fb7.arraynetworks.com.cn 7.0-RELEASE FreeBSD 7.0-RELEASE #0: Sun Feb 24 10:35:36 UTC 2008     [email protected]:/usr/obj/usr/src/sys/GENERIC  amd64
NASM version 2.10.07 compiled on Apr 11 2013

  1. section .data
  2. message:
  3.     db 'hello, world!', 0
  4. section .text
  5.     global _start
  6.         _start:
  7.         mov rax, 4
  8.         mov rdi, 1
  9.         mov rsi, message
  10.         mov rdx, 13
  11.         syscall
  12.         mov rax, 1
  13.         xor rdi, rdi
  14.         syscall
如果需要解释的话,_start相当于函数的入口
rax传递的是调用的syscall id
rdi第一个参数,fd
rsi第二个参数,字符串
rdx第三个参数,字符串长度。
这个是x86-64的约定,参数依次为RDI, RSI, RDX, RCX, R8, R9.如果再有就需要用栈了。
写一个C代码,反汇编看看也能得到上面的结论。
  1. long add(long a, int b, int c, int d, int e, int f, int g, int h, int i, int j, long k){
  2.     long x = a + b + c + d + e + f + g;
  3.     x = x + g + h + i +j + k;
  4.     return x;
  5. }

  6. int main(){
  7.     long x = add(0xfffffffff2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
  8.     return 0x11;
  9. }

反汇编会得到如下的结果
  1. (gdb) disassemble main
  2. Dump of assembler code for function main:
  3. 0x0000000000400600 : push rbp
  4. 0x0000000000400601 : mov rbp,rsp
  5. 0x0000000000400604 : sub rsp,0x40
  6. 0x0000000000400608 : mov esi,0x8
  7. 0x000000000040060d : mov edi,0x400700
  8. 0x0000000000400612 : mov eax,0x0
  9. 0x0000000000400617 : call 0x40042c
  10. 0x000000000040061c : mov DWORD PTR [rsp+32],0xc
  11. 0x0000000000400625 : mov DWORD PTR [rsp+24],0xb
  12. 0x000000000040062d : mov DWORD PTR [rsp+16],0xa
  13. 0x0000000000400635 : mov DWORD PTR [rsp+8],0x9
  14. 0x000000000040063d : mov DWORD PTR [rsp],0x8
  15. 0x0000000000400644 : mov r9d,0x7
  16. 0x000000000040064a : mov r8d,0x6
  17. 0x0000000000400650 : mov ecx,0x5
  18. 0x0000000000400655 : mov edx,0x4
  19. 0x000000000040065a : mov esi,0x3           <----32 bit
  20. 0x000000000040065f : mov rdi,0xfffffffff2  <----64bit register
  21. 0x0000000000400669 : call 0x400570
  22. 0x000000000040066e : mov DWORD PTR [rbp-8],rax
  23. 0x0000000000400672 : mov eax,0x11
1方面能看出寄存器当参数的顺序,另一方面,居然能用32位就用32位,装不下才需要64位,不知道为啥。

另外一个需要注意的问题是,x86-64上,栈是16字节对齐的,比如如果没有参数需要从栈上传递的话,尽管返回指令压栈只占用8个字节,
但实际上,也会占用16个字节,比如下面的C代码
  1. long add(long a, int b, int c){
  2.         long x = a + b + c;
  3.         return x;
  4. }

  5. void print(){
  6. }
  7. int main(){
  8.         long x = add(0xfffffffff2, 3, 4);
  9.         print();
  10.         return 0x11;
  11. }
反汇编得到的结果如下
  1. (gdb) disassemble main
  2. Dump of assembler code for function main:
  3. 0x0000000000400570 : push rbp
  4. 0x0000000000400571 : mov rbp,rsp
  5. 0x0000000000400574 : sub rsp,0x10
  6. 0x0000000000400578 : mov edx,0x4
  7. 0x000000000040057d : mov esi,0x3
  8. 0x0000000000400582 : mov rdi,0xfffffffff2
  9. 0x000000000040058c : call 0x400530
  10. 0x0000000000400591 : mov DWORD PTR [rbp-8],rax
  11. 0x0000000000400595 : mov eax,0x0
  12. 0x000000000040059a : call 0x400560
  13. 0x000000000040059f : mov eax,0x11
  14. 0x00000000004005a4 : leave
  15. 0x00000000004005a5 : ret
  16. 0x00000000004005a6 : nop
  17. 0x00000000004005a7 : nop
  18. 0x00000000004005a8 : nop
  19. 0x00000000004005a9 : nop
  20. 0x00000000004005aa : nop
  21. 0x00000000004005ab : nop
  22. 0x00000000004005ac : nop
  23. 0x00000000004005ad : nop
  24. 0x00000000004005ae : nop
  25. 0x00000000004005af : nop
执行call add指令之前后的rsp变化如下

  1. 0x000000000040058c in main ()
  2. (gdb) info reg rsp
  3. rsp 0x7fffffffeb00 0x7fffffffeb00
  4. (gdb) nexti
  5. Breakpoint 2, 0x0000000000400534 in add ()
  6. (gdb) info reg rsp
  7. rsp 0x7fffffffeaf0 0x7fffffffeaf0
  8. (gdb) x /3xg 0x7fffffffeaf0
  9. 0x7fffffffeaf0: 0x00007fffffffeb10 0x0000000000400591
  10. 0x7fffffffeb00: 0x0000000000000001
  11. (gdb)
可以看出栈顶变化了0x10也就是16个字节,其中,第一个进栈的是0x000....1,后一个进栈是是400591,这个是add函数返回后继续执行的指令。
愿意的话可以自己试一下另外一中情况,比如:
  1. long add(long a, int b, int c, int d, int e, int f, int g){
  2.         long x = a + b + c + d + e + f + g;
  3.         return x;
  4. }

  5. int main(){
  6.         long x = add(0xfffffffff2, 3, 4, 5, 6, 7, 8);
  7.         return 0x11;
  8. }
反汇编代码
  1. 0x0000000000400590 <main+0>: push rbp
  2. 0x0000000000400591 <main+1>: mov rbp,rsp
  3. 0x0000000000400594 <main+4>: sub rsp,0x18
  4. 0x0000000000400598 <main+8>: mov DWORD PTR [rsp],0x8
  5. 0x000000000040059f <main+15>: mov r9d,0x7
  6. 0x00000000004005a5 <main+21>: mov r8d,0x6
  7. 0x00000000004005ab <main+27>: mov ecx,0x5
  8. 0x00000000004005b0 <main+32>: mov edx,0x4
  9. 0x00000000004005b5 <main+37>: mov esi,0x3
  10. 0x00000000004005ba <main+42>: mov rdi,0xfffffffff2
  11. 0x00000000004005c4 <main+52>: call 0x400530 <add>
  12. 0x00000000004005c9 <main+57>: mov DWORD PTR [rbp-8],rax
  13. 0x00000000004005cd <main+61>: mov eax,0x11
  14. 0x00000000004005d2 <main+66>: leave
  15. 0x00000000004005d3 <main+67>: ret
多了一个参数,寄存器放不下,只能放到栈上。这时候看call add前后的rsp寄存器变化

  1. (gdb) nexti
  2. 0x00000000004005c4 in main ()
  3. (gdb) info reg rsp
  4. rsp 0x7fffffffeaf8 0x7fffffffeaf8
  5. (gdb) nexti
  6. Breakpoint 2, 0x0000000000400534 in add ()
  7. (gdb) info reg rsp
  8. rsp 0x7fffffffeae8 0x7fffffffeae8
  9. (gdb) x /3xg 0x7fffffffeae8
  10. 0x7fffffffeae8: 0x00007fffffffeb10 0x00000000004005c9
  11. 0x7fffffffeaf8: 0x0000000000000008
  12. (gdb)
虽然栈顶也变化了0x10,但是依次是参数8和下一个指令的地址4005c9。










09-26 20:11