【软件开发底层知识修炼】二十四 ABI之函数调用约定

  • 上一篇文章学习了Linux环境下的函数栈帧的形成与摧毁。点击链接查看相关文章:软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁
  • 本篇文章继续学习ABI接口相关的内容。函数调用约定

文章目录

  • 1 函数参数如何入栈,返回值在哪里
  • 2 函数调用约定的编程实验
    • 2.1 使用gdb调试代码证明eax存的值是函数返回值
    • 2 .2 查看程序的反汇编文件来说明调用约定的不同
  • 3 总结

1 函数参数如何入栈,返回值在哪里

前面学过的文章中,已经深入的了解了函数调用过程中函数的栈帧的形成与摧毁。在发生函数调用时,首先入栈的是函数的参数。但是我们知道一般函数的参数都是会比较多。在参数比较多的时候,函数的参数是以什么样的顺序入栈的呢?函数返回时是谁来将参数弹出栈呢?

并且,在函数执行完之后,返回的时候,返回值在哪里?通过什么方式将返回值传递给调用者?

首先给出在C语言中默认的调用约定(cdecl

  • 调用函数时,参数从右往左入栈
  • 函数返回时,调用者负责将参数弹出栈。这里说是调用者,实际上是编译器在编译的时候为调用者添加了相应的指令
  • 函数返回值保存在eax寄存器中。前提是函数返回值是基础数据类型,如果是结构体这种的类型,就另说。

上面的调用约定是C语言默认的函数调用约定。我们平常所熟知的也就是上面的默认的调用约定。当然,或许大多数人是不知道的吧!

  • 下面的表格给出了其他各种调用约定
    在这里插入图片描述

注意:以上三个调用约定,只需要注意__thiscall__约定。它一般是C++中的成员函数的约定,C++成员函数又由隐藏的this指针。如果函数的参数是确定个数的,则this存放于ECX寄存器,函数自身清理栈中的参数。如果成员函数是可变参数,那么久相当于前面的__cdecl__调用约定

在上面的函数调用约定中,还有几点需要注意:

  • 只有使用的__cdecl__约定的函数,才支持可变参数。使用其他调用约定的,不支持可变参数
  • 在C++中,当类的成员函数为可变参数时,调用约定自动变为__cedcl__
  • 调用约定定义义了函数被编译后,对应的在符号表中的最终的符号名称的样子

2 函数调用约定的编程实验

2.1 使用gdb调试代码证明eax存的值是函数返回值

convention.c

#include <stdio.h>int test(int a, int b, int c) //默认的__cdecl__调用约定
{return a + b + c;
}
//__cdecl__调用约定
void __attribute__((__cdecl__)) func_1(int i)
{
}
//__stdcall__调用约定
void __attribute__((__stdcall__)) func_2(int i)
{
}
//__fastcall__调用约定
void __attribute__((__fastcall__)) func_3(int i)
{
}int main()
{int r = test(1, 2, 3);printf("r = %d\n", r);return 0;
}
  • 上述代码很简单,分别将几个函数强制设定为相应的调用约定,并可以通过查看变量r的内容与寄存器eax的内容来证明函数返回值是存储在eax寄存器中的。下面就来通过运行该程序,并使用GDB进行查看相应的内存的值。

编译运行程序,并且记性gdb调试

  • gcc -g convention.c -o test.out
  • gdb test.out
  • start
  • break convention.c:6
  • continue
  • info registers
  • continue

因为gdb在前面的文章中已经使用过很多次,并且使用了各种动态图展示gdb的使用。这里就直接给出相应的命令了。

上述第一个contiue后,运行到convention.c的第6行就停止了。此时再info registers。可以看到如下信息:
在这里插入图片描述

可以看到在函数test即将返回时。寄存器eax存的值为6 。 这正好等于test函数的返回值这不是巧合。因为eax寄存器就是用来保存函数的返回值的。当然,后面我们还可以通过查看反汇编文件来分析。

最后的执行continue命令导致整个程序的运行结束

2 .2 查看程序的反汇编文件来说明调用约定的不同

上面的gdb调试方法没有证明出函数调用约定的不同,下面我么使用查看可执行代码的反汇编文件进行说明。

使用命令

  • objdump -S test.c > test.s

生成可执行文件的反汇编代码test.s。

在该文件中可以找到

  • func_1函数的反汇编代码
void __attribute__((__cdecl__)) func_1(int i)
{80483d5:	55                   	push   %ebp80483d6:	89 e5                	mov    %esp,%ebp
}80483d8:	5d                   	pop    %ebp80483d9:	c3                   	ret 
  • func_2的反汇编代码
080483da <func_2>:void __attribute__((__stdcall__)) func_2(int i)
{80483da:	55                   	push   %ebp80483db:	89 e5                	mov    %esp,%ebp
}80483dd:	5d                   	pop    %ebp80483de:	c2 04 00             	ret    $0x4
  • func_3的反汇编代码
080483e1 <func_3>:void __attribute__((__fastcall__)) func_3(int i)
{80483e1:	55                   	push   %ebp80483e2:	89 e5                	mov    %esp,%ebp80483e4:	83 ec 04             	sub    $0x4,%esp80483e7:	89 4d fc             	mov    %ecx,-0x4(%ebp)
}80483ea:	c9                   	leave  80483eb:	c3                   	ret   
  • 从以上三个函数的反汇编代码可以看出,它们的前言和后序是由区别的,这是因为它们分别使用了不同的函数调用约定。使用了不同的 函数调用预定,汇编层面肯定是不一样的。

3 总结

学会了以下内容

  • 函数返回值如果是整形的,通过eax寄存器传递返回值给调用者(下一篇文章讲解返回值是结构体的类型个时是如何传递给调用者的)
  • 学会三种不同的调用约定对应的区别(__cdecl__调用约定,__stdcall__调用约定,__fastcall__调用约定)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入了解 Python 的 import 语句

在 Python 中&#xff0c;import 语句是一个关键的功能&#xff0c;用于在程序中引入模块和包。本文将深入讨论 import 语句的各种用法、注意事项以及一些高级技巧&#xff0c;以帮助你更好地理解和使用这一功能。 概念介绍 package 通常对应一个文件夹&#xff0c;下面可以有…

javascript 获取上一页的url

在js中&#xff0c;如果通过连接或者表单提交里&#xff0c;可以用以下三种方式获取上一页的url&#xff1a; 1、document.referrer 2、top.document.referrer 3、window.parent.document.referrer 这在ie和firefox里都可以实现 但如果在IE中用js函数跳转的话&#xff0c;以上三…

【软件开发底层知识修炼】二十五 ABI之函数调用约定二之函数返回值为结构体时的约定

上一篇文章学习了几种函数调用约定的区别&#xff0c;点击链接查看上一篇文章&#xff1a;【软件开发底层知识修炼】二十四 ABI之函数调用约定本篇文章继续学习函数调用约定中&#xff0c;关于函数返回值的问题。当函数返回值为结构体时&#xff0c;函数返回值是如何来传给调用…

说说我最近比较迷的Criminal Minds

喜欢《犯罪心理》这部美剧很久了&#xff0c;从第四季一直到现在还在追的第六季&#xff0c;觉得集集精彩。简练而不失悬念。该片的一些情节上的特质让我总是与工作上的某些场景产生对比联想。管理模式。BAU是一个典型的家庭式的管理方式。Hotch作为大家长&#xff0c;不辞辛劳…

【软件开发底层知识修炼】二十六 ABI-应用程序二进制接口 学习总结文章目录

前面学习了ABI的知识&#xff0c;感觉受益良多。对底层与编译器有更加深刻的认识&#xff0c;为此这里将前面写过的关于ABI 的文章给列出来&#xff0c;方便学习与翻阅。 【软件开发底层知识修炼】二十一 ABI-应用程序二进制接口一【软件开发底层知识修炼】二十二 ABI-应用程序…

关于《高性能JavaScript》制表(Memoization)笔记

减少工作量是最好的性能优化技术。代码所做的事情越少&#xff0c;他的速度就越快。 书中在关于用制表优化递归的下一段代码中&#xff0c;存在问题。 function memfactorial(n){ if (!memfactorial.cache){ memfactorial.cache { "0": 1, …

CSDN-Markdown-图片设置(大小,居中)

利用markdown在编写文档时插入图片是默认靠左&#xff0c;有些时候将图片设置为居中时可以更加的美观&#xff0c;这时就需要在图片的信息前边添加如下程序 <div aligncenter>![这里写图片描述](http:...如果想将图片位于右侧&#xff0c;只需要将center改为right<di…

查询指定范围内数据记录(适用于sqlserver2005以上)

1 WITHOrderedOrders AS2 (SELECTROW_NUMBER()over(orderbyg_Id asc) asRowNumber,g_Id FROMguanxi) 3 SELECT*4 FROMOrderedOrders 5 WHERERowNumber BETWEEN50AND60;转载于:https://www.cnblogs.com/prolion/archive/2011/04/02/2003277.html

CSDN中markdown字体颜色,大小,首行缩进,居中排布

一、下面是首行缩进的两种方法 1.这里实用空格去替代缩进的字符&#xff0c;下面讲的替代包括分号 2.把输入法由半角改为全角。 两次空格之后就能够有两个汉字的缩进。 半方大的空白用&ensp;或 全方大的空白用&emsp;或 不断行的空白格用 或 示例&#xff1a; 略略略…

SQL查询中的转义字符

如果想查找“_cs”结尾的的账户select * from [user] where loginname like %_cs是不行的&#xff0c;_ 被认为是任意的字符&#xff0c;所以需要转义字符&#xff0c;有两种写法&#xff1a;select * from [user] where loginname like %[_]csselect * from [user] where logi…

【Git、GitHub、GitLab】一 Git安装与Git最小配置

Git学习开始&#xff0c;虽然只是工具。 文章目录1 Git安装2 检查Git安装版本3 Git的最小配置4 git config的其它作用域5 总结1 Git安装 下面是安装Git的的官网链接&#xff1a; Git安装 分别有Linux安装、Mac安装与Windows安装的方法。我个人安装Windows与Linux。方法在上面…

C++类构造函数初始化列表

//以前学习中很模糊的一些概念  结构函数初始化列表以一个冒号开始&#xff0c; 接着是以逗号分隔的数据成员列表&#xff0c; 上面的结构函数(运用初始化列表的结构函数)显式的初始化类的成员;而没运用初始化列表的结构函数是对类的成员赋值&#xff0c; 并没有进行显式的初…

【Git、GitHub、GitLab】二 Git基本命令之建立Git仓库

上一篇文章学习了Git的安装与最小配置&#xff1a;【Git、GitHub、GitLab】一 Git安装与Git最小配置 文章目录建立Git仓库建立Git仓库 有两种方式可以建立Git仓库&#xff1a; 用Git之前已经有项目代码&#xff0c;则使用以下两条命令建立Git仓库 $ cd 项目代码所在的文件夹 $…

poj 3273

参数搜索。 代码&#xff1a; #include<iostream> #include<fstream>using namespace std;long long n,m,mid; long long a[100001];long long solve(){long long i,j1,k0;for(i1;i<n;i){if(a[i]-a[k]>mid){j;ki-1;}}return j; }void read(){ // ifstream ci…

使用VNC软件与花生壳进行内网穿透实现在嵌入式平台中进行广域网下的远程控制

在嵌入式平台中如何实现广域网下的远程登录控制&#xff1f; 文章目录1 项目需要2 解决方案3 首先实现局域网下的VNC远程控制4 总结1 项目需要 在IM.X6q硬件平台&#xff0c;Linux4.1.15内核版本中实现在广域网下进行远程登录控制。主控机为Windows机器&#xff0c;被控机是IM…

HDU 1159 Common Subsequence

/*经典的动态规划*/#include <iostream>usingnamespacestd;charstr1[1000];charstr2[1000];intdp[1001][1001];intmain(){while(cin >>str1 >>str2){intlen1 strlen(str1);intlen2 strlen(str2);intmax -1; memset(dp,0,sizeof(dp));for(inti 0; i <len1;…