简单的汇编程序分析

汇编程序1

.section .data
.section .text
.globl _start
_start:
movl $1, %eax
movl $4, %ebx
int $0x80

将这段程序保存为hello.s,然后用汇编器as把汇编程序中的助记符翻译成机器指令(汇编指令与机器指令是对应的)生成目标文件hello.o。然后用链接器ld把目标文件hello.o链接成可执行文件hello(虽然只有一个目标文件但是也需要经过链接才能成为可执行文件因为链接器要修改目标文件中的一些信息)。这个程序只做了一件事就是退出,退出状态为4。shell中可以echo $?得到上一条命令的退出状态。

as hello.s -o hello.o
ld hello.o -o hello
./hello
echo $?

汇编程序中以"."开头的名称不是指令的助记符,不会被翻译成机器指令,而是给汇编器一些特殊的指示,称为汇编指示或伪操作。

.section .data
.section .text

.section指示把代码划分成若干个段(section),程序被操作系统加载时,每个段被加载到不同的地址,具有不同的读写执行权限。

.data段保存程序的数据是可读写的,C程序的全局变量也属于.data段。上边的程序没定义数据所以.data是空的。

.text段保存代码,是只读和可执行的,后面那些指令都属于这个.text段。

.globl  _start

_start是一个符号(Symbol),符号在汇编程序中代表一个地址,可以用在指令中,汇编程序经过汇编器的处理后所有的符号都被替换成它所代表的地址值。在C中我们可以通过变量名访问一个变量,其实就是读写某个地址的内存单元,我们通过函数名调用一个函数其实就是调转到该函数的第一条指令所在的地址,所以变量名和函数名都是符号,本质上是代表内存地址的。

.globl指示告诉汇编器_start这个符号要被链接器用到,所以要在目标文件的符号表中给它特殊标记。_start就像C程序的main函数一样特殊是整个程序的入口,链接器在链接时会查找目标文件中的_start符号代表的地址,把它设置为整个程序的入口地址,所以每个汇编程序都要提供一个_start符号并且用.globl声明。如果一个符号没有用.globl指示声明这个符号就不会被链接器用到。

_start:

_start在这里就像C语言的语句标号一样。汇编器在处理汇编程序时会计算每个数据对象和每条指令的地址,当汇编器看到这样一个标号时,就把它下面一条指令的地址作为_start这个符号所代表的地址。而_start这个符号又比较特殊,是整个程序的入口地址,所以下一条指令movl $1, %eax就成了程序中第一条被执行的指令。

movl $1, %eax

这是一条数据传送指令,CPU内部产生一个数字1, 然后传送到eax寄存器中。mov后边的l表示long,说明是32位的传送指令。CPU内部产生的数称为立即数,在汇编程序中立即数前面加"$",寄存器前面加"%",以便跟符号名区分开。

movl $4, %ebx

与上条指令类似,生成一个立即数4,传送到ebx寄存器中。

int $0x80

前两条指令都是为这条指令做准备的,执行这条指令时:

1. int指令称为软中断指令,可以用这条指令故意产生一个异常。异常的处理与中断类似,CPU从用户模式切换到特权模式,然后跳转到内核代码中执行异常处理程序。

2. int指令中的立即数0x80是一个参数,在异常处理程序中根据这个参数决定如何处理,在linux内核中,int $0x80这种异常称系统调用(System Call)。内核提供了许多系统服务供用户程序使用,但这些系统服务不能像库函数(比如printf)那样调用,因为在执行用户程序时CPU处于用户模式不能直接调用内核函数,所以需要通过系统调用切换CPU模式,通过异常处理程序进入内核,用户程序只能通过寄存器传几个参数,之后就要按内核设计好的代码路线走,而不能由用户程序随心所欲想调那个内核函数,这样保证了系统服务被安全的调用,在调用结束后CPU再切换回用户模式,继续执行int指令后面的指令,在用户程序看来就像函数的调用和返回一样。

