Testing Assembly on 64 bit Architecture


Author: mywisdom


I prefer to use yasm on 64 bit machines.


Installing yasm

http://www.tortall.net/projects/yasm/releases/yasm-1.1.0.tar.gz

tar zxvf yasm-1.1.0.tar.gz

cd yasm-1.1.0

/configure && make && make install


using yasm pretty much the same as nasm:


#yasm -f elf -o object.o source.asm


* Registers


some of 64 bit registers are:

- rax (eax in 32 bit)

- rbx (ebx)

- rcx (ecx)

- rdx (edx)

- rsp (esp of 64 bit)

- rbp (ebp)

- rsi (esi)


* vsyscall

on 64 bit architecture here i see the vsyscall:

sh-3.2# cat /usr/include/asm-x86_64/vsyscall.h

#ifndef _ASM_X86_64_VSYSCALL_H_

#define _ASM_X86_64_VSYSCALL_H_


enum vsyscall_num {

   __NR_vgettimeofday,

   __NR_vtime,

   __NR_vgetcpu,

};


#define VSYSCALL_START (-10UL << 20)

#define VSYSCALL_SIZE 1024

#define VSYSCALL_END (-2UL << 20)

#define VSYSCALL_ADDR(vsyscall_nr)
(VSYSCALL_START+VSYSCALL_SIZE*(vsyscall_nr))



#endif /* _ASM_X86_64_VSYSCALL_H_ */

---------------------------------------



looking for vyscall_enum we already figure out there are 3 vsyscall:
vgettimeofday, vtime,vgetcpu

enum vsyscall_num {

   __NR_vgettimeofday,

   __NR_vtime,

   __NR_vgetcpu,

};


pretty much the same as 32 bit:

-----------------------------

cat /usr/include/asm/vsyscall.h

#ifndef _ASM_X86_VSYSCALL_H

#define _ASM_X86_VSYSCALL_H


enum vsyscall_num {

   __NR_vgettimeofday,

   __NR_vtime,

   __NR_vgetcpu,

};


#define VSYSCALL_START (-10UL << 20)

#define VSYSCALL_SIZE 1024

#define VSYSCALL_END (-2UL << 20)

#define VSYSCALL_MAPPED_PAGES 1

#define VSYSCALL_ADDR(vsyscall_nr)
(VSYSCALL_START+VSYSCALL_SIZE*(vsyscall_nr))



#endif /* _ASM_X86_VSYSCALL_H */

-----------------------


* start


Here a simple asm 32 bit hello world:

section .text

global _start

_start:

       mov     edx,len

       mov     ecx,msg

       mov     ebx,1

       mov     eax,4

       int     0x80


   mov    ebx,0

       mov     eax,1

       int     0x80

section .data

msg     db      "Hello, world!",0xa

len     equ     $ - msg



it's normally can be executed on any 32 bit machine.


on 64 bit machines resulted a warning:

sh-3.2# yasm -f elf hello.asm

sh-3.2# ld -o hello hello.o

ld: warning: i386 architecture of input file `hello.o' is incompatible
with i386:x86-64 output

sh-3.2# ./hello

Hello, world!

sh-3.2#


actually i think a simple way about changing the name of register ex: eax
becomes rax, so the wrong code that i made:

sh-3.2# cat hello.asm

section .text

global _start

_start:

       mov     rdx,len

       mov     rcx,msg

       mov     rbx,1

       mov     rax,4

       int     0x80


   mov    rbx,0

       mov     rax,1

       int     0x80

section .data

msg     db      "Hello, world!",0xa

len     equ     $ - msg


but when i compile it generates fatal error:

sh-3.2# yasm -f elf hello.asm

hello.asm:4: warning: `rdx' is a register in 64-bit mode

hello.asm:4: error: undefined symbol `rdx' (first use)

hello.asm:4: error:  (Each undefined symbol is reported only once.)

hello.asm:5: warning: `rcx' is a register in 64-bit mode

hello.asm:5: error: undefined symbol `rcx' (first use)

hello.asm:6: warning: `rbx' is a register in 64-bit mode

hello.asm:6: error: undefined symbol `rbx' (first use)

hello.asm:7: warning: `rax' is a register in 64-bit mode

hello.asm:7: error: undefined symbol `rax' (first use)

hello.asm:10: warning: `rbx' is a register in 64-bit mode

hello.asm:11: warning: `rax' is a register in 64-bit mode


To repair that error we need to add a little directive: bits 64 or we can
use : USE64, so here our supposely right (actually wrong code for 64 bit
machine) :


;this is  32 bit assembly style on 64 register (actually wrong)

bits 64

section .text

global _start

_start:

       mov     rdx,len

       mov     rcx,msg

       mov     rbx,1

       mov     rax,4

       int     0x80


   mov    rbx,0

       mov     rax,1

       int     0x80

section .data

msg     db      "Hello, world!",0xa

len     equ     $ - msg


-----------------------

sh-3.2# yasm -f elf hello.asm

sh-3.2# ld -o hello hello.p

ld: hello.p: No such file: No such file or directory

sh-3.2# ld -o hello hello.o

ld: warning: i386 architecture of input file `hello.o' is incompatible
with i386:x86-64 output

sh-3.2# ./hello

Hello, world!

sh-3.2#


so it can run with that above supposed wrong code (actually above is for
32 bit machines). yep it still 32 bit asm code dude not 64, so now we're
gonna

convert it into 64 bit assembly codes


what's the differences?


int     0x80 is for 32 bit machine the equivalent code at 64 bit machine
is : syscall


User-level applications use as integer registers for passing the sequence:
%rdi, %rsi, %rdx, %rcx, %r8 and %r9.

The kernel interface uses %rdi,%rsi, %rdx, %r10, %r8 and %r9.


ok here the style of 64 bit asm:


64 bit style

--------------

bits 64

section .data

   msg db  "Hello World!",10,0

   len     equ     $ - msg

section .text

   global _start


   _start:



       mov     rdx, len

       sub     rdx, rcx



       mov     rsi, dword msg

       push    0x1

       pop     rax

       mov     rdi,rax

   syscall


       xor     rdi,rdi

       push    0x3c

       pop     rax

       syscall



here 32 bit asm style to invoke sys_write

       mov     edx,len

       mov     ecx,msg

       mov     ebx,1

       mov     eax,4

       int     0x80


-  mov     edx,len -> string length at edx register

-  mov     ecx,msg  -> memory address of msg will be at ecx register

-  mov    ebx,0 -> ebx = 0

-  mov     eax,1  -> we suppy syscall number of sys_write here : 1

-  int     0x80    -> finally we call the kernel



but the operation is pretty much different in 64 bit machines:


       mov     rdx, len ; we suppy string length at rdx register

       sub     rdx, rcx ; substract rdx with rcx



       mov     rsi, dword msg ; we supply pointer to msg at rsi

       push    0x1 ; insert 0x1 into stack

       pop     rax ; and then popping it into rax

       mov     rdi,rax

   syscall