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.