X86_64平臺Linux環境下可變參數列表中浮點參數的傳遞

聲明:轉載請註明原連接http://my.oschina.net/u/1167407/blog/486311函數

寫了一個彙編函數調用printf用來打印字符串以下測試

#文件名myprint.s
.section .text
.global myprint
.type myprint,@function
myprint:#只接受一個字符串(的地址)做爲參數
  pushq %rbp
  movq %rsp,%rbp
  call printf
  leave
  ret

用C語言調用上邊的彙編函數:spa

//文件名helloworld.c
int main(){
  myprint("Hello World!\n");
  return 0;
}

用gcc myprint.s helloworld.c -o helloworld編譯獲得可執行程序.net

main函數編譯出的代碼以下:調試

000000000040054d <main>:
  40054d:       55                      push   %rbp
  40054e:       48 89 e5                mov    %rsp,%rbp
  400551:       bf 04 06 40 00          mov    $0x400604,%edi
  400556:       b8 00 00 00 00          mov    $0x0,%eax
  40055b:       e8 07 00 00 00          callq  400567 <myprint>
  400560:       b8 00 00 00 00          mov    $0x0,%eax
  400565:       5d                      pop    %rbp
  400566:       c3                      retq

後來想起了C語言的語法,以爲仍是加上個extern語句聲明咱們在外部定義了一個myprint函數,比較合適。因而C源程序變爲:code

extern int myprint(char *);

int main(){
  myprint("Hello World!\n");
  return 0;
}

再次編譯,main函數部分以下:blog

000000000040054d <main>:
  40054d:       55                      push   %rbp
  40054e:       48 89 e5                mov    %rsp,%rbp
  400551:       bf f4 05 40 00          mov    $0x4005f4,%edi
  400556:       e8 07 00 00 00          callq  400562 <myprint>
  40055b:       b8 00 00 00 00          mov    $0x0,%eax
  400560:       5d                      pop    %rbp
  400561:       c3                      retq

     比較兩次編譯後的main函數,發現沒加extern語句時編譯出的代碼多出了一句mov $0x0,%eax,當時不明就裏。ip

      後來有一次在運行某個程序時遇到了段錯誤,用GDB調試時發現是在執行printf函數時發生的,不明白緣由的我因而就去查看printf的彙編代碼。發現printf函數在%al爲0時,不會操做%xmm寄存器(下面的反彙編代碼說明了這一現象,test %al,%al測試%al是否爲0,je 0x7ffff7a946b3 <printf+99>這一語句代表,若%al爲0則跳轉到<printf+99>處執行,恰好將對%xmm寄存器的操做略去)字符串