3. eax和ebx寄存器的值是传递给系统调用的两个参数,eax的值是系统调用号,1表示_exit系统调用,ebx的值则是传给_exit系统调用的参数,也就是退出状态。_exit这个系统调用会终止掉当前进程,而不会返回它继续执行。不同的系统调用需要的参数个数也不同,有的会需要ebx、ecx、edx三个寄存器的值做参数,大多数系统调用完成之后是会返回用户程序继续执行的,_exit系统调用特殊。

汇编程序2

求一组数最大值的汇编程序:

.section .data
data_items:
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.section .text
.globl _start
_start:
movl $0, %edi
movl data_items(,%edi,4), %eax
movl %eax, %ebx
start_loop:
cmpl $0, %eax
je loop_exit
incl %edi
movl data_items(, %edi,4), %eax
cmpl %ebx, %eax
jle start_loop
movl %eax, %ebx
jmp start_loop
loop_exit:
mov $1, %eax
int $0x80

这个程序在一组数中找到一个最大的数,并把它作为程序的退出状态。这段数在.data段给出:

data_items:
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0

.long指示声明一组数,每个数32位,相当于C数组。数组开头有个标号data_items,汇编器会把数组的首地址作为data_items符号所代表的地址,data_items类似于C中的数组名。data_items这个标号没有.globl声明是因为它只在这个汇编程序内部使用,链接器不需要知道这个名字的存在。除了.long之外常用的声明:

.byte,也是声明一组数,每个数8位。

.ascii,例: .ascii "Hello World",声明了11个数,取值为相应字符的ASCII码。和C语言不同的是这样声明的字符串末尾是没有'\0'字符的。

data_items数组的最后一个数是0,我们在一个循环中依次比较每个数,碰到0的时候就终止循环。在这个循环中: 

edi寄存器保存数组中的当前位置,每次比较完一个数就把edi的值加1,指向数组中的下一个数。

ebx寄存器保存到目前为止找打的最大值,如果发现有更大的数就更新ebx的值。

eax寄存器保存当前要比较的数,每次更新edi之后,就把下一个数读到eax中。

_start:
movl $0, %edi

初始化edi,指向数组的第0个元素。

movl data_items(,%edi,4), %eax

这条指令把数组的第0个元素传送到eax寄存器中。data_items是数组的首地址,edi的值是数组的下标,4表示数组的每个元素占4字节,那么数组中第edi个元素的地址应该是data_items+edi*4。从这个地址读数据,写成指令就是上面那样。

movl %eax, %ebx

ebx的初始值也是数组的第0个元素。

下面进入一个循环,在循环的开头用标号start_loop表示,循环的末尾之后用标号loop_exit表示。

start_loop:
cmpl $0, %eax
je loop_exit

比较eax的值是不是0,如果是0就说明到了数组末尾了,就要跳出循环。cmpl指令将两个操作数相减,但计算结果并不保存,只是根据计算结果改变eflags寄存器中的标志位。如果两个操作数相等,则计算结果为0,eflags中的ZF位置1。je是一个条件跳转指令,它检查eflags中的ZF位,ZF位为1则发生跳转,ZF位为0则不跳转继续执行下一条指令。(条件跳转指令和比较指令是配合使用的)je的e就表示equal。

incl %edi
movl data_items(,%edi,4), %eax

将edi的值加1,把数组中的下一个数组传送到eax寄存器中。

cmpl %ebx, %eax
jle start_loop

把当前数组元素eax和目前为止找到的最大值ebx做比较,如果前者小于等于后者,则最大值没有变,跳转到循环开头比较下一个数,否则继续执行下一条指令。jle也是一个条件跳转指令,le表示less than or equal。

movl %eax, %ebx
jmp start_loop

更新了最大值ebx然后跳转到循环开头继续比较下一个数。jmp是一个无条件跳转指令,什么条件也不判断直接跳转。loop_exit标号后面的指令用_exit系统调用来退出程序。

转载于:https://www.cnblogs.com/xiaojianliu/articles/8733526.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值