5.C调用约定

前言

在不同的语言中,

  • 变量存储
  • 传递参数
  • 返回值
    的方式有所不同,这种差异成为语言的调用约定,因为它表述了在调用函数时函数预期得到什么样的数据。
    使用最广泛的是C语言调用约定,它也是Linux平台的标准。

知识点

一般,栈顶的地址要小于栈底的地址,所以说栈是向下增长的(向地址减小的方向。)

  • 栈寄存器%esp总是包含一个指向当前栈顶的指针(地址),无论栈顶在何处。
    • 如果我们只想访问栈顶的值,而不想移除它,可以这样:movl (%esp), %eax
    • 访问栈顶的下一个值:movl 4(%esp), %eax

规则

执行函数之前:

  • 程序将函数的所有参数按逆序压入栈中
  • 接着,程序发出一条call指令,表明程序希望开始执行的函数。
  • call指令会做两件事:
    • 首先,将下一条指令的地址,即返回地址压入栈中。
    • 然后,修改指令指针(%eip),以指向函数起始处。

执行函数时:

  • 首先,函数通过push %ebp指令保存当前的基址指针寄存器%ebp
    • 基址指针寄存器时一个特殊的寄存器,用来访问函数的参数和局部变量。
  • 然后,它会用movl %esp, %ebp将栈指针%ebp复制到%esp
    • 这使得你能够把函数参数作为相对于基址指针的固定索引进行访问。
  • 接下来,函数为其所需要的局部变量预留栈空间,只需将栈指针向外移动即可实现。
    • 例如需要两个字的内存,只需要将栈指针向下移动两个字即可预留空间:subl $8, %esp,这样,就可以将栈用于变量存储。

函数执行完毕:

  • 将其返回值存储到%eax
  • 将栈恢复到调用函数时的状态(移除当前栈帧,并使调用代码的栈帧重新生效)
  • 将控制权交给调用它的程序。
    • 这是通过ret指令实现的,该指令将栈顶的值弹出,并将指令寄存器%eip设置为该弹出值。

因此,当函数返回给调用它的代码时,必须恢复前一个栈帧。
如果不这样,ret将无法正常工作,

因此,从函数返回,你必须使用如下指令:

movl	%ebp, %esp
popl	%ebp
ret

CPU执行retf指令时,相当于pop IP AND pop CS。

至此,函数调用完成。

  • 调用代码可以检查%eax中的返回值。
  • 调用代码也需要弹出其入栈的所有参数,以将栈指针复位至其原先的位置
    • 如果不再需要参数值,可以用addl指令将4*参数个数加到%esp即可。

关于更详细的C语言调用约定(也称为ABI,即应用程序二进制接口),请查询
System V Application Binary Interface — Intel386 Architecture Processor Supplement

代码示例

#目的:	展示函数如何工作的程序
#		本程序将计算	2^3 + 5^2
#

#主程序所有内容都存储在寄存器中,因此数据段不含任何内容

.section	.data

.section	.text

.globl	_start

_start:
	pushl	$3		#压入第二个参数
	pushl	$2		#压入第一个参数
	call	power
	addl	$8, %esp	#恢复栈指针
	pushl	%eax		#在调用下一个函数前,保存第一个答案

	pushl	$2
	pushl	$5
	call	power
	addl	$8, %esp
	
	popl	%ebx
	addl	%eax, %ebx

	movl	$1, %eax
	int		$0x80

#目的:	用于计算一个数的幂
#
#输入:	第一个参数	底数
#		第二个参数	底数的指数
#
#输出:	以返回值的形式给出结果
#
#注意:	直属必须大于等于1
#
#变量:
#		%ebx保存底数
#		%ecx保存指数
#			-4(%ebp)保存当前结果
#			%eax用于暂时存储

.type	power, @function
power:
		pushl	%ebp
		movl	%esp, %ebp
		subl	$4, %esp		#为本地存储保留空间
		movl	8(%ebp), %ebx	#第一个参数放入%eax
		movl	12(%ebp), %ecx	#第二个参数放入%ecx
		movl	%ebx, -4(%ebp)	#存储当前结果
	power_loop_start:
		cmpl	$1, %ecx	#如果是1次方,就已经获得结果
		je		end_power
		movl	-4(%ebp), %eax	#将当前结果放入%eax
		imull	%ebx, %eax		#将当前结果与底数相乘
		movl	%eax, -4(%ebp)	#保存当前结果
		decl	%ecx
		jmp		power_loop_start
	end_power:
		movl	-4(%ebp), %eax	#返回值移入%eax
		movl	%ebp,	%esp
		popl	%ebp
		ret
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

barbyQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值