Dump of assembler code for function printf:
   0x00007ffff7a94650 <+0>:     sub    $0xd8,%rsp
   0x00007ffff7a94657 <+7>:     test   %al,%al
   0x00007ffff7a94659 <+9>:     mov    %rsi,0x28(%rsp)
   0x00007ffff7a9465e <+14>:    mov    %rdx,0x30(%rsp)
   0x00007ffff7a94663 <+19>:    mov    %rcx,0x38(%rsp)
   0x00007ffff7a94668 <+24>:    mov    %r8,0x40(%rsp)
   0x00007ffff7a9466d <+29>:    mov    %r9,0x48(%rsp)
   0x00007ffff7a94672 <+34>:    je     0x7ffff7a946b3 <printf+99>
   0x00007ffff7a94674 <+36>:    vmovaps %xmm0,0x50(%rsp)
   0x00007ffff7a9467a <+42>:    vmovaps %xmm1,0x60(%rsp)
   0x00007ffff7a94680 <+48>:    vmovaps %xmm2,0x70(%rsp)
   0x00007ffff7a94686 <+54>:    vmovaps %xmm3,0x80(%rsp)
   0x00007ffff7a9468f <+63>:    vmovaps %xmm4,0x90(%rsp)
   0x00007ffff7a94698 <+72>:    vmovaps %xmm5,0xa0(%rsp)
   0x00007ffff7a946a1 <+81>:    vmovaps %xmm6,0xb0(%rsp)
   0x00007ffff7a946aa <+90>:    vmovaps %xmm7,0xc0(%rsp)
   0x00007ffff7a946b3 <+99>:    lea    0x8(%rsp),%rdx
   0x00007ffff7a946b8 <+104>:   mov    %rdi,%rsi
   0x00007ffff7a946bb <+107>:   movl   $0x8,0x8(%rsp)
   0x00007ffff7a946c3 <+115>:   lea    0xe0(%rsp),%rax
   0x00007ffff7a946cb <+123>:   movl   $0x30,0xc(%rsp)
   0x00007ffff7a946d3 <+131>:   mov    %rax,0x10(%rsp)
   0x00007ffff7a946d8 <+136>:   lea    0x20(%rsp),%rax
   0x00007ffff7a946dd <+141>:   mov    %rax,0x18(%rsp)
   0x00007ffff7a946e2 <+146>:   mov    0x341857(%rip),%rax        # 0x7ffff7dd5f40
   0x00007ffff7a946e9 <+153>:   mov    (%rax),%rdi
   0x00007ffff7a946ec <+156>:   callq  0x7ffff7a89af0 <vfprintf>
   0x00007ffff7a946f1 <+161>:   add    $0xd8,%rsp
   0x00007ffff7a946f8 <+168>:   retq   
End of assembler dump.

因爲%xmm寄存器和浮點操做有關,聯想到可能和浮點參數有關。因而就寫一個浮點操做的函數測試一下:get

double foo(double a,double b){
  return a+b;
}

int main(){
  foo(1.0,2.0);
  return 0;
}

編譯出的foo和main函數彙編代碼以下:

00000000004004fd <foo>:
  4004fd:       55                      push   %rbp
  4004fe:       48 89 e5                mov    %rsp,%rbp
  400501:       f2 0f 11 45 f8          movsd  %xmm0,-0x8(%rbp)
  400506:       f2 0f 11 4d f0          movsd  %xmm1,-0x10(%rbp)
  40050b:       f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
  400510:       f2 0f 58 45 f0          addsd  -0x10(%rbp),%xmm0
  400515:       f2 0f 11 45 e8          movsd  %xmm0,-0x18(%rbp)
  40051a:       48 8b 45 e8             mov    -0x18(%rbp),%rax
  40051e:       48 89 45 e8             mov    %rax,-0x18(%rbp)
  400522:       f2 0f 10 45 e8          movsd  -0x18(%rbp),%xmm0
  400527:       5d                      pop    %rbp
  400528:       c3                      retq   

0000000000400529 <main>:
  400529:       55                      push   %rbp
  40052a:       48 89 e5                mov    %rsp,%rbp
  40052d:       48 83 ec 08             sub    $0x8,%rsp
  400531:       48 b8 00 00 00 00 00    movabs $0x4000000000000000,%rax
  400538:       00 00 40 
  40053b:       48 89 45 f8             mov    %rax,-0x8(%rbp)
  40053f:       f2 0f 10 4d f8          movsd  -0x8(%rbp),%xmm1
  400544:       f2 0f 10 05 9c 00 00    movsd  0x9c(%rip),%xmm0        # 4005e8 <_IO_stdin_used+0x8>
  40054b:       00 
  40054c:       e8 ac ff ff ff          callq  4004fd <foo>
  400551:       b8 00 00 00 00          mov    $0x0,%eax
  400556:       c9                      leaveq 
  400557:       c3                      retq   
  400558:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40055f:       00

彷佛在callq以前也沒有給%eax賦值啊,難道和浮點數沒有關係?再一想printf的特色是接受變長參數列表,因而將foo函數也改爲接受變長參數列表的以下:

