Assembly Tutorial – Reading From a File

In previous posts we saw how to read from stdin and write to stdout. Now, we know that stdin and stdout are just special files, so we should be able to read from and write to normal files without much difficulty.

To show this, we’re going to write a simple program that writes the contents of a text file to the terminal (a bit like the cat utility). The following code, opens a file called “inputfile.txt” and then reads the first 500 bytes and writes them to stdout.


.section .data
name:
.string "inputfile.txt"
 
.section .bss
.lcomm buffer_data, 500

.section .text

.globl _start
_start:
 
movq $2, %rax
movq $name, %rdi
movq $0, %rsi
movq $0666, %rdx
syscall

movq %rax, %rdi
movq $0, %rax
movq $buffer_data, %rsi
movq $500, %rdx
syscall

movq $1, %rdi
movq $buffer_data, %rsi
movq $500, %rdx
movq $1, %rax
syscall

movq $60, %rax
movq $0, %rdi
syscall

Just like with our echo utility, we use a buffer defined in the .bss section to temporarily store the data we read. We also define the name of the file we will read in the data section.

The first thing we have to do is open the file we are interested in. We do this with the following code:

movq $2, %rax
movq $name, %rdi
movq $0, %rsi
movq $0666, %rdx
syscall

First we set rax to 2, this is the system call number for opening files. We set rdi to the label of the memory location containing the name of the file. When opening files we need to set the rsi register to indicate whether we are opening to read or write. In this case we are reading, so we set rsi to 0. Finally we set rdx with the permissions we would like this file to have. This is just the normal linux file permissions, in this case 0666 indicates that all users have read and write permission. Finally we invoke a system call and the linux kernel, should open this file. If the kernel manages to open the file successfully, the file descriptor will be in rax once control returns to our code.

Once the file is opened, we have to read it into our buffer. We do this with the following piece of code:

movq %rax, %rdi
movq $0, %rax
movq $buffer_data, %rsi
movq $500, %rdx
syscall

We are going to read from this file just like we read from stdin. The difference is that instead of putting the file descriptor for stdin (0) in rdi, now we put the file descriptor for the file we opened in there. Now, assuming everything went according to plan, the file descriptor we want will be in rax, so the first thing we do is move the value from rax to rdi. The rest of the code is the same, we will read the first 500 bytes of the file with the file descriptor in rdi into our buffer.

Our final two pieces of code just output the contents of the buffer to stdout and exit with a success code.

As usual, if this code is in a file named display.s, we assemble and link it with:

as display.s -o display.o
ld display.o -o display 

This creates a binary file that will display the contents of a file named “inputfile.txt”. If the file does not exist, the open system call will return with the error code -2 in rax. Right now we don’t check for this, but once we know a little more about control flow we will.

Leave a Reply

Your email address will not be published. Required fields are marked *