曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 5594|回复: 0
打印 上一主题 下一主题

传送字符串指令

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2010-8-16 01:05:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
处理字符串时经常将字符串从一个内存位置复制到另一个内存位置,MOVS 指令为此提供了简单的途径。MOVS 指令有 3 种格式:
      MOVSB : 传送单一字节
      MOVSW : 传送一个字 (2 字节)
      MOVSL : 传送一个双子 (4 字节)
Intel 文档使用 MODVSD 传送双字,GNU 汇编器使用 MOVSL 。

MOVS 使用隐含的源和目标操作数。
隐含的源操作数是 ESI 寄存器,它指向源字符串的内存位置。
隐含的目标操作数是 EDI 寄存器,它指向字符串要被复制到的目标内存位置。

两种加载 ESI 和 EDI 的方法:
1. 使用间接寻址。
通过在内存位置标签前面加 $ 符号,内存位置的地址就会被加到 ESI 或 EDI 寄存器中,如:
movl $output, %edi
2. 使用 LEA 指令。
LEA 指令加载一个对象的有效地址,此时源操作数必须指向一个内存位置,如:
leal output, %edi     #把 output 标签的 32 位内存位置加载到 EDI

测试程序:
.section .data
value1:
     .ascii "This is a test string.\n"

.section .bss
     .lcomm output, 23

.section .text

.global _start

_start:
     nop
     leal value1, %esi
     leal output, %edi
     movsb
     movsw
     movsl

     movl $1, %eax
     movl $0, %ebx
     int $0x80
生成可执行程序后,在 gdb 里看到:
(gdb) n
16        movsb
(gdb) n
17        movsw
(gdb) n
18        movsl
(gdb) x/s &output
0x80490b0 <output>:     "Thi"
(gdb) n
20        movl $1, %eax
(gdb) x/s &output
0x80490b0 <output>:     "This is"
正如期望的,字符串按照指令正确的复制到目的地。在每次执行 MOVS 时,数据传送后,ESI 和 EDI 寄存器会自动改变,为下一次传送做准备。需要注意的是,ESI 和 EDI 不仅能自动递增,还能自动递减,这取决于 EFLAGS 寄存器中的 DF 标志。
如果 DF 被清零,那么递增;如果 DF 被置位,则递减。CLD 指令用于将 DF 标志清零,STD 指令用于将 DF 标志置位。

对上面程序做一下修改:
.section .data
value1:
     .ascii "This is a test string.\n"

.section .bss
     .lcomm output, 23

.section .text

.global _start

_start:
     nop
     leal value1+22, %esi
     leal output+22, %edi
     
     std
     movsb
     movsw
     movsl

     movl $1, %eax
     movl $0, %ebx
     int $0x80
在 GDB 里观察:
(gdb) x/23b &output
0x80490b0 <output>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x80490b8 <output+8>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x80490c0 <output+16>:    0x00    0x00    0x00    0x6e    0x67    0x2e    0x0a
由上可见,output 位置的字符串确实是从末尾开始填充的,但在 3 条 MOVS 指令之后,只有 4 个内存位置被填充!原因是:
ESI 和 EDI 是向后计数的,而 MOVSW 和 MOVSL 还是按照向前的顺序获得内存位置。过程是:
当 MOVSB 完成时,ESI 和 EDI 递减 1,'\n' 被复制,ESI 指向 '.' 处。
当 MOVSW 完成时,MOVSW 向前(根据ESI)获得 2 个内存位置,即复制了 '.' 和 '\n',然后送往 EDI 指向位置处,实际上此时 '\n' 被覆盖一次,然后 ESI 和 EDI 递减 2 个位置。
当 MOVSL 完成时,MOVSL 向前(根据ESI)获得 4 个内存位置,即复制了 'n', 'g', '.', '\n',然后送往 EDI 指向位置处,此时实际上是将 '.' 和 '\n' 进行了覆盖。

由上可知,如果使用 STD 向后处理字符串,但 MOVSW 和 MOVSL 指令仍然向前获取内存位置。

如果要复制大型字符串,可用循环来获取全部数据,其中将 ECX 设置为字符串的长度进行循环控制:
.section .data
value1:
     .ascii "This is a test string.\n"

.section .bss
     .lcomm output, 23

.section .text

.global _start

_start:
     nop
     leal value1, %esi
     leal output, %edi
     movl $23, %ecx
loop1:
     movsb
     loop loop1

     movl $1, %eax
     movl $0, %ebx
     int $0x80
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-14 15:04 , Processed in 0.077888 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表