00000000004004fd <foo>:
  4004fd:       55                      push   %rbp
  4004fe:       48 89 e5                mov    %rsp,%rbp
  400501:       48 83 ec 50             sub    $0x50,%rsp
  400505:       f2 0f 11 85 48 ff ff    movsd  %xmm0,-0xb8(%rbp)
  40050c:       ff 
  40050d:       48 89 bd 50 ff ff ff    mov    %rdi,-0xb0(%rbp)
  400514:       48 89 b5 58 ff ff ff    mov    %rsi,-0xa8(%rbp)
  40051b:       48 89 95 60 ff ff ff    mov    %rdx,-0xa0(%rbp)
  400522:       48 89 8d 68 ff ff ff    mov    %rcx,-0x98(%rbp)
  400529:       4c 89 85 70 ff ff ff    mov    %r8,-0x90(%rbp)
  400530:       4c 89 8d 78 ff ff ff    mov    %r9,-0x88(%rbp)
  400537:       84 c0                   test   %al,%al
  400539:       74 18                   je     400553 <foo+0x56>
  40053b:       0f 29 55 a0             movaps %xmm2,-0x60(%rbp)
  40053f:       0f 29 5d b0             movaps %xmm3,-0x50(%rbp)
  400543:       0f 29 65 c0             movaps %xmm4,-0x40(%rbp)
  400547:       0f 29 6d d0             movaps %xmm5,-0x30(%rbp)
  40054b:       0f 29 75 e0             movaps %xmm6,-0x20(%rbp)
  40054f:       0f 29 7d f0             movaps %xmm7,-0x10(%rbp)
  400553:       f2 0f 11 8d 40 ff ff    movsd  %xmm1,-0xc0(%rbp)
  40055a:       ff 
  40055b:       f2 0f 10 85 48 ff ff    movsd  -0xb8(%rbp),%xmm0
  400562:       ff 
  400563:       f2 0f 58 85 40 ff ff    addsd  -0xc0(%rbp),%xmm0
  40056a:       ff 
  40056b:       f2 0f 11 85 38 ff ff    movsd  %xmm0,-0xc8(%rbp)
  400572:       ff 
  400573:       48 8b 85 38 ff ff ff    mov    -0xc8(%rbp),%rax
  40057a:       48 89 85 38 ff ff ff    mov    %rax,-0xc8(%rbp)
  400581:       f2 0f 10 85 38 ff ff    movsd  -0xc8(%rbp),%xmm0
  400588:       ff 
  400589:       c9                      leaveq 
  40058a:       c3                      retq   

000000000040058b <main>:
  40058b:       55                      push   %rbp
  40058c:       48 89 e5                mov    %rsp,%rbp
  40058f:       48 83 ec 10             sub    $0x10,%rsp
  400593:       48 b8 00 00 00 00 00    movabs $0x4000000000000000,%rax
  40059a:       00 00 40 
  40059d:       48 89 45 f8             mov    %rax,-0x8(%rbp)
  4005a1:       f2 0f 10 4d f8          movsd  -0x8(%rbp),%xmm1
  4005a6:       f2 0f 10 05 9a 00 00    movsd  0x9a(%rip),%xmm0        # 400648 <_IO_stdin_used+0x8>
  4005ad:       00 
  4005ae:       b8 02 00 00 00          mov    $0x2,%eax
  4005b3:       e8 45 ff ff ff          callq  4004fd <foo>
  4005b8:       b8 00 00 00 00          mov    $0x0,%eax
  4005bd:       c9                      leaveq 
  4005be:       c3                      retq   
  4005bf:       90                      nop

這下在callq 4004fd <foo>以前有了對%eax賦值的mov  $0x2,%eax語句,並且彷佛這個%eax的值還和浮點數個數有關。再試一下改變傳給foo的參數列表

double foo(double a,double b,...){
  return a+b;
}

