In this post, we'll learn how to print strings and integers to the console on Linux using Intel Assembly. In this post, I'll use the AT&T notation, because it's the notation used in EDDI.
In EDDI, I have to print strings and numbers to the console, as this is not an easy exercise, I wanted to share my experience here.
On Linux, the only way to print something on the console is to use a system call. For that, we have to use the 0x80 interrupt code.
First, we'll see how to declare strings in an Intel Assembly file. You can use the .string instruction to achieve that :
StringToPrint: .string "Hello"
Then, to print, we will call the sys_write system call :
movl $4, %eax movl $1, %ebx movl $StringToPrint, %ecx movl $5, %edx int $0x80
The value in %eax (4) indicates the system call we need (sys_write). The 1 in %ebx indicates that we want to write in the console. Finally the two last parameters indicates the string to print and the size of the string. In Intel assembly, the int instruction launch an interrupt and the 0x80 in the interrupt table is set to the system call in the Linux Kernel.
As you can see, this code does use 4 registers and does not save any of them. Ideally, you will save the registers before and restore them. It depends on when you use this routine.
Writing an integer is a bit more complicated. If you have the integer in the string, there is no problem, but if you have only a long on your assembly, you'll have to convert the int into a string to print it. We will convert the integer char after char and use the stack as storage for our string. Then every char will be printed to the console using the same system as before.
So let's say we have our number in the %eax register :
movl $9234, %eax
So let's take a look at the code :
xorl %esi, %esi loop: movl $0, %edx movl $10, %ebx divl %ebx addl $48, %edx pushl %edx incl %esi cmpl $0, %eax jz next jmp loop next: cmpl $0, %esi jz exit decl %esi movl $4, %eax movl %esp, %ecx movl $1, %ebx movl $1, %edx int $0x80 addl $4, %esp jmp next exit:
The first part of the code consists in dividing the value by 10 until we reach zero. The remainder of the division is pushed onto the stack. For example, for our number, after this part, we'll have 4-3-2-9 on the stack. The order is reversed, which is logic because we stack the remainders from the right. During this phase, we count the number of elements using the %esi register.
Once this is done, we print each characters one by one starting with the last that has been pushed. Here we decrement the counter for each char and we use the sys_write call with %esp as the address of the string of one character. After each character, we incremetn the %esp to cancel the push that we used.
We have to do this in two phases in order to get the characters in the good order and not in reverse order.
Handle negative numbers
As you may have noticed, we do not manage negative numbers in our code. They will be printed, but it will be positive number. Indeed, in Intel Assembly (and in processors in general), negative numbers are handled with two's complement. Handling negative numbers in our code is not a big deal. We can add this code at the beginning :
cmpl $0, %eax jge loop neg %eax pushl %eax ; Print "-" popl %eax
First of all, we check if the number is smaller than 0, if it's not the case, we directly jump to the code we used before. If it's smaller, we negate the number and print a - before printing the real number. We have to save the %eax register before printing the - character because %eax is used for printing.
You'll now have a complete procedure to print an integer on the console in assembly.
I hope that this could be of some help for somebody.