How to print strings and integers in Intel Assembly on Linux ?

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 0x08 interrupt code.

Declare strings

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"

Print strings

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.

Print integers

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.

Comments

Comments powered by Disqus