int main(){
  foo(1.0,2.0,3.0,4.0,5.0,6.0,7.0,10);
  return 0;
}

這下main函數部分對應的代碼變成了下面的形式

000000000040058b <main>:
  40058b:       55                      push   %rbp
  40058c:       48 89 e5                mov    %rsp,%rbp
  40058f:       48 83 ec 10             sub    $0x10,%rsp
  400593:       49 b9 00 00 00 00 00    movabs $0x401c000000000000,%r9
  40059a:       00 1c 40 
  40059d:       49 b8 00 00 00 00 00    movabs $0x4018000000000000,%r8
  4005a4:       00 18 40 
  4005a7:       48 be 00 00 00 00 00    movabs $0x4014000000000000,%rsi
  4005ae:       00 14 40 
  4005b1:       48 b9 00 00 00 00 00    movabs $0x4010000000000000,%rcx
  4005b8:       00 10 40 
  4005bb:       48 ba 00 00 00 00 00    movabs $0x4008000000000000,%rdx
  4005c2:       00 08 40 
  4005c5:       48 b8 00 00 00 00 00    movabs $0x4000000000000000,%rax
  4005cc:       00 00 40 
  4005cf:       bf 0a 00 00 00          mov    $0xa,%edi
  4005d4:       4c 89 4d f8             mov    %r9,-0x8(%rbp)
  4005d8:       f2 0f 10 75 f8          movsd  -0x8(%rbp),%xmm6
  4005dd:       4c 89 45 f8             mov    %r8,-0x8(%rbp)
  4005e1:       f2 0f 10 6d f8          movsd  -0x8(%rbp),%xmm5
  4005e6:       48 89 75 f8             mov    %rsi,-0x8(%rbp)
  4005ea:       f2 0f 10 65 f8          movsd  -0x8(%rbp),%xmm4
  4005ef:       48 89 4d f8             mov    %rcx,-0x8(%rbp)
  4005f3:       f2 0f 10 5d f8          movsd  -0x8(%rbp),%xmm3
  4005f8:       48 89 55 f8             mov    %rdx,-0x8(%rbp)
  4005fc:       f2 0f 10 55 f8          movsd  -0x8(%rbp),%xmm2
  400601:       48 89 45 f8             mov    %rax,-0x8(%rbp)
  400605:       f2 0f 10 4d f8          movsd  -0x8(%rbp),%xmm1
  40060a:       f2 0f 10 05 a6 00 00    movsd  0xa6(%rip),%xmm0        # 4006b8 <_IO_stdin_used+0x8>
  400611:       00 
  400612:       b8 07 00 00 00          mov    $0x7,%eax
  400617:       e8 e1 fe ff ff          callq  4004fd <foo>
  40061c:       b8 00 00 00 00          mov    $0x0,%eax
  400621:       c9                      leaveq 
  400622:       c3                      retq   
  400623:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40062a:       00 00 00 
  40062d:       0f 1f 00                nopl   (%rax)

       能夠看到向foo函數傳遞了7個浮點數和一個整數,在callq 4004fd <foo>前變成了mov $0x7,%eax,恰好是參數列表中浮點數的個數。咱們還能夠試驗浮點參數多於8個的狀況,以及foo的默認參數沒有浮點數時編譯出的foo函數的代碼等

 

由此咱們獲得瞭如下的猜測和結論

在X86_64平臺Linux環境下有

猜測:對於沒有用extern聲明的外部函數GCC都將其看作可變參數列表的函數

結論1:浮點數是用%xmm0~%xmm7總共8寄存器傳遞的,多過8個的那部分浮點參數則壓到棧上

結論2:對於可變參數列表的函數,GCC使用%eax來表示參數列表中是否有浮點數,並且頗有多是用於浮點數的個數的計數(因爲用於傳遞參數的%xmm寄存器一共只有8個,故參數列表中多過8個浮點數則%eax直接賦8)

相關文章
相關標籤/搜索