Linux彙編教程13:系統調用和文件處理上

緩衝區和.bss

 

緩衝區是連續的字節塊,用於批量數據傳輸。.bss段類似於數據段,不同的是它不佔用可執行程序空間。.bss段可以保存存儲位置,卻不能對其進行初始化。

爲了實現這一點,我們需要如下指令:

.section .bss

.lcomm buffer, 500

.lcomm指令將開闢500字節的空間,使用buffer這個符號指向這個區域。

接下來,假設我們打開了一個文件進行讀取,將文件描述符放在了%ebx裏

movl $buffer, %eax

movl 500, %edx

movl 3, %eax

上面的指令將最多500字節的數據讀入緩衝區。read是系統調用3.

 

系統調用

 

關於系統調用%eax保存系統調用號,返回值和錯誤代碼也存儲在%eax中

 

Linux內核給彙編提供系統調用的接口是數字,eax寄存器用來保存這個系統調用的數字,這些數字在linux 內核源代碼中所對應的平臺下的unist_32.h中可以查到。Linux通過系統調用軟件中斷的方式來完成一個系統調用。int是AT&T彙編中的中斷指令,Linux 的系統調中斷號爲0x80。故而int $0x80指令引發一個Linux系統調用軟件中斷。系統調用軟件中斷髮生時,根據eax中所定的系統調用去調用系統調用所對應的的內核函數,然後根據相關寄存器內所保存的數據實現一次系統調用。

完成一個系統調用的涉及的方面:

[1] 決定系統調用類型

在明確目標系統調用對應的數字後,給eax寄存器賦所需系統調用對應的數字。eax寄存器用來保存系統調用接口。

movl  $n, %eax    #n爲對應的系統調用提供的接口

[2]爲系統調用準備相關的數據

在C中,程序所需要的數據保存在棧、堆等內存中。而對於彙編來說,這些數據需要保存在寄存器中,在將數據存到各寄存器的過程中要保證正確的順序,否則有可能爲程序帶來災難性的後果。

在一次系統調用中,只使用ebx、ecx、edx、esi、edi五個寄存器來保存相對程序來說的輸入數據,這些輸入數據其實是傳遞給在系統調用過程中所訪問的內核函數的實參。輸入數據被保存的順序如下:

  • ebx—第一個參數
  • ecx—第二個參數
  • edx—第三個參數
  • esi—第四個參數
  • edi—第五個參數

這裏第*個參數的含義是指系統調用函數的參數次序,可以通過“man 2函數名”的方式查詢到。在之前的“彙編程序打印字符”彙編程序中,往ebx,ecx,edx寄存器中保存的參數依次對應“寫系統調用”函數write(int  fd, const  void  *buf,size_t  count)的三個參數。所以,ebx中保存的是文件描述符(STDOUT:1),ecx中保存的是要向控制檯輸出的字符串地址,edx保存的是字符串的長度。So,使用系統調用,man 2相應的內核函數也是必須的。

對於超過六個參數的情況是使用ebx保存相繼而存的數據所在內存的起始地址。系統調用通過ebx內的地址訪問這些數據。

[3]系統調用的返回值

執行一次系統調用的本質是調用了對應的內核函數,一部分內核函數是有返回值的。一次系統調用完成後(內核函數執行完畢)的返回值保存在eax寄存器中。這裏突然想起以前的筆記,關於棧地址的返回:return 棧地址。

[4] 總結使用系統調用

  • 搭一個彙編程序框架。
  • 查詢應賦給eax的系統調用接口。[最好的地方是內核源代碼unist_32.h中 +搜索引擎]
  • man 2 系統調用函數的參數,按照順序將函數參數賦給ebx,ecx,edx,esi,edi。
  • 使用int $0x80系統調用軟件中斷。
  • …….
  • 退出彙編程序的系統調用。

 

查看系統調用號,可以區尋找unist_32.h查看,不用GNU/Linux不同,我的是Ubuntu發行版。在/usr/include/x86_64-linux-gnu/asm/目錄下可以找到

unist_32.h

查看函數異read爲例子,在終端輸入

man 2 read

出現下面界面,函數的參數對應前面的寄存器位置。

man-2-read

