汇编字符串处理指令

注:以下内容为学习笔记,多数是从书本、资料中得来,只为加深印象,及日后参考。然而本人表达能力较差,写的不好。因非翻译、非转载,只好选原创,但多数乃摘抄,实为惭愧。但若能帮助一二访客,幸甚!

一.传送字符串

把字符串从一个内存位置复制到另一个内存位置。


1.MOVS

3种格式:
MOVSB:传送单一字节
MOVSW:传送一个字(2字节)
MOVSL:传送一个双字(4字节)
MOVS使用隐含的源(ESI)、目的(EDI)操作数。
两种加载ESI、EDI值的方式:
1)间接寻址:
movl $output,  %edi
2)lea指令加载一个对象的有效地址
leal output,  %esi

每次执行MOVS指令时,数据传送后,ESI和EDI寄存器会自动改变,为另一次传送做准备。
ESI、EDI可能递增也可能递减,这取决于EFLAGS中的DF标志。如果DF被清零则递增,DF被设置,则递减。
CLD将DF清零
STD设置DF标志
示例:
  1. # An example of the MOVS instructions  
  2. .section .data  
  3. str:  
  4.     .ascii  "Hi AT&T.\n"  
  5.   
  6. .section .bss  
  7.     .lcomm  output, 9  
  8.   
  9. .section .text  
  10. .global _start  
  11.   
  12. _start:  
  13.     nop  
  14.   
  15.     leal    str,    %esi  
  16.     leal    output, %edi  
  17.   
  18.     movsb  
  19.     movsw  
  20.     movsl  
  21.   
  22.     movl    $1,     %eax  
  23.     movl    $0,     %ebx  
  24.     int     $0x80  
调试:
liury@liury-laptop:~/program/asm/working_with_string/movs_example$ make
as -gstabs -o movs_example.o movs_example.s
ld -o movs_example movs_example.o
liury@liury-laptop:~/program/asm/working_with_string/movs_example$ ls
makefile  movs_example  movs_example.o  movs_example.s
liury@liury-laptop:~/program/asm/working_with_string/movs_example$ gdb ./movs_example 
GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/liury/program/asm/working_with_string/movs_example/movs_example...done.
(gdb) l
1 # An example of the MOVS instructions
2 .section .data
3 str:
4 .ascii  "Hi AT&T.\n"
5
6 .section .bss
7 .lcomm  output, 9
8
9 .section .text
10 .global _start
(gdb) b *_start+1
Breakpoint 1 at 0x8048075: file movs_example.s, line 15.
(gdb) r
Starting program: /home/liury/program/asm/working_with_string/movs_example/movs_example 

Breakpoint 1, _start () at movs_example.s:15
15 leal  str, %esi
(gdb) n
16 leal  output, %edi
(gdb) print /x $esi
$1 = 0x8049094
(gdb) print /x str
$2 = 0x41206948
(gdb) print /x &str
$3 = 0x8049094
(gdb) n
18 movsb
(gdb) x /9cb &output
0x80490a0 <output>: 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000'
0x80490a8 <output+8>: 0 '\000'
(gdb) x /s &output
0x80490a0 <output>: ""
(gdb) n
19 movsw
(gdb) x /s &output
0x80490a0 <output>: "H"
(gdb) n
20 movsl
(gdb) x /s &output
0x80490a0 <output>: "Hi "
(gdb) n
22 movl  $1, %eax
(gdb) x /s &output
0x80490a0 <output>: "Hi AT&T"
(gdb) c
Continuing.

Program exited normally.
(gdb) q

对于要传送大型字符串,可用循环:
  1.     leal    datas,  %esi  
  2.     leal    output, %edi  
  3.     mvol    $100,   %ecx  
  4.     cld  
  5. loop_cp:  
  6.     movsb  
  7.     loop    loop_cp  

2.REP

可更简单地传送大型字符串。它自己并不执行什么操作,这条指令用于按照特定次数重复执行字符串指令,由ECX寄存器中的值进行控制。例如
  1. leal    datas,  %esi  
  2. leal    output, %edi  
  3. mvol    $100,   %ecx  
  4. cld  
  5. rep     movsb  

MOVSW,MOVSL传送大型字符串效率更高,但小心不能除尽的情况。

有些REP指令除监视ECX外还监视ZF(零标志)的状态。
--------------------------------------------------
指令 描述
-------------------------------------------
REPE 等于时重复
REPNE 不等于时重复
REPNZ 不为0时重复
REPZ 为0时重复
---------------------------------------------------

二.存储和加载字符串


1.LODS

