首先介紹一下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