【Linux x86汇编踩坑】文件读写(一)读取文件并输出

【Linux x86汇编踩坑】文件读写

前言

linux的文件是unix的文件处理方式,因此,无论linux文件是什么程序创建的,都可以作为连续的字节流进行访问,明确这一点是相当重要的。

文件调用号

首先看看几个关于文件的系统调用号

3 //读
4 //写
5 //打开
6 //关闭

如果想知道更多linux系统调用号请移步Linux系统调用号。
对文件的操作都需要系统来完成,因此当我们需要打开或者读写一个文件时,需要向系统发出中断信号,由系统来完成这些操作,因为操作系统的安全性,这些对文件的操作都是放在管态中执行的,向系统发送中断信号都是通知cpu从目态进入管态。所以在文件读写中,向系统发送中断信号是必不可少的。

打开文件

当我们需要打开一个文件时,我们要告诉linux打开的文件名,并以特定的方式打开(read,write,create等),这是通过open系统调用处理的,通常来说,需要对文件操作都要经过打开这一步骤,但是有三个文件例外,这三个文件不是说不需要打开,而是系统在程序运行的过程中就已经帮我们完成了,它们是stdin,stdout,stderr分别对应标准输入文件,标准输出文件,标准错误文件,stdin通常指代键盘,用来接收键盘的输入,stdout通常指带屏幕,用来输出东西在屏幕上,stderr,当有错误发生的时,可以往里面写入错误信息。

在打开文件之前,我们需要将系统调用号放入%eax,文件的首地址放在%ebx中,以数字表示的读写意图放在%ecx中(通常用0来表示读,03101表示写),权限集合作为数字放在%edx中(默认为0666)。

把指定的信息放入相应的寄存器之后,就可以向系统发送中断信号来通知打开文件了。

打开文件过后,文件描述符通常放在%eax中,文件描述符可以代表一个文件,在整个程序中你都可以通过文件描述符对文件进行操作,在文件使用完毕后,你可以通知linux将文件关闭,这时,你的文件描述符不会有效。

以下是一个简单的打开文件示例,我们以打开当前目录下的data文件为例

openFile.s

.section .data
.section .text
.globl _start
_start: 
  mov %esp, %ebp
  #系统调用号
  movl $5, %eax
  #文件名
  movl 8(%ebp), %ebx
  #读写意图为只读
  movl $0, %ecx
  #系统权限
  movl $0666, %edx
  #系统中断
  int $0x80
  movl %eax, %ebx
  movl $1, %eax
  int $0x80

汇编连接运行

as openFile.s -o openFile.o
ld openFile.o -o openFile
./openFile data

读取文件

打开文件之后,我们可以对文件进行读的操作,为了进行该调用,你必须将文件描述符放入%ebx,将储存数据的缓冲地址放入%ecx,将缓冲区的大小放入%edx,读操作返回读取的字符数或者一个错误代码,错误代码通常是负数。

写入文件

写入文件和读取文件参数基本一致,唯一的区别是缓冲区里应该已经填满了要写的数据。

读取文件并输出示例

下面我上一个示例,在data文件中,我写上一些信息,然后写一个汇编程序,将该文件里的内容输出到屏幕上:

为了方便我会用.equ定义一系列的别名

test.s

.section .data
#系统调用号
.equ SYS_EXIT, 1
.equ SYS_READ, 3
.equ SYS_WRITE, 4
.equ SYS_OPEN, 5
.equ SYS_CLOSE, 6

#文件打开选项
.equ FILE_READONLY, 0
.equ FILE_WRITE, 03101

#系统调用中断
.equ SYS_INTERRUP, 0x80

#标准文件描述符
.equ STD_IN, 0
.equ STD_OUT, 1
.equ STD_ERR, 2

.section .bss
#申请缓冲区20byte
.equ BUFFER_SIZE, 20
.lcomm BUFFER_DATA, BUFFER_SIZE

.section .text
.globl _start
_start:
  #文件名入栈
  movl %esp, %ebp
  pushl 8(%ebp)
  # 打开文件
  call openFile
  addl $4, %esp
  # 文件描述符保存在%eax
  pushl %eax
  # 读取文件
  call readFile
  addl $4, %esp
  call writeFile
  movl %eax, %ebx
  movl $SYS_EXIT, %eax
  int $SYS_INTERRUP

#系统调用打开文件
.type openFile, @function
openFile:
  pushl %ebp
  movl %esp, %ebp
  #系统调用号
  movl $SYS_OPEN, %eax
  #文件名
  movl 8(%ebp), %ebx
  #读写意图
  movl $FILE_READONLY, %ecx
  #系统权限
  movl $0666, %edx
  #系统中断
  int $SYS_INTERRUP
  popl %ebp
  ret

#读取文件
.type readFile, @function
readFile:
  pushl %ebp
  movl %esp, %ebp
  #文件描述符
  movl 8(%ebp), %ebx
  #系统调用号
  movl $SYS_READ, %eax
  #缓冲区
  movl $BUFFER_DATA, %ecx
  #缓冲区大小
  movl $BUFFER_SIZE, %edx
  #系统调用
  int $SYS_INTERRUP
  popl %ebp
  ret

#写入文件
.type writeFile, @function
writeFile:
  pushl %ebp
  movl %esp, %ebp
  #文件描述符
  movl $1, %ebx
  #系统调用号
  movl $SYS_WRITE, %eax
  #缓冲区
  movl $BUFFER_DATA, %ecx
  #缓冲区大小
  movl $BUFFER_SIZE, %edx
  #系统调用
  int $SYS_INTERRUP
  popl %ebp
  ret

我们在data里写上

hello assembly

汇编连接

$as test.s -o test.o
$ld test.o -o test
$./test data
hello assembly

大功告成!这里为了划分功能模块,把他们都写成了函数。值得注意的是,在进行传入文件名操作的时候,文件名为当前esp的往上8个地址

你可能感兴趣的:(x86汇编)