用于把内存中的字符串传送到EAX。三种形式:
LODSB:把一个字节加载到AL
LODSW:把一个字加载到AX
LODSL:把一个双字加载到EAX

LODS指令使用ESI寄存器作为隐含的源操作数。ESI必须包含要加载的字符串所在的内存地址。
同样加载后ESI递增或递减取决于DF标志。

2.STOS

把字符串从EAX放到一个内存地址。
STOSB:存储AL中一个字节数据
STOSW:存储AX中一个字数据
STOSL:存储EAX中一个双字数据

STOS指令使用EDI作为隐含的目标操作数。可方便地与REP配合:
  1. leal    space,  %esi  
  2. leal    buffer, %edi  
  3. movl    $256,   %ecx  
  4. cld  
  5. lodsb  
  6. rep     stosb  

字符串处理示例,小写变大写:
  1. # Converting lower to upper case  
  2.   
  3. .section .data  
  4. str1:  
  5.     .asciz  "This is a TEST, of the program, hello AT&R!\n"  
  6. len:  
  7.     .int    . - str1  
  8.   
  9. .section .text  
  10. .global _start  
  11.   
  12. _start:  
  13.     nop  
  14.     leal    str1,   %esi  
  15.     movl    %esi,   %edi  
  16.     movl    len,    %ecx  
  17.     cld  
  18. 1:  
  19.     lodsb  
  20.   
  21.     cmpb    $'a',   %al  
  22.     jl      skipb  
  23.     cmpb    $'z',   %al  
  24.     jg      skipb  
  25.   
  26.     subb    $0x20,  %al  
  27.   
  28. skipb:  
  29.     stosb  
  30.     loop    1b  
  31.   
  32. end:  
  33.     pushl   $str1  
  34.     call    printf  
  35.   
  36.     addl    $4,     %esp  
  37.   
  38.     pushl   $0  
  39.     call    exit  
结果:
liury@liury-laptop:~/program/asm/working_with_string/str_process$ ./convert 
THIS IS A TEST, OF THE PROGRAM, HELLO AT&R!

三.比较字符串


1.CMPS

三种格式:CMPSB,CMPSW,CMPSL
隐含的源和目的操作数同样存储在ESI和EDI寄存器中。每次执行CMPS时,根据DF的设置,ESI和EDI递增或递减
CMPS指令从源字符串减去目标字符串,并且适当地设置EFLAGS的寄存器的进位、符号、溢出、零、奇偶校验和辅助进位标志。后面可跟跳转指令。

2.CMPS和REP一起使用

示例:
  1. # An example of using the REPE CMPS instruction  
  2. .section .data  
  3. output:  
  4.     .asciz  "The len is %d.\n"  
  5. str1:  
  6.     .ascii  "This is a test of the cmps instructions."  
  7. str2:  
  8.     .ascii  "This is a test of the CMPS instructions."  
  9. len:  
  10.     .int    . - str2  
  11.   
  12. .section .text  
  13. .global _start  
  14.   
  15. _start:  
  16.     nop  
  17.     movl    len,    %ecx  
  18.     pushl   %ecx  
  19.     pushl   $output  
  20.     call    printf  
  21.     addl    $8,     %esp  
  22.   
  23.     leal    str1,   %esi  
  24.     leal    str2,   %edi  
  25.     movl    len,    %ecx  
  26.     movl    $1,     %eax  
  27.   
  28.     cld   
  29.     repe    cmpsb  
  30.     je      equal  
  31.     movl    %ecx,   %ebx  
  32.     int     $0x80  
  33.   
  34. equal:  
  35.     movl    $0,     %ebx  
  36.     int     $0x80  
运行结果:
liury@liury-laptop:~/program/asm/working_with_string/cmps_rep$ make
as -gstabs -o cmps_rep.o cmps_rep.s
ld -dynamic-link /lib/ld-linux.so.2 -lc -o cmps_rep cmps_rep.o
liury@liury-laptop:~/program/asm/working_with_string/cmps_rep$ ./cmps_rep 
The len is 40.
liury@liury-laptop:~/program/asm/working_with_string/cmps_rep$ echo $?
17
解析:
上面的代码首先尝试来'.'运算符作为当前地址,求字符串长度的方法,为验证其正确性,程序开始处打印来一下即40。
然后比较两个字符串,直到第一个不相同的字符结束,即‘cmps’与‘CMPS’中的c处。
系统调用结束,调用号保存在EAX中,返回值保存在EBX中。则该程序的返回值若比较结束,即所有字符都相同则返回0,若不同则返回ECX(传到EBX返回)的值,本例为17(通过echo $?打印),17为从后往前数,第一个不同的字符的位置,即c的位置。