標準文件和特殊文件

  • STDIN
    標準輸入,文件描述符0
  • STDOUT
    標準輸出,文件描述符1
  • STDERR
    標準錯誤,文件描述符2

 

在程序中使用文件

 

需要實現的功能:從一個文件中讀取數據,把小寫字母轉換爲大寫字母,然後寫入到另一個文件中。

由於這小段代碼涉及到一些系統調用號、中斷號等等。多了以後,容易搞錯,所以我們需要利用一個.equ指令把這些數字分配一個人類可以理解的符號。具體用法看完代碼就知道了。

 .section .data

 .equ SYS_READ, 3
 .equ SYS_WRITE, 4
 .equ SYS_OPEN, 5
 .equ SYS_CLOSE, 6
 .equ SYS_EXIT, 1

 .equ O_RDONLY, 0
 .equ O_CREAT_WRONLY_TRUNC, 03101

 .equ STDIN, 0
 .equ STDOUT, 1
 .equ STDERR, 2

 .equ LINUX_SYSCALL, 0x80
 .equ END_OF_FILE, 0
 .equ NUMBER_ARGUMENTS, 2

 .section .bss

 .equ BUFFER_SIZE, 500
 .lcomm BUFFER, BUFFER_SIZE

.section .text
#棧位置
 .equ ST_SIZE_RESERVE, 8
 .equ ST_FD_IN, -4
 .equ ST_FD_OUT, -8
 .equ ST_AGRC, 0
 .equ ST_ARGV_0, 4
 .equ ST_ARGV_1, 8
 .equ ST_ARGV_2, 12

 .globl _start
_start:
 # 保留棧指針
movl %esp, %ebp

#開闢空間
 subl $ST_SIZE_RESERVE, %esp

open_files:
open_fd_in:
 movl $SYS_OPEN, %eax
 #將輸入文件名放到%ebx
 movl ST_AGRV_1(%ebp), %ebx
# 只讀
 movl O_RDONLY, %ecx
#權限
 movl $0666, %edx
#調用Linux
 int $LINUX_SYSCALL

store_fd_in:
 #保存給定的文件描述符
 movl %eax, ST_FD_IN(%ebp)

open_fd_out:
 # 打開文件
 movl $SYS_OPEN, %eax
 # 將輸入文件名放入%ebx
 movl ST_ARGV_2(%ebp), %ebx
 # 寫入文件標誌
 movl $O_CREAT_WRONLY_TRUNC, %ecx
 # 新文件模式
 movl $0666, %edx
 # 調用linux
 int $LINUX_SYSCALL

stroe_fd_out:
 # 存儲文件描述符
 movl %eax, ST_FD_OUT(%ebp)

 ### 主循環 ###
read_loop_begin:

 movl $SYS_READ, %eax
 movl ST_FD_IN(%ebp), %ebx
 movl $BUFFER, %ecx
 movl $BUFFER_SIZE, edx
 int $LINUX_SYSCALL

 cmpl $END_OF_FILE, %eax
 jle end_loop

continue_read_loop:
 pushl $BUFFER
 pushl $BUFFER_SIZE
 call convert_to_upper
 popl %eax
 addl $4, %esp

 movl %eax, %edx
 movl $SYS_WRITE, %eax
 movl ST_FD_OUT(%ebp), %ebx
 movl $BUFFER, %ecx
 int $LINUX_SYSCALL
 jmp read_loop_begin

end_loop:
 movl $SYS_CLOSE, %eax
 movl ST_FD_OUT(%ebp), %ebx
 int $LINUX_SYSCALL

 movl $SYS_CLOSE, %eax
 movl ST_FD_IN(%ebp), %ebx
 int $LINUX_SYSCALL

 movl $SYS_EXIT, %eax
 movl $0, %ebx
 int $LINUX_SYSCALL

現在這一部分實現了主程序的基本框架,convert_to_upper函數部分在下一節給出。

版權聲明

Moriarty_221爲本文的CSDN博客

如未註明,均爲原創,轉載請註明出處

轉載請註明:coskimo » Linux彙編教程13:系統調用和文件處理上

版權所有 © 科斯基摩 | 本網站採用cc by-nc-sa 3.0協議進行授權

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章