AT&T汇编处理字符串

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

一.传送字符串

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


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标志
示例:
# An example of the MOVS instructions
.section .data
str:
	.ascii	"Hi AT&T.\n"

.section .bss
	.lcomm	output, 9

.section .text
.global _start

_start:
	nop

	leal	str,	%esi
	leal	output,	%edi

	movsb
	movsw
	movsl

	movl	$1,		%eax
	movl	$0,		%ebx
	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
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:
...
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 : 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000'
0x80490a8 : 0 '\000'
(gdb) x /s &output
0x80490a0 : ""
(gdb) n
19 movsw
(gdb) x /s &output
0x80490a0 : "H"
(gdb) n
20 movsl
(gdb) x /s &output
0x80490a0 : "Hi "
(gdb) n
22 movl $1, %eax
(gdb) x /s &output
0x80490a0 : "Hi AT&T"
(gdb) c
Continuing.

Program exited normally.
(gdb) q

对于要传送大型字符串,可用循环:
	leal	datas,	%esi
	leal	output,	%edi
	mvol	$100,	%ecx
	cld
loop_cp:
	movsb
	loop	loop_cp

2.REP

可更简单地传送大型字符串。它自己并不执行什么操作,这条指令用于按照特定次数重复执行字符串指令,由ECX寄存器中的值进行控制。例如
	leal	datas,	%esi
	leal	output,	%edi
	mvol	$100,	%ecx
	cld
	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配合:
leal	space,	%esi
leal	buffer,	%edi
movl	$256,	%ecx
cld
lodsb
rep		stosb

字符串处理示例,小写变大写:
# Converting lower to upper case

.section .data
str1:
	.asciz	"This is a TEST, of the program, hello AT&R!\n"
len:
	.int	. - str1

.section .text
.global	_start

_start:
	nop
	leal	str1,	%esi
	movl	%esi,	%edi
	movl	len,	%ecx
	cld
1:
	lodsb

	cmpb	$'a',	%al
	jl		skipb
	cmpb	$'z',	%al
	jg		skipb

	subb	$0x20,	%al

skipb:
	stosb
	loop	1b

end:
	pushl	$str1
	call	printf

	addl	$4,		%esp

	pushl	$0
	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一起使用

示例:
# An example of using the REPE CMPS instruction
.section .data
output:
	.asciz	"The len is %d.\n"
str1:
	.ascii	"This is a test of the cmps instructions."
str2:
	.ascii	"This is a test of the CMPS instructions."
len:
	.int	. - str2

.section .text
.global _start

_start:
	nop
	movl	len,	%ecx
	pushl	%ecx
	pushl	$output
	call	printf
	addl	$8,		%esp

	leal	str1,	%esi
	leal	str2,	%edi
	movl	len,	%ecx
	movl	$1,		%eax

	cld	
	repe	cmpsb
	je		equal
	movl	%ecx,	%ebx
	int		$0x80

equal:
	movl	$0,		%ebx
	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:扫描字符串,查找匹配搜索字符的位置
示例:
# An example of the SCAS instruction
.section .data
str:
	.ascii	"Hello AT&T!"
len:
	.int	. - str
char:
	.ascii	"&"

.section .text
.global _start

_start:
	nop
	leal	str,	%edi
	leal	char,	%esi
	movl	len,	%ecx
	
	lodsb
	cld
	repne	scasb
	jne		notfound
	
	subw	len,	%cx		# len是长度,cx值为找到的位置距离末尾的位置,
							# 则%cx - len为正着数位置的负数
	neg		%cx				# neg 为求补指令,即负数变正数
	movl	%ecx,	%ebx	# 作为中断的返回值,可在程序退出后echo $?查看
	movl	$1,		%eax
	int		$0x80

notfound:
	movl	$1,		%eax
	movl	$0,		%ebx
	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结尾的字符串的长度。
示例:
# Finding the len of a string useing the SCAS instruction
.section .data
output:
	.asciz	"The len of the str is : %d.\n"
str:
	.asciz	"I am learning AT&T assembly language."
len:
	.int	. - str - 1			# 减一表示去掉最后的'0'

.section .text
.global _start

_start:
	nop

	pushl	len
	pushl	$output
	call	printf
	addl	$8,		%esp
	
	leal	str,	%edi
	movl	$0xffff,%ecx	# 支持的最大长度0xfffff = 65535
	movb	$0,		%al

	cld
	repne	scasb			# 每次迭代ECX递减
	jne		notfound

	subw	$0xffff,%cx		# %cx-0xffff为进行了多少次迭代的负数
	neg		%cx				# 求补,即负数变整数
	dec		%cx				# 减去‘0’,即字符串长度不包含最后的‘0’
	movl	$1,		%eax
	movl	%ecx,	%ebx
	int		$0x80

notfound:
	movl	$1,		%eax
	movl	$0,		%ebx
	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

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


你可能感兴趣的:(BabyOS,AT&T汇编)