四.扫描字符串


1.SCAS

用于扫描字符串搜索一个或多个字符。三种形式:
SCASB,SCASW,SCASL,分别比较内存中的一个自己、字、双字和AL、AX、EAX的值。
SCAS使用EDI作为隐含的目标操作数。EDI必须包含要扫描的字符串的内存地址。指令执行时EDI按DF值递增或递减。
进行比较时会相应地设置EFLAGS标志。
可与REPE,REPNE一起使用:
REPE:扫描字符串,查找不匹配搜索字符的位置
REPNE:扫描字符串,查找匹配搜索字符的位置
示例:
  1. # An example of the SCAS instruction  
  2. .section .data  
  3. str:  
  4.     .ascii  "Hello AT&T!"  
  5. len:  
  6.     .int    . - str  
  7. char:  
  8.     .ascii  "&"  
  9.   
  10. .section .text  
  11. .global _start  
  12.   
  13. _start:  
  14.     nop  
  15.     leal    str,    %edi  
  16.     leal    char,   %esi  
  17.     movl    len,    %ecx  
  18.       
  19.     lodsb  
  20.     cld  
  21.     repne   scasb  
  22.     jne     notfound  
  23.       
  24.     subw    len,    %cx     # len是长度,cx值为找到的位置距离末尾的位置,  
  25.                             # 则%cx - len为正着数位置的负数  
  26.     neg     %cx             # neg 为求补指令,即负数变正数  
  27.     movl    %ecx,   %ebx    # 作为中断的返回值,可在程序退出后echo $?查看  
  28.     movl    $1,     %eax  
  29.     int     $0x80  
  30.   
  31. notfound:  
  32.     movl    $1,     %eax  
  33.     movl    $0,     %ebx  
  34.     int     $0x80  
结果:
liury@liury-laptop:~/program/asm/working_with_string/find_char$ make
as -gstabs -o find_char.o find_char.s
ld -o find_char find_char.o
liury@liury-laptop:~/program/asm/working_with_string/find_char$ ./find_char 
liury@liury-laptop:~/program/asm/working_with_string/find_char$ echo $?
9

2.搜索多个字符

SCASW,SCASL可以搜索2或4个字符的序列,但他们不会逐字符比较,而是每次递增2或4.
如在“abctestaaabb”中搜索用SCASL搜索“test”会用“test”与第一个串中的"abct", "esta", "aabb"依次比较,而不会逐字符增减寻找"test" 所以结果是找不到。

3.计算字符串的长度

SCAS指令的一个非常有用的功能是确定0结尾的字符串的长度。
示例:
  1. # Finding the len of a string useing the SCAS instruction  
  2. .section .data  
  3. output:  
  4.     .asciz  "The len of the str is : %d.\n"  
  5. str:  
  6.     .asciz  "I am learning AT&T assembly language."  
  7. len:  
  8.     .int    . - str - 1         # 减一表示去掉最后的'0'  
  9.   
  10. .section .text  
  11. .global _start  
  12.   
  13. _start:  
  14.     nop  
  15.   
  16.     pushl   len  
  17.     pushl   $output  
  18.     call    printf  
  19.     addl    $8,     %esp  
  20.       
  21.     leal    str,    %edi  
  22.     movl    $0xffff,%ecx    # 支持的最大长度0xfffff = 65535  
  23.     movb    $0,     %al  
  24.   
  25.     cld  
  26.     repne   scasb           # 每次迭代ECX递减  
  27.     jne     notfound  
  28.   
  29.     subw    $0xffff,%cx     # %cx-0xffff为进行了多少次迭代的负数  
  30.     neg     %cx             # 求补,即负数变整数  
  31.     dec     %cx             # 减去‘0’,即字符串长度不包含最后的‘0’  
  32.     movl    $1,     %eax  
  33.     movl    %ecx,   %ebx  
  34.     int     $0x80  
  35.   
  36. notfound:  
  37.     movl    $1,     %eax  
  38.     movl    $0,     %ebx  
  39.     int     $0x80  
运行:
liury@liury-laptop:~/program/asm/working_with_string/str_len$ make
as -o str_len.o str_len.s
ld -dynamic-linker /lib/ld-linux.so.2 -lc -o str_len str_len.o
liury@liury-laptop:~/program/asm/working_with_string/str_len$ ./str_len 
The len of the str is : 37.
liury@liury-laptop:~/program/asm/working_with_string/str_len$ echo $?
37

用两种方法求得字符串的长度,两者相等。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值