Linux汇编之数据传送

汇编 2015年03月03日 ,

ASM汇编数据传送

定义了数据元素后,就必须对数据元素进行处理。因为数据元素位于内在中,并且处理器的很多指令要利用寄存器,所以处理数据元素的第一个步骤是在内在和寄存器之间传送它们。

在本文枫竹梦将介绍Linux汇编语言(ASM)中如何使用MOV进行数据元素的传送。

MOV指令格式

MOV指令的基本格式如下:

movx source, destination

sourcedestination的值可以是内存地址、存储在内在中的数据值、指令语句中定义的数据值或者寄存器。

GNU汇编器为MOV指令添加了后缀,以指定要传送数据元素的长度:

movx

其中x可以是以下字符:

  • l用于32位数据
  • w用于16位数据
  • b用于8位数据

使用时须将movx指令与寄存器的长度保持一致。

mov指令在特殊的规则,只在符合如下规则的源和目的操作数组合可以实现数据的传送。

源操作数 方向 目的操作数
立即数 ==> 通用寄存器
立即数 ==> 内存位置
通用寄存器 <==> 通用寄存器
段寄存器 <==> 通用寄存器
控制寄存器 <==> 通用寄存器
调试寄存器 <==> 通用寄存器
内存位置 <==> 通用寄存器
内存位置 <==> 段寄存器

把立即数传送给寄存器和内存

立即数中直接在程序中指定的,程序运行后不能改变。如:

movl $0, %eax # 将0传送给寄存器EAX
movl $0x80, %ebx # 将0x80传送给寄存器EBX
movl $100, height # 将100传送给height引用的内存位置

注意:传送立即数时,要在立即数前加上美元符号($),表明其是立即数。

在寄存器间传送数据

MOV可以将数据从一个寄存器传送给另一个寄存器,这是处理器传送数据最快的方式。如果可以,尽可能的将数据保存在寄存器中,这样可以加快处理器对数据访问速度。

8个通用寄存器(EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP)是保存数据最常用的寄存器,其可以将数据传送给专用寄存器(控制寄存器、调试寄存器、段寄存器),专用寄存器也可以将数据传送给通用寄存器,但是专用寄存器之间不能直接进行数据在传送。

寄存器间数据传送数据如:

movl %eax, %ecx # 将32位数据从EAX传送给ECX
movw %ax, %cx # 将16位数据从AX传送给CX

注意:指令与寄存器的长度就匹配。

在内存和寄存器之间传送数据

数据在寄存器间传送是比较简单的,但将数据在寄存器和内存间传送就比较复杂。下面分情况进行讨论。

把数据从内存传送到寄存器

如何在指令当中表示内存地址是首先在考虑的问题。最简单的情况是使用定义内存位置的标签:

movl value, $eax

MOVL指令传送32位的信息,因此,它传送将value标签引用的内存位置开始的4个字节数据。
实例如下:

# mov1.s - By furzoom @ Mar 3, 2015
.section .data
value:
.int 1
.section .text
.globl _start
_start:
nop
movl value, %ecx

movl $1, %eax
movl $0, %ebx
int $0x80

使用-gstabs参数汇编该程序,连接后进行调试程序如下:

[mn@furzoom asm]$ as -gstabs -o mov1.o mov1.s
[mn@furzoom asm]$ ld -o mov1 mov1.o
[mn@furzoom asm]$ gdb -q mov1
Reading symbols from /home/mn/Desktop/Documents/asm/mov1...done.
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file mov1.s, line 9.
(gdb) run
Starting program: /home/mn/Desktop/Documents/asm/mov1

Breakpoint 1, _start () at mov1.s:9
9         movl value, %ecx
(gdb) print /x $ecx
$1 = 0x0
(gdb) step
11        movl $1, %eax
(gdb) print /x $ecx
$2 = 0x1
(gdb) 

正如期望的那样,数据从标签引用内存位置传送到寄存器中。

把数据从寄存器传送给内存

该传送方法与将数据从内存传送给寄存器的方式相似,示例代码如下:

# mov2.s - By furzoom @ Mar 3, 2015
.section .data
value:
.int 1
.section .text
.globl _start
_start:
nop
movl $10, %ecx
movl %ecx, value

movl $1, %eax
movl $0, %ebx
int $0x80

使用-gstabs参数汇编该程序,连接后进行调试程序如下:

[mn@furzoom asm]$ as -gstabs -o mov2.o mov2.s
[mn@furzoom asm]$ ld -o mov2 mov2.o
[mn@furzoom asm]$ gdb -q mov2
Reading symbols from /home/mn/Desktop/Documents/asm/mov2...done.
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file mov2.s, line 9.
(gdb) run
Starting program: /home/mn/Desktop/Documents/asm/mov2

Breakpoint 1, _start () at mov2.s:9
9         movl $10, %ecx
(gdb) step
10        movl %ecx, value
(gdb) step
12        movl $1, %eax
(gdb) x/d &value
0x804908c <value>:      10
(gdb)  

