上一篇介绍了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', , , , , (char*)0x4, (void**)0x5, .f, (void*), .f);
0x000000000040067b <+>: movsd 0x2d5(%rip),%xmm0 # 0x400958 <__dso_handle+> double d = .f
0x0000000000400683 <+>: movq $0x6,0x8(%rsp) # (void*)
0x000000000040068c <+>: movq $0x5,(%rsp) # (void**) 0x5
0x0000000000400694 <+>: movapd %xmm0,%xmm1
0x0000000000400698 <+>: movss 0x2c0(%rip),%xmm0 # 0x400960 <__dso_handle+> float f = .f
0x00000000004006a0 <+>: mov $0x4,%r9d # (char*) 0x4
0x00000000004006a6 <+>: mov $0x3,%r8d # (long long)
0x00000000004006ac <+>: mov $0x2,%ecx # (long)
0x00000000004006b1 <+>: mov $0x1,%edx # (int)
0x00000000004006b6 <+>: mov $0x0,%esi # (short)
0x00000000004006bb <+>: mov $0x63,%edi # (char) 'c'
0x00000000004006c0 <+>: 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', , , , , (char*), (void**), .f, (void*), .f, .f, .f, .f, .f, .f, .f, (float)i);
0x00000000004006c5 <+>: cvtsi2ssl -0xc(%rbp),%xmm0
0x00000000004006ca <+>: movsd 0x286(%rip),%xmm1 # 0x400958 <__dso_handle+>
0x00000000004006d2 <+>: movss %xmm0,0x10(%rsp) # *** 最尾的浮点型只被放入堆栈中
0x00000000004006d8 <+>: movq $0x6,0x8(%rsp) # *** 和foo一样
0x00000000004006e1 <+>: movq $0x5,(%rsp) # *** 和foo一样
0x00000000004006e9 <+>: movss 0x273(%rip),%xmm7 # 0x400964 <__dso_handle+>
0x00000000004006f1 <+>: movss 0x26f(%rip),%xmm6 # 0x400968 <__dso_handle+>
0x00000000004006f9 <+>: movss 0x26b(%rip),%xmm5 # 0x40096c <__dso_handle+>
0x0000000000400701 <+>: movss 0x267(%rip),%xmm4 # 0x400970 <__dso_handle+>
0x0000000000400709 <+>: movss 0x263(%rip),%xmm3 # 0x400974 <__dso_handle+>
0x0000000000400711 <+>: movss 0x25f(%rip),%xmm2 # 0x400978 <__dso_handle+>
0x0000000000400719 <+>: movss 0x23f(%rip),%xmm0 # 0x400960 <__dso_handle+>
0x0000000000400721 <+>: mov $0x4,%r9d
0x0000000000400727 <+>: mov $0x3,%r8d
0x000000000040072d <+>: mov $0x2,%ecx
0x0000000000400732 <+>: mov $0x1,%edx
0x0000000000400737 <+>: mov $0x0,%esi
0x000000000040073c <+>: mov $0x63,%edi
0x0000000000400741 <+>: 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[];
void foo(point pt)
{
f[] += pt.x;
f[] *= pt.y;
}
};

反汇编调用

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

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

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

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

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

