|
处理字符串时经常将字符串从一个内存位置复制到另一个内存位置,MOVS 指令为此提供了简单的途径。MOVS 指令有 3 种格式:
Intel 文档使用 MODVSD 传送双字,GNU 汇编器使用 MOVSL 。
MOVS 使用隐含的源和目标操作数。
隐含的源操作数是 ESI 寄存器,它指向源字符串的内存位置。
隐含的目标操作数是 EDI 寄存器,它指向字符串要被复制到的目标内存位置。
两种加载 ESI 和 EDI 的方法:
1. 使用间接寻址。
通过在内存位置标签前面加 $ 符号,内存位置的地址就会被加到 ESI 或 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 |
|