I'm a newbie in writing assembly code and I need some help.
My task is to write a program in NASM (on Linux), which computes n-th Fibonacci number, where n is read from STDIN with read syscall and converted to int/long with C atoi/atol. The calculated number will be written to STDOUT (I can use C printf).
I managed to write the working 32-bit code and I'm stuck at converting it to 64-bit (using 64-bit registers, 64-bit long int). I tried to do it naively (changing eax -> rax, esp -> rsp and so on), but the only thing I get is segfault.
EDIT: Typo is fixed
EDIT2: Any ideas, how to use 64-bit Integers, to show higher than 46-th Fibonacci number?
Here's the code:
section .data
format: db '%d', 0xA
section .bss
buffer resb 8
bufferLength equ $-buffer;
section .text
extern printf
extern atoi
global main
main:
call fib
fib:
mov rax, 3
mov rbx, 0
mov rcx, buffer
mov rdx, bufferLength
int 0x80
push 0
push buffer
call atoi
add rsp, 8
push rbx
mov rcx, rax
xor rax, rax
xor rbx, rbx
inc rbx
call print
exitProg:
mov rbx, 0
mov rax, 1
int 0x80
print:
push rax
push rcx
push rax
push format
cmp rcx, 1
je lastPrint
add rsp, 8
pop rcx
pop rax
mov rdx, rax
mov rax, rbx
add rbx, rdx
dec ecx
jnz print
pop rbx
lastPrint:
call printf
add rsp, 8
call exitProg
Thanks in advance.
Your functions are not really functions as they don't return. You should consider rewriting them. If you are using the C library, it's better practice to return from main
instead of using the exit
system call. Also, it's advisable to use the C library I/O functions, if allowed.
In 64 bit mode, you typically access system calls using the syscall
instruction, although the int 0x80
interface is also available for compatibility. Note that system call numbers are different from 32 bit.
In addition, even the the calling convention is different (both user and system call), some arguments are passed in registers and the stack needs to be kept aligned. See the ABI documentation for details.
I have a hard time understanding the logic of the print
code especially the crazy stack manipulations. Also note that the pop rbx
line will never be reached because rcx
is checked for being 1
earlier, hence after decrementing it will never be zero.
You also have a typo, bufor
. Finally, your format string is in the text section. While that works, I am guessing you wanted it in .data
, you just have misplaced the directive (which is on the first line of your file).
I expect most of the above apply to the original 32 bit code too.
Update here is a possible implementation, now with 64 bit result (works up to n=93) and using XADD
(thanks to Frank Kotler):
section .data
format:
db "%lu", 10, 0
section .bss
buffer resb 8
bufferLength equ $-buffer
section .text
default rel ; use rip relative addressing (optional)
extern printf
extern atoi
global main
main:
sub rsp, 8 ; stack alignment
; note: this should be a call to libc read function
; but apparently assignment forces us to use syscall
xor eax, eax ; syscall number for "read" is 0
xor edi, edi ; fd 0, stdin
lea rsi, [buffer] ; buf
mov edx, bufferLength ; length
syscall
lea rdi, [buffer]
call atoi
mov edi, eax ; pass returned value from atoi
call fib
lea rdi, [format]
mov rsi, rax ; the returned value from fib
xor eax, eax ; no xmm registers used
call printf
xor eax, eax ; return zero
add rsp, 8
ret
fib:
mov eax, edi
sub edi, 1
jle fib_done ; f(0)=0, f(1)=1
xor ecx, ecx ; f(n-1)
mov eax, 1 ; f(n)
fib_loop:
xadd rax, rcx
sub edi, 1
jnz fib_loop
fib_done:
ret