gcc在x64体系中如何传递参数,linux,mac,iOS适用的更多相关文章

  1. vc在x64体系的一般传参数方式

    前篇分析过在objc中函数调用传参的一般方式,本篇分析vc在x64体系中的一般传参方式.手头上因为没有64位的vc编译器,只好用windbg看ms自身的函数是怎么样调用的. 首先看两个再熟悉不过的ap ...

  2. 深入理解python中函数传递参数是值传递还是引用传递

    深入理解python中函数传递参数是值传递还是引用传递 目前网络上大部分博客的结论都是这样的: Python不允许程序员选择采用传值还是传 引用.Python参数传递采用的肯定是"传对象引用 ...

  3. Python中函数传递参数有四种形式

    Python中函数传递参数有四种形式 fun1(a,b,c) fun2(a=1,b=2,c=3) fun3(*args) fun4(**kargs) 四种中最常见是前两种,基本上一般点的教程都会涉及, ...

  4. 关于Delphi中多线程传递参数的简单问题

    http://bbs.csdn.net/topics/390513469/ unit uThread; interface uses Classes; type Th = class(TThread) ...

  5. JavaScript中url 传递参数(特殊字符)解决方法

    有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了.下表中列出了一些URL特殊符号及编码 十六进制值1. + URL 中+号表示空格 %2B2. 空格 UR ...

  6. vue项目webpack中Npm传递参数配置不同域名接口

    项目开发中,前端在配置后端api域名时很困扰,常常出现:本地开发环境: api-dev.demo.com测试环境: api-test.demo.com线上生产环境: api.demo.com, 这次是 ...

  7. 转载】JQuery中如何传递参数如click(),change()等具体实现

    转载地址:http://www.jb51.net/article/36249.htm 有个需求让两个select中option相互转换,这个作业就是给几个按钮添加click()事件接下来为大家介绍下如 ...

  8. vue.js中路由传递参数

    知识点:vue路由传递参数,第二个页面(A.B页面)拿到参数,使用参数 方法一:使用 <router-link :to="{name:'edithospital',params:{hi ...

  9. JavaScript中url 传递参数(特殊字符)解决方法及转码解码的介绍

    有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了.下表中列出了一些URL特殊符号及编码   十六进制值 1. + URL 中+号表示空格 %2B 2. 空 ...

随机推荐

  1. linux使用jq工具解析json

    jq类似一个awk或grep一样的神器,可以方便地在命令行操作json 这里我使用海南万宁的天气接口做演示,地址:http://t.weather.sojson.com/api/weather/cit ...

  2. php能在手机上运行吗

    php能在手机上运行吗? php能在手机上运行.但是需要你的手机具有PHP运行环境. 比如一款安卓手机上面的php运行环境软件:anmpp. ANMPP是Android NGINX MYSQL PGS ...

  3. ssd原理及代码实现详解

    通过https://github.com/amdegroot/ssd.pytorch,结合论文https://arxiv.org/abs/1512.02325来理解ssd. ssd由三部分组成: ba ...

  4. 页面报错常用状态码总结(Http常见状态码)

    作为一个互联网开发人员对于一些服务器返回的HTTP状态的意思都必须是了如指掌的,只有将这些状态码一一弄清楚,工作中遇到的各种问题才能够处理的得心应手.好了,下面就让我们来了解一下比较常见的HTTP状态 ...

  5. Redis(四)Jedis客户端

    一.客户端通信协议 二.Java客户端Jedis 1.获取Jedis Jedis属于Java的第三方开发包,在Java中获取第三方开发包通常有两种方式: 直接下载目标版本的Jedis-${versio ...

  6. django-模板之now标签(七)

    1.在settings.py中设置成中国时区 2.index.html 3.显示

  7. Web购物车案例(servlet+bean+jsp)

    一些B话 事情来得太突然,一个朋友说是要我帮忙做一个JavaWeb的课程设计,在过一天就要去答辩了- 这我哪受的了时间这么短还没有~~~,正准备委婉的拒绝的时候他说写完之后给我那个啥- 好吧,我就是这 ...

  8. Python中Linux开发的技巧

    Python的Linux基础目录 操作系统  Windows和Linux的区别  常用基本命令1.操作系统 1 操作系统的作用:向上支持应用软件的运行,向下控制硬件,软件和硬件的过渡层Linux的版本 ...

  9. Microsoft.Extensions.DependencyInjection 之三:展开测试

    目录 前文回顾 IServiceCallSite CallSiteFactory ServiceProviderEngine CompiledServiceProviderEngine Dynamic ...

  10. php从数据库获取数据并遍历在表格中

    <?php /*连接数据库并以一个数组的形式获得数据*/ header("Content-type:text/html;charset=UTF-8"); $con = mys ...