上一篇介绍了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. CentOS6.5下搭建FTP服务

    一.FTP协议 FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一.FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端.其中FT ...

  2. gulp 自动化管理工具实现全过程

    1.全局安装gulp npm install gulp -g 2.项目内安装gulp npm install gulp -s 3.项目根目录新建gulpfile.js js内代码: //载入gulp核 ...

  3. 关于javascript闭包的最通俗易懂的理解

    这两天在研究闭包,网上一通找,有牛人写的帖子,有普通人写的帖子,但是大多没戳中本小白所纠结的点,而且大多插入了立即执行函数,其实根本不需要的,反而让人产生了误解.这里我用我的方式讲解一下闭包. 1.目 ...

  4. Kafka消费者 从Kafka中读取数据并写入文件

    Kafka消费者 从Kafka中读取数据 最近有需求要从kafak上消费读取实时数据,并将数据中的key输出到文件中,用于发布端的原始点进行比对,以此来确定是否传输过程中有遗漏数据. 不废话,直接上代 ...

  5. java入门到秃路线导航,元芳你怎么看?【教学视频+博客+书籍整理】

    目录 一.Java基础 二.关于JavaWeb基础 三.关于数据库 四.关于ssm框架 五.关于数据结构与算法 六.关于开发工具idea 七.关于项目管理工具Mawen.Git.SVN.Gradle. ...

  6. 怎么在.NetCore3.0 中使用Log4net 写日志 及读取配置文件的信息

    1:安装Log4Net的 NuGet 包: 我们通常之需要安装这一个包即可,其他的主包会自动被添加进来: insatll-package  Microsoft.Extensions.Logging.L ...

  7. ZTUnity Profiler概述及Profiler window 说明

    转贴链接:https://www.jianshu.com/p/ca2ee8a51754

  8. go map数据结构和源码详解

    目录 1. 前言 2. go map的数据结构 2.1 核心结体体 2.2 数据结构图 3. go map的常用操作 3.1 创建 3.2 插入或更新 3.3 删除 3.4 查找 3.5 range迭 ...

  9. SpringBoot整合MybatisPlus3.X之SQL注入器(九)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

  10. Flask源码分析二:路由内部实现原理

    前言 Flask是目前为止我最喜欢的一个Python Web框架了,为了更好的掌握其内部实现机制,这两天准备学习下Flask的源码,将由浅入深跟大家分享下,其中Flask版本为1.1.1. 上次了解了 ...