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