• gcc在x64体系中如何传递参数,linux,mac,iOS适用


    上一篇介绍了vc(windows)平台在x64体系当中,c函数的传参方式。本篇将要介绍gcc(类linux,mac)平台在x64中,c函数是如何传参的。
    为节约时间和篇幅,首先来定义一个有十个参数的函数,参数类型包罗了内嵌类型:

    int foo(char c, short s, int i, long l, long long ll, char* p, // 前6个参数,注意我的划分和参数类型
          void** pp, float f, void* x, double d);

    反汇编调用

    foo('c', 0, 1, 2, 3, (char*)0x4, (void**)0x5, 1.f, (void*)6, 2.f);
    0x000000000040067b <+55>:    movsd  0x2d5(%rip),%xmm0        # 0x400958 <__dso_handle+8>    double d = 2.f
       0x0000000000400683 <+63>:    movq   $0x6,0x8(%rsp)        # (void*) 6
       0x000000000040068c <+72>:    movq   $0x5,(%rsp)        # (void**) 0x5
       0x0000000000400694 <+80>:    movapd %xmm0,%xmm1
       0x0000000000400698 <+84>:    movss  0x2c0(%rip),%xmm0        # 0x400960 <__dso_handle+16>    float f = 1.f
       0x00000000004006a0 <+92>:    mov    $0x4,%r9d        # (char*) 0x4
       0x00000000004006a6 <+98>:    mov    $0x3,%r8d        # (long long) 3
    0x00000000004006ac <+104>:    mov    $0x2,%ecx        # (long) 2
       0x00000000004006b1 <+109>:    mov    $0x1,%edx        # (int) 1
       0x00000000004006b6 <+114>:    mov    $0x0,%esi        # (short) 0
       0x00000000004006bb <+119>:    mov    $0x63,%edi        # (char) 'c'
       0x00000000004006c0 <+124>:    callq  0x4005c4 <_Z3foocsilxPcPPvfS0_d>

    可以看到数据类型分两类,浮点和非浮点型。我传的实参数也是按这两类划分递增的。
    非浮点参数分别是 'c', 0, 1, 2, 3, (char*)0x4, (void**)0x5, (void*)6。先将前6个优先按顺序按排到rdi,rsi,rdx,rcx,r8和r9。剩下(void**)5,(void*)6。
    浮点参数分别是 1.f, 2.f。 按顺序安排到xmm0,xmm1。
    最后将两种类型不能放入寄存器的剩余参数,由右向左依次入栈。

    下面再定义一个超级无敌多参数的函数,用尽全部传参寄存器,印证我上面的分析。

    int foo2(char c, short s, int i, long l, long long ll, char* p, 
            void** pp, float f, void* x, double d,                     // 至此和上面foo定义一样
            float xmm2, float xmm3, float xmm4, float xmm5, float xmm6, float xmm7,     // 追加6个浮点型用尽余下的寄存器
            float xmmUnknow);

    反汇编调用

    foo2('c', 0, 1, 2, 3, (char*)4, (void**)5, 1.f, (void*)6, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, (float)i);
    0x00000000004006c5 <+129>:    cvtsi2ssl -0xc(%rbp),%xmm0
       0x00000000004006ca <+134>:    movsd  0x286(%rip),%xmm1        # 0x400958 <__dso_handle+8>
       0x00000000004006d2 <+142>:    movss  %xmm0,0x10(%rsp)        # *** 最尾的浮点型只被放入堆栈中
       0x00000000004006d8 <+148>:    movq   $0x6,0x8(%rsp)        # *** 和foo一样 
       0x00000000004006e1 <+157>:    movq   $0x5,(%rsp)        # *** 和foo一样
       0x00000000004006e9 <+165>:    movss  0x273(%rip),%xmm7        # 0x400964 <__dso_handle+20>
       0x00000000004006f1 <+173>:    movss  0x26f(%rip),%xmm6        # 0x400968 <__dso_handle+24>
       0x00000000004006f9 <+181>:    movss  0x26b(%rip),%xmm5        # 0x40096c <__dso_handle+28>
       0x0000000000400701 <+189>:    movss  0x267(%rip),%xmm4        # 0x400970 <__dso_handle+32>
       0x0000000000400709 <+197>:    movss  0x263(%rip),%xmm3        # 0x400974 <__dso_handle+36>
       0x0000000000400711 <+205>:    movss  0x25f(%rip),%xmm2        # 0x400978 <__dso_handle+40>
    0x0000000000400719 <+213>:    movss  0x23f(%rip),%xmm0        # 0x400960 <__dso_handle+16>
       0x0000000000400721 <+221>:    mov    $0x4,%r9d
       0x0000000000400727 <+227>:    mov    $0x3,%r8d
       0x000000000040072d <+233>:    mov    $0x2,%ecx
       0x0000000000400732 <+238>:    mov    $0x1,%edx
       0x0000000000400737 <+243>:    mov    $0x0,%esi
       0x000000000040073c <+248>:    mov    $0x63,%edi
       0x0000000000400741 <+253>:    callq  0x4005f5 <_Z4foo2csilxPcPPvfS0_dfffffff>

    非浮点参数分别是 'c', 0, 1, 2, 3, (char*)0x4, (void**)0x5, (void*)6。先将前6个优先按顺序按排到rdi,rsi,rdx,rcx,r8和r9。剩下(void**)5,(void*)6。
    浮点参数分别是 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, (float)i。 按顺序安排到xmm0-xmm7,剩下(float)i。
    最后将两种类型不能放入寄存器的剩余参数,分别是(void**)5,(void*)6,(float)i,由右向左依次入栈。

    最后我选取一个特例来作为本篇结束,gcc如何传递临时对象。

    struct point {float x,y;};
    struct obj
    {
            int i;
            float f[8];
            void foo(point pt)
            {
                    f[2] += pt.x;
                    f[3] *= pt.y;
            }
    };

    反汇编调用

    obj j;
     point pt;
     j.foo(pt);
    0x000000000040078d <+329>:    movq   -0x20(%rbp),%xmm0    
       0x0000000000400792 <+334>:    lea    -0x50(%rbp),%rax
       0x0000000000400796 <+338>:    mov    %rax,%rdi
       0x0000000000400799 <+341>:    callq  0x400814 <_ZN3obj3fooE5point>

    rdi是什么大家都清楚,剩下另一个参数载体就是xmm0了。再看一看函数定义,参数是个临时对象,再看对象定义,point结构体是两个单精浮点,共占64位。而xmm寄存器可以存放4个单精浮点数据。
    下面再看成员函数foo的反汇编刚好印证了。

    Dump of assembler code for function _ZN3obj3fooE5point:
       0x0000000000400814 <+0>:    push   %rbp
       0x0000000000400815 <+1>:    mov    %rsp,%rbp
       0x0000000000400818 <+4>:    mov    %rdi,-0x8(%rbp)
       0x000000000040081c <+8>:    movq   %xmm0,-0x10(%rbp)    # 低64位存放了临时对象
       0x0000000000400821 <+13>:    mov    -0x8(%rbp),%rax
       0x0000000000400825 <+17>:    movss  0xc(%rax),%xmm1
       0x000000000040082a <+22>:    movss  -0x10(%rbp),%xmm0    # pt.x
       0x000000000040082f <+27>:    addss  %xmm1,%xmm0
       0x0000000000400833 <+31>:    mov    -0x8(%rbp),%rax
       0x0000000000400837 <+35>:    movss  %xmm0,0xc(%rax)
       0x000000000040083c <+40>:    mov    -0x8(%rbp),%rax
       0x0000000000400840 <+44>:    movss  0x10(%rax),%xmm1
       0x0000000000400845 <+49>:    movss  -0xc(%rbp),%xmm0        # pt.y
       0x000000000040084a <+54>:    mulss  %xmm1,%xmm0
       0x000000000040084e <+58>:    mov    -0x8(%rbp),%rax
       0x0000000000400852 <+62>:    movss  %xmm0,0x10(%rax)
       0x0000000000400857 <+67>:    leaveq 
       0x0000000000400858 <+68>:    retq   
    End of assembler dump.

    到此为止,我已经用了三篇来介绍x64体系三种常用平台在c/c++/objc编程的传参方式。
    上篇,通过lldb调试介绍mac平台下x64传参;
    中篇,通过windbg调试介绍windows平台下x64传参;
    下篇,通过gdb调试介绍gcc(类linux)平台下x64传参,本篇对于mac,ios同样适用。

    预告:后面将要进入反汇编分析objc程序。

  • 相关阅读:
    《Orange'S:一个操作系统的实现》与上一版之比较
    IPC
    末日帝国——Agile公司的困境 (2)
    取经学道真经验——你听过这么享受的培训吗
    数据库设计指南(五)数据库小技巧
    软件项目开发典型风险一览
    数据库设计指南(四)保证数据的完整性
    官网的Ext direct包中.NET版的问题
    软件项目开发应写的13类文档
    面试EJB常考题
  • 原文地址:https://www.cnblogs.com/bbqzsl/p/5084540.html
Copyright © 2020-2023  润新知