汇编语言学习篇5——GAS汇编实践篇(4)- power.s

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  《Computer Systems A Programmer’s Perspective》

上下文链接

  上篇:汇编语言学习篇5——GAS汇编实践篇(3)- maximum.s-数据类型、跳跃指令、寻址模式
  下篇:汇编语言学习篇5——GAS汇编实践篇(5)- factorial.s

第四个程序:power.s

  程序功能:计算 2 3 + 5 2 2^{3} + 5^{2} 23+52的值()
  说明:主程序中的所有内容都存储在寄存器中,所以数据部分没有任何内容。

  power函数功能:计算一个数的幂值。
    输入:参数一:基数,参数二:权数
    输出:返回值:计算结果
  寄存器作用
    %eax用于临时存储
    %ebx-基数
     %ecx -权数
    -4(%ebp) -保存当前结果
  注意:权数必须大于或等于1

.code32
.section .data		#所有的数据都存在寄存器中,故数据段没有任何数值。
.section .text
.globl _start
_start:
	pushl $3 		#压入第二个参数
	pushl $2		#压入第一个参数
	call power 		#调用函数
	addl $8,%esp    #将堆栈指针移回初值地址位置(long=4 * 2 = 8)
	pushl %eax 		#在调用下一个函数之前保存第一个结果
	pushl $2		#压入第二个参数
	pushl $5 		#压入第一个参数
	call power		#调用函数
	addl $8,%esp	#将堆栈指针移回初值地址位置(long=4 * 2 = 8)
	popl %ebx		#第二个结果已经在%eax中。将第一个结果保存到堆栈中,所以现在我们可以将它弹出到%ebx中
	addl %eax,%ebx  #将两个结果加在一起,结果存入%ebx
	movl $1,%eax    #退出(返回%ebx)
	int $0x80
	
.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

输入指令:as power.s -o power.o --32
输入指令:ld power.o -o power -m elf_i386
输入指令:./power
输入指令:echo $?
  分析:
输入程序,组装它,然后运行它。尝试对不同的值调用power,但是请记住,当它被传递回操作系统时,结果必须小于256。也试着减去这两个计算的结果。尝试向幂函数添加第三次调用,并将其结果添加回来。
主程序代码非常简单。将参数压入堆栈,调用函数,然后将堆栈指针移回。结果存储在%eax中。注意,在对power的两次调用之间,我们将第一个值保存到堆栈上。这是因为保证保存的唯一寄存器是%ebp。因此,我们将该值压入堆栈,并在第二个函数调用完成后取出该值。
  .type power,@function:告诉链接器应该将符号幂作为函数来处理。因为这个程序只在一个文件中,所以它的工作原理与省略这个相同.

  call和jmp的区别在于,call也将返回地址压入堆栈,这样函数就可以返回,而jmp则不会。

程序所做的是从基数开始,并将其存储为乘数(存储在%ebx中)和当前值(存储在-4中(%ebp))。它还将功率存储在%ecx中,然后不断地将当前值乘以乘数,降低功率,如果功率(在%ecx中)降至1,则退出循环
imull执行整数乘法,并将结果存储在第二个操作数中,decl将给定寄存器减少1

汇编中的函数

  函数的组成
    ①函数名:为一个符号,表示函数开始的地址
    ②函数参数:显式给函数处理的数据项。
    ③局部变量-local variables:函数处理过程中使用的数据存储,函数返回时被丢弃。
    ④静态变量-static variables:函数处理过程中使用的数据存储,之后不会丢弃,每次函数被激活时都被重用,其他部分无法访问此数据,非必要不使用。
    ⑤全局变量-global variables:用于处理函数外部管理的数据存储,可在函数外使用。
    ⑥返回地址:不可见的参数,不是函数中直接使用,返回地址是个参数,告诉函数完成后在哪里继续执行。【在大多数编程语言中该参数在调用函数时会自动传递,汇编语言中,call指令传递返回地址,ret指令使用该地址返回调用函数的位置】
    ⑦返回值:将数据传输到主函数。绝对多数编程语言只允许返回一个值。

汇编函数调用C函数

  什么是stack?
  stack即栈/栈堆/堆叠,一般可分为两种:①数据结构(栈)②与内存分配有关(栈区)。
  是一种数据项按序排列的数据结构(特殊的线性表),只能在一端对数据项进行插入(压栈 / pushl)或删除(出栈 / popl),故:具有先进后出的特点。
  允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)。
  栈区是内存中的一段区域,位于内存的最顶端地址(对栈区而言称为栈底),从内存顶部开始并向下增。
  栈指针寄存器%esp:总是包含一个指向栈当前顶部的指针,可知堆栈当前的“顶部”在哪里。
  栈区操作指令
    插入数据pushl:将数据压入栈区顶部 ,%esp将减4
    删除数据popl:将栈区顶部的数据弹出 ,%esp将加4
    PS:每个数据4Byte(long),栈区位于内存顶部(地址最大),向下增长。
    查询数据(不删除数据):
    movl (%esp), %eax.获取%esp所指位置的数值。【间接寻址模式】
    movl % esp, % eax:表示%eax将只保存指向堆栈顶部的指针,而不是顶部的值。
    movl 4(%esp), %eax:表示查找所指的值之前数值,即%esp加4【基指针寻址模式】

  C语言中栈区(stack)的作用?
    实现函数的局部变量参数返回地址的关键元素。
    函数执行前,先将所有函数参数按文件保存的相反次序压入栈区。使用指令:call来指示希望先启动哪个函数。
    call指令做两件事:
      ①将下一条指令地址(即返回地址)压入栈区。
      ②修改指令指针寄存器%eip指向函数的开头。
    接着函数本身开始工作:
      ①pushl %ebp,保存当前的基指针。基指针寄存器%ebp:一种用于访问函数参数和局部变量的特殊寄存器。
      ②movl %esp, %ebp,将栈指针复制到基指针寄存器,这将允许从基本指针访问函数参数作为固定索引。【这可以让你始终知道你的形参在哪里(以及我们将看到的局部变量),即使你可能会将东西推入或推出栈区。%ebp将始终位于堆栈指针在函数开始的位置】
      即:可通过使用基地址寄存器%ebp的不同偏移量来访问函数所需的所有数据。
    当一个函数被执行时:
      ①将返回值存储在%eax中。
      ②将堆栈重置为调用时的状态(它删除当前的堆栈帧并使调用代码的堆栈帧生效)
      ③将控制返回到调用它的地方。这是使用ret指令完成的,它弹出堆栈顶部的任何值,并将指令指针%eip设置为该值。

因此,在函数将控制权返回给调用它的代码之前,它必须恢复上一个堆栈帧。还请注意,如果不这样做,ret将无法工作,因为在我们当前的堆栈帧中,返回地址不在堆栈的顶部。因此,在返回之前,我们必须将堆栈指针%esp和基础指针%ebp重置为函数开始时的状态。因此,要从函数返回,您必须执行以下操作
movl %ebp, %esp
popl %ebp
ret

PS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值