Monday, January 26, 2009

64 Bit ASM

Over the last few days I've been trying to rekindle my passion for the lost art of assembly programming. I am running FreeBSD 64 bit and although 32 bit asm applications will run on a 64 bit system, i haven't found a solid way to compile and run asm to 32 bit without it core dumping. So, i thought i would just rewrite my applications - had to be done at some stage.

There's a few things to note about 64 bit as the assembly has changed quite a bit. A good resource that outlines some of these differences is http://www.milw0rm.com/papers/110.

Let's take one of my very first ASM application (32 bit ) :


.data

string: .asciz "Looping!\n"
len = . - string - 1

.text
.global _start

_start:
movl $0,%ecx # set the inital counter value

compare:
cmpl $9,%ecx # compare to what is in %ecx
jle write # call if less than or equal
jmp exit # otherwise, exit

write:
pushl $len # pushl length of string
pushl $string # pushl the string
pushl $1 # pushl the file descriptor
movl $4,%eax # move the value '4' into %eax: write()
call kernel # call kernel, execute syscall
addl $12,%esp # clean stack
jmp increment # jump to the increment procedure

increment:
incl %ecx # increment the value in %ecx by 1
jmp compare # jump back to the compare procedure

exit:
pushl $0 # pushl our exit status
movl $1,%eax # move the value '1' into %eax: exit()
call kernel # call kernel, execute syscall

kernel: # call the kernel
int $0x80
ret

The equivalent C code for the above is:


int main () {
int i;
for ( i=0;i<9;i++)
{
puts ("Looping\n");
}
}


Here is the break down:

There are 6 main 'functions' - start, compare, write, increment, exit and kernel

start: this is equivalent to 'main' in c based languages
compare: compare the current counter value with '9' ( which will exit the 'loop' )
write: write the string 'Looping\n'
increment: increment the counter by 1
exit: exit the application
kernel: call the kernel - called in 'write' and 'exit'


The flow of the application is:

- set the counter
- test if the value of the counter is <= 9
- if the counter is <= 9 call the write function
- the write function will write 'Looping\n' then call increment
- increment will incremenent the counter by '1' and call the compare function
- once the counter is equal to 9 the exit function is called and the application exits

In this loop application, the following syscalls are used ( see /usr/src/sys/sys/syscall.h ) :

syscall 1 - exit : void exit(int status);
syscall 4 - write : write(int d, const void *buf, size_t nbytes);

Note: check the man pages for relevant args.

When this is compiled on a 64 bit machine, 'as' produces the following error:
$ as loop.s -o loop.o
loop.s: Assembler messages:
loop.s:18: Error: suffix or operands invalid for `push'
loop.s:19: Error: suffix or operands invalid for `push'
loop.s:20: Error: suffix or operands invalid for `push'
loop.s:31: Error: suffix or operands invalid for `push'

After a bit of research, i've rewritten the application. The following differences should be noted:
- Instead of calling the kernel with int 80, i've used 'syscall'
- pushq and movq have replaced pushl and mov
- rdx, rdi, rsi have been used to store arguments instead of pushing them on the stack
- rax has replaced eax

The 64 bit is as follows :
.data

string: .asciz "Looping!\n"
len = . - string - 1

.text
.global _start

_start:
movq $0,%r12 # set the inital counter value

compare:
cmp $9,%r12 # compare to counter value ( 9 iterations )
jle write # call if less than or equal
jmp exit # otherwise, exit

write:
xor %rdx,%rdx
movq $len,%rdx # length of string
movq $string,%rsi # the string
movq $1,%rdi # the file descriptor
movq $4,%rax # move the value '4' into %eax: write()
syscall
jmp increment # jump to the increment procedure

increment:
inc %r12 # increment the counter by 1
jmp compare # jump back to the compare procedure

exit:
movq $1,%rax # syscall 1 ( exit )
syscall




You can compile the above code with the following commands:

$ as loop.s -o loop.o
$ ld -s loop.o -o loop

To show that it is indeed a 64 bit application:

$ file loop
loop: ELF 64-bit LSB executable, x86-64, version 1 (FreeBSD), statically linked, stripped

No comments: