We are gong to write a simple program that reads from the terminal and writes the data it has read back out to the terminal. As we know from the previous post, reading and writing to the terminal is just a matter of reading and writing to two special files, stdin
and stdout
. These have file descriptors 0 and 1 respectively. We also need to set aside some space in memory to store the characters we read in from the terminal.
Let’s look at the code!
movq $1, %rax
movq $0, %rbx
int $0x80
.section .data
.section .bss
.lcomm buffer_data, 500
.section .text
.globl _start
_start:
movq $0, %rax
movq $0, %rdi
movq $buffer_data, %rsi
movq $500, %rdx
syscall
movq $1, %rax
movq $1, %rdi
syscall
movq $60, %rax
movq $0, %rdi
syscall
We have some new syntax here. The second line:
.section .bss
indicates to the assembler that this is the section in which we will define buffers. A buffer is a chunk of contiguous memory that we use to perform input and output operation. The next line:
.lcomm buffer_data, 500
declares a buffer named buffer_data that is 500 byes long. Now we read from the terminal with a usual file I/O kernel interrupt. We set rax
to 0 to indicate we are reading form this file and we set rdi
to 0 as that is the file descriptor of stdin
. We set rsi
to the address of our buffer with
movq $buffer_data, %rsi
and we set rdx
to 500 to tell the kernel we would like to read 500 bytes. Then we invoke the system call to transfer control to the kernel.
The next three lines set up the system call to write the contents of the buffer to the terminal. The registers rsi
and rdx
will not have been altered by the kernel, so they still contain the address of the buffer and the number of bytes, 500. We set rax
to 1 to indicate that we want to write and rdi
to 1 to tell the kernel that it is stdout
that we want to write to.
The final three lines are the usual exit with 0. Now if you write this code in a file named echo_input.s and run
as echo_input.s -o echo_input.o
ld echo_input.o -o echo_input
you will have a new binary file named echo input. If you execute this binary, it will wait for you to enter some input and hit the return key. When you do this, it will print what you wrote out again and exit with exit code 0.
Now, in our code, we specified that our buffer is 500 bytes long, and that we should read and write 500 bytes. The read and write operations will handle data smaller than 500 bytes perfectly well. However if you try to pass in a string larger than this, only the first 500 characters will be passed to our echo utility, the shell will try, and probably fail, to execute whatever comes after that.