首先介绍一下linux的shell脚本写法,其实与windows下.bat文件的写法差不多,一行一行的写命令就行了,例如,当我们要用vim打开某个目录(/user/include/printf.h)下的文件,可以这样写:
cd /usr/include
vim printf.h
保存为run.sh
第一次运行shell脚本时,要先给这个脚本权限,假设脚本名称为“run.sh”,命令为“chmod 777 ./run.sh”,之后就能一直使用了。
进入正文,linux下AT&T汇编——文件操作。
事实上汇编文件操作甚至要比某些高级语言更简单,因为不用去记那么一堆打开参数,在汇编中,由于基本是对于底层的操作,所以不会区分以文本方式打开还是以二进制方式打开,诸如此类。
基本步骤:打开文件(获取文件的标识符)-->操作文件(读取文件或者写入文件)-->关闭文件
以32位linux为例(64位下系统调用号是不同的):
打开文件的调用号为5,将5存入eax,ebx存入文件路径字符串的首地址,ecx存入打开方式,只读为“0”,写为“03101”,edx存入权限集合,现在存入“0666”就行了,反正我不懂unix的权限。打开成功后,系统会返回该文件的“文件标识符”,在eax里面。之后全程都要用这个文件标识符指代打开的那个文件。
读取文件的调用号为3,存入eax,文件标识符存入ebx,在此之前,要划一个缓存区,用来储存读取的数据,将该缓存区的首地址存入ecx,长度存入edx。读取成功后,会按照edx中的长度填充缓存区,就是edx的值是多少,就填充多少(当然,由文件的长度而定),返回已经读取的长度。
写入文件的调用号为4,存入eax,其余参数同上。返回写入的字节数或者错误代码(写入失败),存入eax。
关闭文件的调用号为6,存入eax,文件描述符存入ebx。只有这两个参数。
下面有一个例子,可以读取一个文件制定数量的字符串,并将里面的小写字符转换为大写字符,存入另一个文件的。代码在最后。设读取的文件名为“data”,写入的文件为“data1”,data的文件内容如下:
hello,world
abcdefg
hijklmn
opqrst
uvwxyz
编写的shell脚本内容如下:
echo "编译代码:"
make
echo "执行程序:"
./f
echo "返回值:" $?
makefile文件内容如下(makefile文件是用来编译代码的):
f:f_open.o
ld -m elf_i386 f_open.o -o f
f_open.o:f_open.s
as --32 f_open.s -o f_open.o
注:因为我的系统是centos7_64位,as和ld程序是默认以64位进行处理的,所以“--32”可以告诉as程序以32位进行编译,“-m elf_i386”告诉ld程序链接32位的库。
结果如下:
具体的汇编代码如下,120多行:
.section .data
fin_src:
.string "data"
fout_src:
.string "data1"
//定义常量
.equ SYQUIT,1
.equ FILE_OPEN,5
.equ FILE_CLOSE,6
.equ FILE_READ,3
.equ FILE_WRITE,4
.equ ONLY_READ,0
.equ ONLY_WRITE,03101
#储存小写字符值的范围,大于‘a’,小于‘z’。
.equ LETTER_A,'a'
.equ LETTER_Z,'z'
.section .bss
.equ bufSIZE,50
.lcomm buf,bufSIZE #指定一个50byte的缓存区,用来储存从文件获取的字符串
.section .text
.globl _start
_start:
//打开要读取的文件
pushl $fin_src #文件名地址
pushl $ONLY_READ #只读
call openFILE #打开文件
//读取文件
pushl %eax #文件标识符
pushl $buf #缓存区首地址
pushl $bufSIZE #缓存区长度
call readFILE
//字符转换
call upper_letter #之所以在这里调用函数,是因为堆栈中仍旧保存着readFILE函数的参数,在这里可以用到。
//关闭文件
subl $8,%esp
call closeFILE
//打开写入文件
pushl $fout_src
pushl $ONLY_WRITE
call openFILE
//写入文件
pushl %eax
pushl $buf
pushl $bufSIZE
call writeFILE
//向屏幕打印缓存区的内容
movl $1,8(%esp) #修改writeFILE函数第一个参数的值,使其向屏幕打印
call writeFILE
//关闭文件
subl $8,%esp
call closeFILE
//结束程序
sys_exit:
movl $SYQUIT,%eax
int $0x80
#openFILE堆栈参数:打开文件名,模式(返回文件描述符)
openFILE:
pushl %ebp
movl %esp,%ebp
movl $FILE_OPEN,%eax
movl $0666,%edx
movl 8(%ebp),%ecx
movl 12(%ebp),%ebx
int $0x80
movl %ebp,%esp
popl %ebp
ret
#readFILE参数:文件描述符,缓存区首地址,缓冲区长度(返回读取的长度)
readFILE:
pushl %ebp
movl %esp,%ebp
movl $FILE_READ,%eax
movl 8(%ebp),%edx
movl 12(%ebp),%ecx
movl 16(%ebp),%ebx
int $0x80
movl %ebp,%esp
popl %ebp
ret
#writeFILE参数:文件描述符,缓存区首地址,缓冲区长度(返回写入的长度)
writeFILE:
pushl %ebp
movl %esp,%ebp
movl $FILE_WRITE,%eax
movl 8(%ebp),%edx
movl 12(%ebp),%ecx
movl 16(%ebp),%ebx
int $0x80
movl %ebp,%esp
popl %ebp
ret
#closeFile参数:文件描述符(返回是否成功)
closeFILE:
pushl %ebp
movl %esp,%ebp
movl $FILE_CLOSE,%eax
movl 8(%ebp),%ebx
int $0x80
movl %ebp,%esp
popl %ebp
ret
#将小写字符转换为大写字符,参数:缓存区首地址,缓存区长度
upper_letter:
pushl %ebp
movl %esp,%ebp
movl 12(%ebp),%ecx #ecx储存首地址
movl 8(%ebp),%edx #edx储存长度
movl $0,%edi
movb (%ecx,%edi,1),%al
letter_loop:
cmp %edx,%edi
jge upexit
movb (%ecx,%edi,1),%al
cmp $LETTER_A,%al #如果al的值小于'a',说明不是小写字母
jl _up
cmp $LETTER_Z,%al #如果al的值大于'z',说明不是小写字母
jg _up
//进行到这里,判断出属于小写字母
sub $0x20,%al
movb %al,(%ecx,%edi,1)
_up:
incl %edi
jmp letter_loop
upexit:
movl %ebp,%esp
popl %ebp
ret