通过查看value标签引用的内存位置,发现寄存器ECX中的值10被存储到相应内存当中。

使用变址的内存位置

当访问标签引用的单一内存位置时,处理的很好,如果需要引用多个值,如数组中的值,如:

value:
.int 1, 2, 3

这创建存放在内存中的连续的数据值,要引用除第一个值以处的值时必须使用变址系统确定要访问的哪一个值。完成这种操作的方式称为变址内存模式(indexed memory mode)。此时需要确定内存位置需要以下因素:

  • 基址
  • 偏移地址
  • 数据元素长度
  • 数据元素索引

表达式如:

base_address(offset_address, index, size)

获取的数据值位于:

base_address + offset_address + index * size

如果其中有参数为零,可以省略不写,但逗号在保留,且offset_addressindex的值必须是寄存器,size的值可以是立即数。
还是看实例更加清晰:

# mov3.s - By furzoom @ Mar 3, 2015
.section .data
value:
.int 1, 2, 3
.section .text
.globl _start
_start:
nop
movl $1, %edi
movl value(, %edi, 4), %eax
movl $2, %edi
movl %eax, value(, %edi, 4)

movl $1, %eax
movl $0, %ebx
int $0x80

使用-gstabs参数汇编该程序,连接后进行调试程序如下:

[mn@furzoom asm]$ as -gstabs -o mov3.o mov3.s
[mn@furzoom asm]$ ld -o mov3 mov3.o
[mn@furzoom asm]$ gdb -q mov3
Reading symbols from /home/mn/Desktop/Documents/asm/mov3...done.
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file mov3.s, line 9.
(gdb) run
Starting program: /home/mn/Desktop/Documents/asm/mov3

Breakpoint 1, _start () at mov3.s:9
9         movl $1, %edi
(gdb) step
10        movl value(, %edi, 4), %eax
(gdb) step
11        movl $2, %edi
(gdb) print /d $eax
$1 = 2
(gdb) step
12        movl %eax, value(, %edi, 4)
(gdb)
14        movl $1, %eax
(gdb) x/3d &value
0x804909c <value>:      1       2       2
(gdb)     

该程序将标签value引用的内存位置的第二个数传给寄存器EAX,然后将传送给第三个位置。通过查看程序运行中的寄存器值和value引用位置的内存的数据,可以看到达到这样的目的。

使用寄存器间接寻址

寄存器除了保存数据之处,还可以保存内存地址。当寄存器保存内存地址时,它被称为指针(pointer)。使用指针访问存储在内存位置中的数据称为间接寻址(indirect addressing)。

通过在指令中的标签前加上美元符号($)获得数据值的内存位置,如:

movl $value, %edi

value标签引用的内存地址传送给寄存器EDI。为表明寄存器中的数据中内存地址而不是作为数值,GNU汇编需要在给寄存器加上括号,如:

movl $1, (%edi)

还可以给出相对于该地址的偏移量,如:

movl $1, 4(%edi)

该指令将立即数1放在EDI寄存器指向的位置之后4个字节的内存位置。也可以指定为负数,表明是之前的位置。

一个完整的实例如下:

# mov4.s - By furzoom @ Mar 3, 2015
.section .data
value:
.int 1, 2, 3
.section .text
.globl _start
_start:
nop
movl $value, %edi
movl $100, 4(%edi)
movl 8(%edi), %eax

movl $1, %eax
movl $0, %ebx
int $0x80

使用-gstabs参数汇编该程序,连接后进行调试程序如下:

[mn@furzoom asm]$ as -gstabs -o mov4.o mov4.s
[mn@furzoom asm]$ ld -o mov4 mov4.o
[mn@furzoom asm]$ gdb -q mov4
Reading symbols from /home/mn/Desktop/Documents/asm/mov4...done.
(gdb) break *_start+1
Breakpoint 1 at 0x8048075: file mov4.s, line 9.
(gdb) run
Starting program: /home/mn/Desktop/Documents/asm/mov4

Breakpoint 1, _start () at mov4.s:9
9         movl $value, %edi
(gdb) step
10        movl $100, 4(%edi)
(gdb) x /3d &value
0x8049090 <value>:      1       2       3
(gdb) print /x $edi
$1 = 0x8049090
(gdb) step
11        movl 8(%edi), %eax
(gdb) x /3d &value
0x8049090 <value>:      1       100     3
(gdb) step
13        movl $1, %eax
(gdb) print /d $eax
$2 = 3
(gdb)   

该程序完成了将100传送给标签value引用的内存位置的后4个字节地址,并将标签value引用的内存位置的后8个字节地址的32位数(3)传送给EAX寄存器。

以上就是数据传送指令的介绍。(完)

如无特别说明,本站文章皆为原创,若要转载,务必请注明以下原文信息:
日志标题:《Linux汇编之数据传送》
日志链接:http://furzoom.com/linux-asm-mov/
博客名称:枫竹梦

发表评论

插入图片

NOTICE1:请申请gravatar头像,没有头像的评论可能不会被回复!

回到顶部